Repository navigation
feat(server): serve Skill Stack browse and install over the v2 HttpApi - #364
Merged
Merged
Conversation
Puts CoDev SkillHub Skill Stack traffic behind the server as the fork-only v2 group `server.skillhub`, so the TUI — which reaches the hub only through the SDK — and the desktop app share one implementation, and an install lands on the host that actually runs the agent rather than wherever the client happens to be. Install is two steps so a client can show what is already installed and ask before overwriting, without downloading the archive twice: prepare downloads, parses, and plans against the request location's project root (resolved server-side, never taken from the client) and parks the ZIP on disk under a stagingID with a 10-minute TTL; install consumes it, re- planning against the disk as it is then. Failures carry a machine-readable code (auth, not-found, already-installed, network, expired, unknown) so the clients never parse English. The exerciser gates every fork route in CI with --fail-on-missing --fail-on-skip and must never reach the live hub, so it installs a fake through the package's injectable transport — the same seam the desktop uses for Chromium's net.fetch — and the four scenarios drive the real handler end to end against a fixture stack. Clients regenerated: @opencode-ai/client (skillhub.listStacks/ prepareStack/installStack/discardStack) and the legacy JS SDK the TUI uses. No user-facing change yet; the TUI dialog and desktop tab build on this next. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig
quickbeard
added a commit
that referenced
this pull request
Sep 14, 2026
Adds the TUI's first way onto the CoDev SkillHub: `/stacks` (and the unbound `prompt_stacks` keybind) opens a browser for the hub's Skill Stacks, then walks scope (this project or global), an agent preset, and — only when prepare reports collisions — a skip-or-overwrite choice before installing. Escaping the conflict prompt discards the staging rather than leaving it to the server's TTL. Everything goes through the server's v2.skillhub routes (#364), so the install lands on the host that runs the agent, in the same folders `codev skill stack pull` writes. After a successful install the dialog disposes the server instance — what a config update does too — because skill discovery is cached per instance and nothing else invalidates it; without that the model would not see the new skills until a restart. Flow state lives in an awaited controller rather than components, as in dialog-configure.tsx: dialog.replace stores the element factory and can re-invoke it. And since replace() fires every prior step's onClose even when this flow advances itself, "did the user escape mid-download?" is answered by checking whether the dialog still shows the step we put up. The agent picker is a preset list because DialogSelect is single-choice (a per-row toggle needs its own keybind, like dialog.mcp.toggle); CoDev Code is in every preset and is folded in server-side regardless. Claude-Session: https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
quickbeard
added a commit
that referenced
this pull request
Sep 14, 2026
The Skill Hub gains a Skills | Skill Stacks tab strip. The Stacks tab lists the hub's stacks — owner, skill and member counts, a preview of the skills inside — and installs one through the server the app is connected to (the v2.skillhub routes from #364), so the install lands on the host that runs the agent and works against a remote server. No Electron bridge is involved: this is the first Skill Hub surface in the app that the web build could serve too. The install flow is a state machine over injected server calls (stack-install-model.ts) so its transitions — prepare, stop to ask about collisions, install, then dispose the instance so the model sees the new skills — are unit-tested without rendering. The dialog is a view over it, reusing the single-skill dialog's agent rows and remembered choice; scope "project" is carried as the request directory and resolved server-side, never as a path the renderer chooses. Numeric fields from the generated client are coerced at the two places they are compared or subtracted: Effect's Schema.Number admits the "NaN"/"Infinity" string encodings, so the generated type is not plain number. Claude-Session: https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.
P1 of the Skill Stack integration (P0 was #361): the server endpoints both UIs will drive, plus the regenerated clients. No user-facing change on its own — the TUI dialog (P2) and desktop Stacks tab (P3) land on top of this.
Routes (fork-only v2 group
server.skillhub)Two-step install so a client can show what's already installed and ask before overwriting, without downloading twice.
prepareparks the ZIP on disk underGlobal.Path.cache/skillhub/stacks/with a 10-minute TTL (not in memory — a stack can be hundreds of MB).installre-plans against the disk as it is then, so a skill that appeared between the two calls is still honoured byskip-existing.Location-scoped, never client-pathed.
scope: "project"resolves server-side to the request location's project root. This is the architectural fix for the off-host install that #363 currently blocks in the desktop app — once the desktop moves onto these routes, that block can go.Errors carry a code (
auth/not-found/already-installed/network/expired/unknown) matching the desktop's existingSkillHubErrorcodes, so P3 maps 1:1 and nobody parses English. Unknown and expired stagings deliberately look the same — the archive is gone either way and the fix is identical.Layering
Schema → Protocol → Server, as CLAUDE.md requires.
packages/servertakes the wire types through the protocol group's re-export rather than a new direct schema dependency, matching every other handler. Hub auth is the server host's shared~/.codev-hubsession — the user's own machine for every local server, which is how the TUI always runs.@codev/skillhub's transport seam from #363 means the server gets Node's default transport with no extra plumbing.CI: the exerciser
test:httpapiruns with--fail-on-missing --fail-on-skip, sotodoplaceholders would fail and the four routes needed real scenarios that never touch the live hub.skillhub-fake.tsswaps in a fixture hub through the package's injectable transport — the same seam the desktop uses for Chromium'snet.fetch— so there are no mocks: the real handler downloads, parses, plans, and stages a fixture stack. All three modes (effect,coverage,auth) pass for the new routes.Generated clients
@opencode-ai/clientgainsclient.skillhub.{listStacks,prepareStack,installStack,discardStack}(endpoint names overridden — the codegen's default of the last id segment would givelist/prepare/install, which collide the moment single-skill routes join the group). The legacy JS SDK the TUI uses is regenerated too.check:generatedwill be green once this is on a branch; locally it only diffs against HEAD.Verification
packages/client16 pass;packages/codev-skillhub245 pass.--include skillhubin all three modes: 4 pass / 0 fail / missing=0.DIVERGENCES.mdrows added (routes, generated clients, exerciser).🤖 Generated with Claude Code
https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig