feat: per-host SKILL.md generated from shared template (#55)#57
Conversation
Each AI CLI host (Claude Code / Codex / Gemini / OpenCode) now reads a
SKILL.md that only mentions its own tool names — no cross-host
enumeration. Removes the qwen3.6-35b mis-dispatch class of failure
where the model picked the first listed value (Claude's
`general-purpose`) on a different host and got rejected.
- SKILL.md.tmpl is the source of truth; tools/gen_skill_md.py emits
SKILL-<host>.md per host plus a default SKILL.md (= claude variant).
- Template engine is minimal: {{var}} + {{#if host}}/{{#unless host}}.
No Jinja2 dep, ~120 lines of stdlib Python.
- install.sh detects the install path and swaps in the right variant;
clones to non-canonical paths default to claude.
- tests/test_skill_md_template_generation.py (16 tests) pin drift,
per-host shape, and install.sh wiring.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Closing per maintainer call. The right move at v0.5 is simplification (tighten SKILL.md while keeping every prescription) rather than templating-on-top-of-redundancy. The qwen failure modes we shipped fixes for in v0.4.4 were all 'prescriptiveness at ambiguity points,' not 'doc length' — and trimming the 250-line telemetry reference + 150-line debugging playbook + 9 expanded Phase 0 question shapes should help weak-model reliability (more of the load-bearing Phase 0–7 content stays inside OpenCode's tool_output cap). Templating can return as v0.6+ if simplification doesn't suffice. Branch |
Summary
samples-issue42/session-ses_235c.mdwhere the model picked Claude Code'sgeneral-purpose(listed first in the cross-host bullet) on its OpenCode dispatch and gotUnknown agent typerejected before self-correcting togeneral.SKILL.md.tmplis the source of truth;tools/gen_skill_md.pyemitsSKILL-<host>.mdper host (claude / codex / gemini / opencode) plus a defaultSKILL.md(= claude variant) so a clone-without-install user still gets a working file. Template engine is intentionally minimal —{{var}}substitution +{{#if host}}...{{/if}}/{{#unless host}}...{{/unless}}conditional blocks, no Jinja2 dependency, ~120 lines of stdlib Python.install.shdetects the install path (.claude/skills/...→ claude,.codex/skills/...→ codex, etc.) and copies the matching variant overSKILL.md. Clones to non-canonical paths default to claude.tests/test_skill_md_template_generation.py(16 tests) pin drift detection (--checkexits non-zero if regenerated content would differ), per-host shape (each variant references ONLY its own tools; OpenCode keeps itsOPENCODE_ENABLE_EXA+ parallel-flake notes; Codex keeps its prose-instruction dispatch; Gemini keeps@generalist), andinstall.shwiring (case-statement per host, swap referencesSKILL-$DETECTED_HOST.md, catch-all defaults to claude).tools/host_config.py+ one branch ininstall.sh).Test plan
python tools/gen_skill_md.py --checkexits 0 in a clean tree.pytest tests/test_skill_md_template_generation.py -v— 16/16 pass.pytest tests/ -qfull suite — 476/476 pass (was 460 + 16 new).task/general/OPENCODE_ENABLE_EXA/sst/opencode#14195; Claude variant hasTask/general-purposeand nogeneralstandalone; Codex variant explains the prose-instruction dispatch shape; Gemini variant uses@generalist.🤖 Generated with Claude Code