Skip to content

fix(skills): report the engines with no skills primitive instead of dropping them - #166

Merged
ralyodio merged 1 commit into
moshcoder:mainfrom
clawedassistant26:fix/skill-install-drops-unsupported-engines
Aug 1, 2026
Merged

fix(skills): report the engines with no skills primitive instead of dropping them#166
ralyodio merged 1 commit into
moshcoder:mainfrom
clawedassistant26:fix/skill-install-drops-unsupported-engines

Conversation

@clawedassistant26

Copy link
Copy Markdown
Contributor

The bug

prd/0003 R8 says engines without a skills primitive MUST be reported as skipped, and the PRD's own UX Notes print exactly that:

moshcode skill install https://github.com/acme/some-skill
#   ✓ claude    cloned into ~/.claude/skills/some-skill
#   ✓ gemini    installed (--scope user)
#   · codex/opencode/aider  skipped — no skills primitive

planSkillInstall maps SKILL_ENGINES, so codex, opencode, privacycode and aider never enter the plan, never reach runSkillInstall, and never reach summarize. The user is not told they were skipped — they are told nothing at all.

A consequence worth stating: the default: return { skip: "no skills primitive" } branch in skillInstallAction is unreachable through the command. It is only ever called with keys from SKILL_ENGINES.

Reproduced through the real CLI

No hand-editing, HOME pointed at a scratch dir so nothing touched a real skills directory:

$ moshcode skill install https://github.com/acme/some-skill
· installing skill some-skill → https://github.com/acme/some-skill across skills engines…
   claude    not installed — /install claude
   gemini    not installed — /install gemini

Two rows. Four engines silently absent.

The asymmetry that makes it a bug and not a choice

/skill list already gets this right, and its comment states the principle:

The engines with no skills primitive are whatever ENGINES has left over. Derived, not hardcoded: a hardcoded list silently drops any engine added later, so the matrix stops showing every engine moshcode supports.

Same command family, same run, same machine:

surface engines reported
/skill list (the matrix) 6 — claude, gemini, opencode, privacycode, codex, aider
/skill install (the fan-out) — before 2 — claude, gemini
/skill install (the fan-out) — after 6

privacycode is the concrete case the matrix comment warns about: an engine added after SKILL_ENGINES was written, present in the matrix, missing from the fan-out.

The fix

10 insertions, 2 deletions, one file. Plan over every engine, SKILL_ENGINES first then the remainder, derived from ENGINES the same way the matrix derives it.

runSkillInstall already checks skip before installed, so a skipped engine reports its reason whether or not the binary is present — matching runMcpAdd's existing precedent for R6.

After:

$ moshcode skill install https://github.com/acme/some-skill
· installing skill some-skill → https://github.com/acme/some-skill across skills engines…
   claude    not installed — /install claude
   gemini    not installed — /install gemini
   opencode  skipped — no skills primitive
   privacycode skipped — no skills primitive
   codex     skipped — no skills primitive
   aider     skipped — no skills primitive

Tests

New test/skill-install-fanout.test.mjs, 16 tests.

Seven are the bug: plan covers every ENGINES key; each non-primitive engine carries the skip reason; the fan-out reports them as skipped; skipped beats not-installed when nothing is installed; an installed non-primitive engine is skipped and never spawned (asserted by counting spawns); privacycode specifically; fan-out and matrix name the same set.

Nine are controls in the opposite direction, so the fix cannot buy green by over-reporting: SKILL_ENGINES still exactly ["claude","gemini"]; the primitive engines still come first in order; claude's git clone argv byte-identical; gemini's argv byte-identical; installed/not-installed still decides a real target; a signal still reports failed; a non-zero exit still reports failed and not a skip; no engine planned twice; every entry carries its real bin.

Fail-before / pass-after: 7 fail, 9 pass unpatched → 16/16 patched.
Suite: 622 → 638 tests, 0 failing (pass 499 → 515, skipped 123 unchanged).

A test that passes by accident

test/skills.test.mjs:49"skillInstallAction: engines without a skills primitive are skipped" — guards precisely the right function and passes unchanged, because it calls skillInstallAction("codex", …) directly, with keys planSkillInstall never passes it. The skip branch is proven correct and simultaneously unreachable in production. The new tests go through planSkillInstall/runSkillInstall, which is where the defect lives.

Deliberately out of scope

planMcpAdd maps MCP_ENGINES, which omits aider, so mcp install prints five rows where the PRD example shows a sixth (· aider skipped — no MCP support). Observed in the same session and not claimed or changed here: that is R6/R7 territory with its own per-engine reasons, and it deserves its own change rather than riding along on this one.

…ropping them

prd/0003 R8 requires `/skill install` to report engines without a skills
primitive as skipped, and the PRD's own UX example prints a
`skipped — no skills primitive` line for them. planSkillInstall maps
SKILL_ENGINES, so codex, opencode, privacycode and aider never enter the
plan, never reach runSkillInstall, and never reach the summary. The
`{ skip: "no skills primitive" }` branch in skillInstallAction is
unreachable through the command.

Plan over every engine, ordered SKILL_ENGINES first then the remainder,
derived from ENGINES exactly as the /skill list matrix already derives it.
runSkillInstall checks skip before installed, so a skipped engine reports
its reason whether or not it is installed.
@ralyodio
ralyodio merged commit d36a0ac into moshcoder:main Aug 1, 2026
3 checks passed
@ralyodio ralyodio mentioned this pull request Aug 1, 2026
ralyodio added a commit that referenced this pull request Aug 1, 2026
install.sh resolves releases/latest, so the sixteen commits merged since
v0.13.3 have been sitting on main unreachable — including a fix for a page
that locks browsers up.

The headline is the pit. /pit rendered every ending an account held and a form
per name under each, with no bound on either: at 50 endings x 100 names that
was 3.1 MiB of HTML and 36,082 DOM elements, and it managed to jam a browser
with no script on the page at all (#167). It now draws a window and says what
it is not drawing — 173 KiB, 1,926 elements — with a filter box over the top
that takes `eggs` as a substring and `def*` as a glob, debounced against the
API (#168).

The namespace also stopped being the one part of the product a script could
not touch: /api/moshpit/* now accepts the same API key /api/me and
/api/sessions already did (#169), and /pit/dns finally documents the
TronBrowser route for machines whose DNS is not theirs to change (#165).

  moshcode: foreign keys are enforced, and the licence package.json
  claims actually ships (#154)
  cli: help aliases exit 0 (#157), invalid integration commands fail (#160),
  `--` is honoured (#159), a BOM before a shebang no longer breaks (#158)
  skills: engines with no skills primitive are reported, not dropped (#166);
  `--name` requires a value (#156)
  mcp: an unsupported flag is rejected rather than registered as the server
  name (#164)
  pit: the namespace rules are vendored again with a drift test holding them
  to the published package (#161, #162, #163)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants