Skip to content

feat(desktop): add a native Skill Hub to the desktop app - #331

Merged
quickbeard merged 4 commits into
mainfrom
feat/desktop-skillhub
Aug 26, 2026
Merged

quickbeard merged 4 commits into
mainfrom
feat/desktop-skillhub

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Summary

Adds a Skill Hub entry to the desktop sidebar, directly above Settings, opening a native SkillHub browser: search/sort/domain-filtered browsing of the registry, skill detail with rendered SKILL.md and read-only stars/downloads, and one-click install into the current project or globally — with the same agent semantics as codev skill pull (CoDev Code locked and always included, one real copy plus links). Publishing/ratings/comments stay on the web hub, reachable via "Open in SkillHub".

Why native (not an embed)

SkillHub sends X-Frame-Options: SAMEORIGIN + frame-ancestors 'self' (iframes blocked), its web UI is login-walled and Vietnamese-only, and its API serves no Access-Control-Allow-Origin on /api/v1 plus a 403 on cross-origin writes — so the renderer can never call it directly. All hub HTTP originates in the Electron main process, bridged over IPC with the fork's established gateway-SSO wiring template. Auth rides the same shared session file as codev skill and codevhub; browsing and installs work anonymously.

What's here

  • @codev/skillhub extensions (fork-only package): src/hub.ts — browseHubSkills (page/limit/sort/domain + the full card shape: stars, downloads, author, runtimes) and getSkillDetail (SKILL.md text inlined by the API); a new {kind:"agents-at", root} install target so a host whose cwd isn't the project (Electron main) can install into an explicit project root — scope-based skill-dirs functions became thin wrappers over root-based cores, safeSegment/safeSkillDir guards untouched. CLI surface byte-stable.
  • Desktop bridge (fork-only src/main/skillhub/{controller,ipc}.ts + tracked one-line wirings in main/preload/renderer): three channels (skillhub-browse|detail|install), input validation, discriminated SkillHubResult unions with machine-readable error codes (already-installed drives the overwrite offer, network the unreachable card), and the CoDev-Code agent lock enforced main-side. @codev/skillhub under desktop devDependencies (electron-vite bundling rule).
  • App UI (fork-only src/skillhub/**, pages/skill-hub*.tsx, components/skill-hub-nav.tsx): browse page (debounced search, sort select, domain segmented control, card grid, pagination), detail page (session-ui Markdown pipeline — hub text never reaches innerHTML), install dialog (project/global scope with project picker preselected to the sidebar selection; agent checkboxes with CoDev Code checked+disabled; overwrite prompt showing the existing path; placements listed on success). Sidebar row sits above Settings via a wrapper in fork-only app-sidebar.tsx — upstream HomeUtilityNav untouched. Web builds hide the entry and show a desktop-only card.
  • Wire types owned by the app (src/skillhub/types.ts, exported as @opencode-ai/app/skillhub/types) — the renderer never imports the node package; skillhub? capability on PlatformBase.
  • i18n keys in en/es/vi; DIVERGENCES.md rows extended (platform.tsx, app package.json) + a new "Desktop Skill Hub wiring" row; CHANGELOG entry.

Verification

  • Unit: 194 tests in codev-skillhub (new: hub API mapping/params/auth-header matrix, agents-at placement/guards), 5 controller tests in packages/desktop, 716 in packages/app (test:unit, incl. browse-model debounce/page-reset). All typechecks clean.
  • Live main-process probe against the real hub: browse (13 results), detail (SKILL.md found among 11,910 inlined files), install into a scratch project root → .agents/skills/<name> store + .claude/skills/<name> relative symlink, re-install → already-installed.
  • Live UI via CDP on bun dev:desktop: sidebar row labeled "Skill Hub" rendered directly above Settings; click navigates and marks the row selected; browse page shows search box, domain tabs, and 24 live cards; card → detail renders the markdown with Install + Open-in-SkillHub; install dialog shows "This project"/"Global (all projects)" and the CoDev Code checkbox {disabled: true, checked: true} with its "Always included" hint; Escape closes; back returns to the preserved list.

🤖 Generated with Claude Code

quickbeard and others added 2 commits August 26, 2026 15:34
A new Skill Hub sidebar entry (directly above Settings) opens a native
SolidJS page for browsing the CoDev SkillHub: search, sort, and domain
filters, skill detail with rendered SKILL.md plus stars/downloads, and
install into the current project or globally with the CLI's agent
semantics (CoDev Code locked and always included).

All hub HTTP originates in the Electron main process — the hub serves no
ACAO on /api/v1 and 403s cross-origin writes — bridged over IPC using
the gateway-SSO wiring template:

- @codev/skillhub grows a richer hub API (src/hub.ts: browseHubSkills
  with page/sort/domain + full card shape, getSkillDetail with inlined
  files) and an explicit-root install target ({kind:"agents-at"}) so the
  main process can install into a project directory that is not its cwd;
  scope-based skill-dirs functions became wrappers over root-based cores.
- Fork-only desktop controller/ipc (src/main/skillhub/) normalizes
  errors into machine-readable codes (auth / not-found /
  already-installed / network / unknown) and folds CoDev Code into the
  agent set server-side.
- Fork-only app pages (pages/skill-hub*.tsx, src/skillhub/**) with a
  module-level browse model (MemoryRouter has no shareable URLs), the
  session-ui Markdown pipeline for SKILL.md, and an install dialog with
  a locked CoDev Code row and safe overwrite flow. Web builds hide the
  sidebar entry and show a desktop-only card on the route.
- i18n keys in en/es/vi; DIVERGENCES rows extended/added; CHANGELOG
  entry.

Verified live against the real hub via CDP on the running app (sidebar
placement, browse/detail/dialog) and a controller probe (project-scoped
install layout, overwrite guard). 194 + 5 + 716 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard
quickbeard force-pushed the feat/desktop-skillhub branch from cedab6f to 4be4c05 Compare August 26, 2026 08:35
quickbeard and others added 2 commits August 26, 2026 15:45
The domain tabs were crushed into the segmented control's default fixed
232px track, clipping every label, and the page capped itself at a
centered 760px column. The tab strip now uses the control's full-width
modifier and the grid fills the content area (up to 4 columns on wide
windows).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The ScrollView root sizes to its content, so with no result cards the
whole column — header, tabs, empty message — collapsed to its intrinsic
width and the tab labels clipped again. Both Skill Hub pages now pin the
ScrollView to w-full so the layout is identical with and without
results.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard
quickbeard merged commit cb914bd into main Aug 26, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the feat/desktop-skillhub branch August 26, 2026 08:57
quickbeard added a commit that referenced this pull request Aug 26, 2026
* chore: sync upstream v1.18.19

Replay of the resolved chore/sync-upstream branch (upstream opencode
v1.18.4..v1.18.19 as a filtered 3-way patch per the DIVERGENCES.md
runbook: 714 files applied clean, 50 conflicts resolved per the
divergence table) onto current main, reconciling it with the fork work
merged since (#285-#331):

- fork manifests keep their post-base additions (@codev/skillhub,
  @codev/gateway, @codev/upload, @clack/core deps; skillhub/i18n
  exports; desktop test script) on the synced 1.18.19 version
- client-side message ordering converges on upstream's session-message
  helpers ((time.created, id) — upstream adopted the same fix);
  fork-only utils/message-order.ts deleted; the server keeps
  MessageV2.chronological and the prompt-loop exit comparison (stored
  fork assistants predate parentID)
- the desktop menu and updater dialogs move onto upstream's
  native-translations pipeline (desktop.menu.*/desktop.updater.* keys
  via nativeT); the fork's main/i18n.ts translator and
  refreshMenuLanguage are deleted as superseded; fork menu items
  (Log in/Configure, Upload Session Logs) re-added with registry keys
