Skip to content

feat(app): add a Skill Stacks tab to the desktop Skill Hub - #367

Merged
quickbeard merged 1 commit into
mainfrom
feat/skill-stack-desktop
Sep 14, 2026
Merged

quickbeard merged 1 commit into
mainfrom
feat/skill-stack-desktop

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

P3 of the Skill Stack integration (P0 #361 CLI · P1 #364 server routes · P2 #366 TUI). The desktop app's turn.

What it does

The Skill Hub now has a Skills | Skill Stacks tab strip (one sidebar entry, two halves). The Stacks tab lists the hub's stacks with owner, skill/member counts and a preview of the skills inside, with search and paging. Install opens a dialog: this project (any project of the connected server) or global, which agents, then it prepares, stops to ask skip / overwrite only when the server reports skills already installed, installs, refreshes, and shows what was installed, skipped, or failed.

Why it's shaped this way

  • Over the server, not the Electron bridge. Everything goes through v2.skillhub (feat(server): serve Skill Stack browse and install over the v2 HttpApi #364) on the connected server, so the install lands on the host that runs the agent — and it works against a remote server, which the single-skill Skill Hub cannot (that's what fix(desktop): route Skill Hub through the proxy-aware stack and refuse off-host project installs #363 blocks). No preload/IPC/main changes at all.
  • Scope is never a renderer-chosen path. "This project" sends the request scoped to that project's directory (createClient({ directory })); the server resolves the install root from it. That closes, for stacks, the hole the single-skill IPC bridge still has (parseScope trusts a renderer-supplied directory).
  • The model sees the new skills immediately. After install the flow calls instance.dispose for the target directory — what a config update does — because skill discovery is cached per instance and nothing else invalidates it.
  • Testable core. stack-install-model.ts is a state machine over injected calls; six unit tests cover straight-through install, stop-and-ask on collisions, cancel-discards-staging, failed prepare, failed install (staging consumed → retry must re-prepare), and refresh failure never masking a completed install.
  • Reuses the single-skill dialog's AGENT_ROWS and remembered-agents key (now exported), so one remembered choice serves both dialogs.

Not in this PR — a decision for you

The single-skill Skill Hub still installs via the Electron main process, which is why #363 has to refuse off-host projects. Moving it onto server routes (a skillhub.skill.* group next to stack.*) would remove that block, delete the IPC bridge, and make the whole Skill Hub work on web and remote servers. That's a clean follow-up; I didn't fold it in here because it changes shipped behaviour beyond the feature asked for.

Also: the sidebar entry is still gated on platform.skillhub (desktop), so the web build has no entry point to the Stacks page even though the page itself would work there. Same follow-up.

Verification

  • packages/app: bun typecheck clean; bun run test:unit 823 pass / 0 fail (6 new).
  • packages/desktop: bun typecheck clean (its renderer compiles the app).
  • i18n: 33 new skillstack.* / skillhub.tabs.* keys in en, es, vi.
  • DIVERGENCES.md row 228 extended (app.tsx route + i18n families); CHANGELOG.md Unreleased → Skills.
  • Not done: a visual pass in the running desktop app — the routes were driven end to end by the P1 exerciser and the P2 TUI view test, and this page's logic is unit-tested, but nobody has clicked it yet.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig

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.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig
@quickbeard
quickbeard merged commit 81b0a11 into main Sep 14, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the feat/skill-stack-desktop branch September 14, 2026 06:47
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.

1 participant