Repository navigation
Record each built-in tool's CLI command and return it with fetched skills; fetch one skill by name; version the skill list - #3553
Conversation
Pragmatic Code Review: PR #3553 (issue #3540)Review Type: Initial Review (no prior reviews or comments on the PR) Summary: The change is cohesive and follows the existing layering: core finds the tools a skill names, the daemon admits a built-in tool's command only from its seeded node, and the CLI prints it inside the skill's provenance banner. The node type sequence is followed for the one new field. I found no blocking defects. One hardening gap on the new field and a few smaller points are below. Verified by reading the diff and the surrounding code; Requirements Check
Code Review Findings🟡 Important1. The seed gate checks which node a command comes from, not what the command says
This stays inside the provenance banner and the documented trust boundary, so it is not a blocker. But the contract ("command and subcommand, with no arguments") is enforced only for seeds, by a test ( Recommendation: enforce the shape in 🟢 Suggestions2. A listing now reads every skill's subtree, and a failed read changes the version
Principle: a value clients compare for equality should fail loudly, not degrade silently. 3. The version covers less than what a fetch returns The digest covers id, name, description, tool list and body. It does not cover a skill's 4.
Nitpicks
CLAUDE.md adherence
RecommendationAPPROVE. Finding 1 is worth folding into this PR because it is a few lines in the validator the PR already touches; the rest are optional. |
malibio
left a comment
There was a problem hiding this comment.
Automated pragmatic-code-review: APPROVE (posted as a comment — GitHub blocks self-approval). Full findings in the review comment above.
Review addressedAddressed (4 findings, 1 nit)
Skipped (2 nits)
Re-Review DecisionDecision: NO RE-REVIEW NEEDED Rationale: The review approved the PR with no blocking finding. The changes are small and local: a pure validation function with its own unit test, one error-propagation change, error-message wording, and one mechanical substitution. None changes the design the review read. |
…ills; fetch one skill by name; version the skill list (closes #3540) tool-native declares an optional cli_command field, seeded for every built-in tool the CLI has an equivalent of. A skill fetch returns the command of each registry tool the skill lists in tool_whitelist or names in its body, for built-in, edited and user-written skills. nodespace skill get fetches one skill by exact name or id. The skill listing carries a version derived from stored state. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n an unread skill, tidy skill get errors - tool-native refuses a cli_command that is not nodespace plus one or two subcommand words (no flags, arguments or second line) - A skill list whose skill body cannot be read is an error, not a wrong version - skill get: the not-found hint is printed for NOT_FOUND only, and the message names the skill once - available_tools uses Tool::is_seeded_as Skipped: reading the tool-native bucket through behaviors' private helper (it would widen that module's surface for one call), and trimming the commands listed for an unedited built-in skill (the issue asks for every listed tool's command). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2d81651 to
97fdc72
Compare
Closes #3540. Implements ADR-093 §1 (get a skill, the list version) and §2 (a tool carries its command-line form).
What changes
A tool's CLI command lives on its registry node.
tool-nativedeclares an optional text field,cli_command: thenodespacecommand and subcommand that does what the tool does (create_relationship→nodespace relationship create). Every built-in tool with a CLI equivalent is seeded with one, fromTool::cli_command, an exhaustive match, so a tool added to the registry has to say which it is.A fetched skill comes with the commands of the tools it names. For each skill a fetch returns, the daemon returns the command of every registry tool the skill lists in
tool_whitelistor names in its body. This holds for built-in, user-edited and user-written skills. The body is not rewritten.update_nodeis not named byupdate_nodes_from_markdown, and the prose "update node" is not a tool name.nodespace skill get "<name or id>"fetches one skill. It is its own subcommand, backed by a newGetSkillRPC, and returns what a match returns for that skill: instructions, the schemas it is linked to throughapplies_to, tool commands, and the same provenance envelope. It needs no embedding model. A name no skill has fails with a message naming it; a name two skills share fails with their ids.The skill list has a version.
nodespace skill guidancewith no task carriesversion(top level in--json, in the first line of human output). It is a digest of every skill's id, name, description, tool list and body, computed on read: nothing is stored and no counter is kept.Node type change
tool-native, a core subtype of the abstract basetool; category flat.cli_command(text, optional, no default), stored in thetool-nativebucket. No relationship or structural rule changes.tool-nativehas neither. A tool travels as the generic node with its chain's fields insideproperties, and is written through the flat update, which the same validation pipeline checks.bun run gen:typesproduces no diff.cli_command, when set, must be text.Two things to check
route_clarifyhas no command. The issue asks for a test that fails when a tool a seeded skill names has no command. Node Creation and Graph Editing listroute_clarify, which hands a question back to the user and has no CLI equivalent. The test therefore allows a tool with no command only when it is listed inNO_CLI_EQUIVALENTwith its reason, and fails if a listed tool later gains a command.resolve_querymaps tonodespace query, the same command assearch_nodes. The CLI has no resolver of its own: its caller writes the query the reference resolves to.The version also covers a skill's tool list, which the issue does not name. A changed tool list changes the commands a fetch returns, so a client holding the old list should be told.
Unchanged
Tests
skill_guidance_tool_commands(agent, no model): every built-in skill by name and by id with its commands; a user-written skill; a user-edited built-in skill; word-boundary matching; a tool with no command; an archived tool; an unknown and a shared name; the version across add, rename, redescribe, body edit, archive, delete and an unrelated node.cli_integration:GetSkillandskill getend to end; the listing's version over gRPC.skill_pipeline: every tool a seeded skill names has a command; each seed records its tool's command.tool_subtypes_test: the field's bucket, flat update, clear, wire shape, wrong type refused.--jsonoutput.nodespace relationship createis parsed by the CLI and run, creating the link.bun run test:changedandbun run quality:fixpass.Docs
In nodespace-docs, pushed to
mainjust before the merge:components/node-types.md(thetool-nativesheet),components/tool-registry.md(the field and how commands are returned),architecture/prompt-skill-system.md(what a fetch returns, fetch by name, the version).packages/skill/SKILL.md,references/cli.mdandreferences/graph-authored-guidance.mdcoverskill get, the returned tool commands and the version.🤖 Generated with Claude Code