Repository navigation
feat: finish Phase 1 discovery (CLI envelope, get_kit, WebMCP mirror, contracts in CLI) - #97
Merged
Merged
Conversation
mrgoonie
force-pushed
the
sx-phase1-discovery
branch
2 times, most recently
from
October 20, 2026 02:35
b9ec312 to
669d61c
Compare
Bundle @skillx/contracts into the CLI (workspace devDependency, tsup noExternal) and drop the hand-written mirror of the actionability rule. The CLI now decides every status in COMPATIBILITY_STATUSES exactly as the contract does, which the parity notes no longer have to excuse. Closes #48 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add runCommand/emit/CliError so config, report, use and find join check, inspect and search on the same --json envelope and exit codes. Parse errors exit 2 with a usage_error envelope, 429 maps to rate_limited (7), and a programming TypeError is no longer reported as a network outage. Exit codes are set on process.exitCode so piped stdout drains. find now reads the search API's real shape (avg_rating, results) and opens the pick through use, instead of crashing on undefined fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
get_kit reads one kit's effective revision with the personal kit API's visibility: a public kit whose revision was authored public, reviewed and activated is readable by anyone, an owner (session or unscoped API key) reads their own kit, and everything else is not_found. With the feature flag off the tool fails with feature_disabled. The other four tools still never read the request's credentials. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The in-page tools post a tools/call to the same-origin /api/mcp route and return that tool's answer, so access codes and protected-payload rules stay on the server. Names, schemas and descriptions are held equal to the remote definitions by a test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… publishers A stranger now sees a public kit as metadata and revision digest only, the reference a foreign ancestor gets in the lock; the document stays with the owner. A suspended or revoked publisher answers publisher_revoked. The route checks the personal kits flag before reading credentials and reports an unexpected viewer failure as internal_error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… read --json only before -- Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…not_found everywhere config set-key and find raced nothing against stdin closing, so an empty pipe left an unsettled await and Node exited 13 without an envelope. Both now read through promptLine and fail with usage_error. use, search --use and find share one not_found path for an empty resolution. The incompatible error code is gone: check reports exit 4 on a success envelope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed envelope The registry commands had their own CliError and runner, and the installer its own error type, so the envelope and exit codes could drift apart. Both now use the shared CliError, runCommand and exit-code table; import moves off process.exit and gains --json, and a 429 from the registry is rate_limited. The docs keep one exit-code table (0-8), and a built-CLI test checks that every command offers --json and fails with an envelope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…em on 429 Installer commands now finish through the shared runCommand: the exit code is set on process.exitCode instead of process.exit, so piped stdout drains, human mode prints the hint, and conflict details still follow the message. A registry 429 is rate_limited (exit 7) for install, update and sync as it already was for publish and promote. Also: the API key hint names `skillx config set-key`, an import 404 hints at creating SKILL.md again, and validate and publish name themselves when the source cannot be packed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mrgoonie
force-pushed
the
sx-phase1-discovery
branch
from
October 20, 2026 02:38
669d61c to
2be37c3
Compare
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Finishes the remaining Phase 1 discovery items of #33 and closes #48.
search,use,find,inspect,check,report,config *,import), registry (validate,pack,publish,promote,release *, from feat(registry)!: release CLI, idempotent publish, promote, quarantine review, signed downloads and GitHub OIDC trusted publishing #99) and installer (install,update,uninstall,list,sync, from feat: collections API and verified CLI installer with lockfile, sync and local MCP (#35 phase 3) #98). All go through oneCliError+runCommandinlib/output.ts/lib/run-command.tsand one exit-code table: 0 ok, 1api_error/unexpected_error/integrity_error, 2 usage, 3 not found, 4 incompatible, 5 auth, 6 network, 7rate_limited, 8 installerconflict. Parse errors exit2(with ausage_errorenvelope under--json). Onlyskillx mcp(an MCP stdio server) has no--json.@skillx/contracts(workspace devDependency, inlined by tsup) andisActionabledelegates toisCompatibilityActionable; the mirrored rule is gone.get_kit: the owner reads their own kit (any visibility) with the full revision document. Anyone else sees only a public kit whose effective revision is reviewed, activated and authored public, and then only metadata plus{ digest, number, activatedAt }(the foreign-ancestor reference of the lock), elsenot_found. A suspended or revoked publisher answerspublisher_revoked, likeget_release. Flag off → tool errorfeature_disabledbefore any credential is read; an unexpected viewer failure isinternal_error. Onlyget_kitreads credentials; the other four tools stay anonymous.get_package/get_release: in-page tools that posttools/callto/api/mcpand return its answer unchanged; a test holds names, schemas and descriptions equal to the remote definitions.skillx findcrashed on selection (readtotal/ratingthat the API does not return); it now reuses theuseresolution. A plainTypeErroris no longer misreported as a network outage.#33 checklist → evidence
--compatible <runtime>search filterpackages/cli/src/lib/search-api.ts,commands/search.ts(now on the envelope)skillx inspectcommands/inspect.ts; nowrunCommand-basedskillx check --targetcommands/check.ts;built-cli.test.tsruns the built bundle against a stub server for all five statuses (exit 0/4) and a missing skill (exit 3)lib/output.ts,lib/run-command.ts;release-cli.tsandinstall-errors.tsnow reuse them; one table indocs/cli-reference.md#json-output-and-exit-codes; tests inoutput.test.ts,release-commands.test.ts, andbuilt-cli.test.ts, which walksskillx --helprecursively to assert--jsonon every leaf command (exceptmcp) and checks that failinginstall/list/validate/pack/promote/importanswer one envelope with exit 2components/compatibility-panel.tsxroutes/api.mcp.ts,lib/mcp/search_packages,get_package,get_release,check_compatibility,get_kitget_kitin this PR:lib/mcp/handlers-kits.ts(+handlers-kits.test.ts,api.mcp.test.tsauth/flag cases)lib/webmcp/model-context.ts);get_package/get_releasemirror in this PR:lib/webmcp/mcp-mirror-tools.ts(+ test answering through the real route)lib/markdown/skill-markdown*.tsLink: rel=alternate/ canonicallib/markdown/skill-markdown-handler.ts,routes/skill-detail.tsxcomponents/copy-markdown-button.tsxcomponents/copy-markdown-button.tsx/llms.txt,/llms-full.txtroutes/llms-txt.ts,routes/llms-full-txt.tsroutes/docs-harness.tsx,routes/docs-publishers.tsxlib/parity/compatibility-parity.test.ts; the "CLI cannot be reached" note is removed — CLI decisions for all five statuses are covered byskill-lookup.test.ts(against the contract) andbuilt-cli.test.ts(against the bundle)Verification
pnpm install --frozen-lockfile,pnpm typecheck: pass.pnpm test: 116 files, 2357 passed, 2 skipped (rebased onfbd0333).429answersrate_limitedwith exit 7 on bothinstallandpromote(local stub,--jsonand human mode; alsobuilt-cli.test.tsandregistry-client.test.ts).pnpm --filter skillx-sh buildandpnpm --filter web build: pass.@skillx/import (contracts inlined);node packages/cli/bin/skillx.js check <slug> --target <runtime> --jsonexercised bybuilt-cli.test.tsagainst a stub server;npm pack --dry-run: 6 files, 168.8 kB.Notes for reviewers
2instead of1, for every command includingpublish/release *. A bareskillxand a group without its subcommand (skillx config) print help and exit2.--help,help <command>and--versionexit0.--jsonis only recognised before--.usewith no match isnot_found(exit3) in both modes; human mode used to print a yellow notice and exit0, it now prints the message to stderr and exits3.search --useandfind(after a pick) share the same path.4:checkanswers it on a success envelope withdata.actionable: false; the installer fails with theincompatibleerror code. Both map to exit 4.2c5a892(feat: collections API and verified CLI installer with lockfile, sync and local MCP (#35 phase 3) #98, feat(registry)!: release CLI, idempotent publish, promote, quarantine review, signed downloads and GitHub OIDC trusted publishing #99; now onfbd0333), non-trivial resolutions:lib/output.ts: kept both additions:7 rate_limitedfrom this PR, and8 conflict/integrity_error/incompatiblefrom the installer.lib/release-cli.ts: its privateCliError(with a.failureenvelope) andrunCommandare replaced by the sharedCliError(re-exported) and a thin wrapper over the shared runner; human failures keep the<label> failed: <message>wording viafailureLabel. A registry429is nowrate_limited(exit 7) instead ofapi_error.lib/install/install-errors.ts:InstallErrornow extends the sharedCliError(and can carry a hint).commands/install-runner.ts:runInstalleris a thin wrapper over the sharedrunCommand. It setsprocess.exitCodeinstead of callingprocess.exit, so piped stdout drains. Human mode prints the hint, anddetails.conflictsis still listed after the message (throughfailureDetails).lib/install/registry-client.ts: a429israte_limited(exit 7) with the hint "Retry later.", matching publish/promote.commands/import.ts(from feat(registry)!: release CLI, idempotent publish, promote, quarantine review, signed downloads and GitHub OIDC trusted publishing #99, still onprocess.exit+ora): moved ontorunCommand, gained--json, and checks arguments before the API key, so a bad repo isusage_error(2) with or without a key. A 404 keeps its "Create a SKILL.md file in your repo and try again." hint.commands/pack.ts: the shared pack failure names the command (cannot validate ./x,cannot publish ./x).lib/release-cli.ts,docs/codebase-summary.md: the API key hint namesskillx config set-key, not the missingconfig set api-key.src/index.ts: main's new commands plus this PR'srouteUsageErrors+parseAsync.built-cli.test.ts: both test sets kept in onedescribe.docs/cli-reference.md: the installer's separate exit table (which called 7 "reserved") and the registry paragraph's own code list now point to the one table.config set-keyand interactivefindread stdin throughpromptLine: an empty or closed stdin isusage_error(exit2) instead of hanging into Node's exit13with no envelope. A piped value without a trailing newline (printf 'key' |) is still read./api/mcprow inCLAUDE.mdcorrected: tools are anonymous exceptget_kit.Closes #48
Refs #33
🤖 Generated with Claude Code