- the desktop-native locale registry extends to en/es/vi — the fork's
  Vietnamese rides upstream's trimmed registry (labels, tags,
  detection, translate-app derivation, renderer vi bundle)
- fork menu.*/updater.dialog.* dict families renamed to the upstream
  key scheme in en/es/vi; es duplicates dropped; console vi picks up
  upstream's reworked go.faq.a5.* keys
- upstream's notification onClick callback supersedes the fork's
  notification-click util (deleted); notification icon points at the
  app's own favicon instead of opencode.ai
- Skill Hub, gateway hardening, codev skill CLI, session pages, and
  dialog close-button divergences re-applied per their DIVERGENCES rows
- DIVERGENCES rows for the menu, updater, desktop-native, and message
  ordering updated to the post-sync shape

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: sync upstream v1.18.23

Apply upstream opencode v1.18.19..v1.18.23 as a filtered 3-way patch per
the DIVERGENCES.md runbook on top of the replayed v1.18.19 sync: 226
files applied clean, 8 conflicts resolved per the divergence table:

- message-timeline keeps the fork's SessionActionsMenu (pinned sessions
  row) wired to upstream's new useSessionArchive service; the local
  archive helper upstream deleted is routed through sessionArchive
- run-process.test.ts reverts to upstream wholesale — upstream adopted
  the retrying unknown-finish contract the fork shipped, exactly the
  outcome its divergence row planned for
- ui i18n keeps the CoDev Go wording/pricing in en/es/vi (also fixes an
  OpenCode Go rebrand miss in the fork's vi); vscode manifest keeps the
  fork identity on the 1.18.23 version
- merge-base docs keep the fork rebrand and take upstream's additions
  (acp "type": "custom", github action share/variant notes)
- console vi follows upstream's go.* key churn; app callers follow the
  platform.openLink -> openExternal rename

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <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.

1 participant