Skip to content

feat(skills): skills.sh marketplace browse and install#2608

Merged
k11kirky merged 2 commits into
mainfrom
posthog-code/skills-05-marketplace
Jun 12, 2026
Merged

feat(skills): skills.sh marketplace browse and install#2608
k11kirky merged 2 commits into
mainfrom
posthog-code/skills-05-marketplace

Conversation

@k11kirky

@k11kirky k11kirky commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Problem

Users have no way to discover and install community skills from within the app. They must manually find and copy skill files, with no browsing, previewing, or one-click install experience.

Changes

Backend: SkillsMarketplaceService

  • Added a new skills-marketplace service that integrates with the skills.sh search API to find community skills by query.
  • Skills are fetched as GitHub repository ZIP archives, with a short-lived in-process cache (5 min TTL, max 4 entries) to avoid redundant downloads.
  • preview() returns all files in a skill directory before anything touches disk, flagging skills that contain executable scripts and marking binary files as non-previewable.
  • install() extracts the skill into ~/.claude/skills/<skillId> as an ordinary editable user skill. An installed.json state file tracks which skills came from the marketplace solely to power the "Installed" badge in search results.
  • Zip-slip protection is enforced: any archive entry with path traversal sequences or backslashes is silently dropped.
  • validateSkillDirName is exported from the skills service so the marketplace service can reuse it.
  • The module is registered in the DI container and wired into the host router under skills.marketplace.{search,preview,install}.

Frontend: Marketplace UI

  • SkillsView gains an "Installed" / "Marketplace" tab bar. The existing skills list is shown under "Installed"; the new MarketplaceBrowse component is shown under "Marketplace".
  • MarketplaceBrowse provides a debounced search input (300 ms) that queries the marketplace. Results show skill name, source repo, install count, and an "Installed" badge when applicable. Selecting a result opens a resizable MarketplaceSkillPanel sidebar.
  • MarketplaceSkillPanel shows a full pre-install file preview. SKILL.md is rendered as Markdown (frontmatter stripped); all other files open in the read-only CodeMirror editor. A warning callout is shown when the skill contains scripts. Installing a skill that already exists locally prompts an overwrite confirmation dialog.
  • stripFrontmatter is extracted into its own module and shared between SkillDetailPanel and MarketplaceSkillPanel.
  • React Query hooks (useMarketplaceSearch, useMarketplacePreview, useInstallMarketplaceSkill) are added in useMarketplace.ts. A successful install invalidates the skills query so the "Installed" tab reflects the new skill immediately.

How did you test this?

A full unit test suite was added for SkillsMarketplaceService covering:

  • findSkillDirPrefix — shallowest-match selection and absent-skill handling.
  • collectSkillFiles — zip-slip path traversal rejection.
  • search — correct mapping of skills.sh results, installed-state decoration, and short-query early-exit.
  • preview — full file list with contents, binary file detection, missing-skill error, and invalid repository reference rejection.
  • install — successful extraction and state file write, overwrite guard and replacement, and invalid skill ID rejection.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

React Doctor could not complete this scan.

No React dependency found in /tmp/react-doctor-baseline-Zf0prF/package.json. Add "react" to dependencies (or peerDependencies) and re-run.

Reviewed by React Doctor for commit a1c1c5c.

@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff (2)

  1. packages/ui/src/features/skills/MarketplaceSkillPanel.tsx, line 292-295 (link)

    P2 Preview fetch errors are silently swallowed

    useMarketplacePreview returns an error field but it is not destructured here. When the preview request fails (network error, invalid skill reference, etc.), preview is undefined, files becomes [], and selected is undefined — so the UI falls through to "No content to preview". A user has no way to tell whether the content is genuinely absent or whether the fetch failed. Surfacing error with a distinct message (e.g. "Failed to load preview") would make failures actionable.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/ui/src/features/skills/MarketplaceSkillPanel.tsx
    Line: 292-295
    
    Comment:
    **Preview fetch errors are silently swallowed**
    
    `useMarketplacePreview` returns an `error` field but it is not destructured here. When the preview request fails (network error, invalid skill reference, etc.), `preview` is `undefined`, `files` becomes `[]`, and `selected` is `undefined` — so the UI falls through to "No content to preview". A user has no way to tell whether the content is genuinely absent or whether the fetch failed. Surfacing `error` with a distinct message (e.g. "Failed to load preview") would make failures actionable.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. packages/ui/src/features/skills/MarketplaceSkillPanel.tsx, line 314-317 (link)

    P2 Overwrite detection relies on substring-matching a server error message

    The "already exists" check on line 315 couples the client to the exact wording of the server error thrown in skills-marketplace.ts. If that message is ever reworded or localised, the overwrite confirmation dialog will silently stop appearing and the user will only see a generic "Failed to install skill" toast. Consider using a typed error (e.g. a distinct error code field or a custom error class) so the client can respond to the condition structurally rather than by text search.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/ui/src/features/skills/MarketplaceSkillPanel.tsx
    Line: 314-317
    
    Comment:
    **Overwrite detection relies on substring-matching a server error message**
    
    The `"already exists"` check on line 315 couples the client to the exact wording of the server error thrown in `skills-marketplace.ts`. If that message is ever reworded or localised, the overwrite confirmation dialog will silently stop appearing and the user will only see a generic "Failed to install skill" toast. Consider using a typed error (e.g. a distinct error `code` field or a custom error class) so the client can respond to the condition structurally rather than by text search.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/ui/src/features/skills/MarketplaceBrowse.tsx:101-103
**Duplicate `installsFormatter` definition**

`installsFormatter` is defined identically in both `MarketplaceBrowse.tsx` (line 101) and `MarketplaceSkillPanel.tsx` (line 273). Extracting it into a shared constant in `useMarketplace.ts` or a small `marketplaceFormatters.ts` helper would satisfy the OnceAndOnlyOnce principle and prevent the two definitions from drifting apart.

### Issue 2 of 3
packages/ui/src/features/skills/MarketplaceSkillPanel.tsx:292-295
**Preview fetch errors are silently swallowed**

`useMarketplacePreview` returns an `error` field but it is not destructured here. When the preview request fails (network error, invalid skill reference, etc.), `preview` is `undefined`, `files` becomes `[]`, and `selected` is `undefined` — so the UI falls through to "No content to preview". A user has no way to tell whether the content is genuinely absent or whether the fetch failed. Surfacing `error` with a distinct message (e.g. "Failed to load preview") would make failures actionable.

### Issue 3 of 3
packages/ui/src/features/skills/MarketplaceSkillPanel.tsx:314-317
**Overwrite detection relies on substring-matching a server error message**

The `"already exists"` check on line 315 couples the client to the exact wording of the server error thrown in `skills-marketplace.ts`. If that message is ever reworded or localised, the overwrite confirmation dialog will silently stop appearing and the user will only see a generic "Failed to install skill" toast. Consider using a typed error (e.g. a distinct error `code` field or a custom error class) so the client can respond to the condition structurally rather than by text search.

Reviews (1): Last reviewed commit: "feat(skills): skills.sh marketplace brow..." | Re-trigger Greptile

Comment thread packages/ui/src/features/skills/MarketplaceBrowse.tsx
@k11kirky k11kirky marked this pull request as ready for review June 11, 2026 13:54
Comment thread packages/workspace-server/src/services/skills-marketplace/skills-marketplace.ts Outdated
Comment thread packages/workspace-server/src/services/skills-marketplace/skills-marketplace.ts Outdated
@k11kirky k11kirky force-pushed the posthog-code/skills-05-marketplace branch from 26e5ccb to 1f13739 Compare June 12, 2026 07:50
@k11kirky k11kirky force-pushed the posthog-code/skills-04-validation branch from 41afe29 to 0bdd245 Compare June 12, 2026 07:50
@k11kirky k11kirky force-pushed the posthog-code/skills-05-marketplace branch from 1f13739 to 7853c15 Compare June 12, 2026 08:19
@k11kirky k11kirky force-pushed the posthog-code/skills-04-validation branch from 0bdd245 to 17f53b7 Compare June 12, 2026 08:19

k11kirky commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

@k11kirky k11kirky force-pushed the posthog-code/skills-05-marketplace branch from 7853c15 to c949c44 Compare June 12, 2026 11:43
@k11kirky k11kirky force-pushed the posthog-code/skills-04-validation branch from 17f53b7 to 17c16d7 Compare June 12, 2026 11:43
@k11kirky k11kirky changed the base branch from posthog-code/skills-04-validation to graphite-base/2608 June 12, 2026 12:06
@k11kirky k11kirky changed the base branch from graphite-base/2608 to main June 12, 2026 12:19
k11kirky added 2 commits June 12, 2026 12:20
Install is copy-and-forget: download, extract into
~/.claude/skills/<name>, done. From that moment it is an ordinary,
editable user skill — no upstream tracking, no hashes, no update
checks. Reinstall (confirm + overwrite) is the update story.

- SkillsMarketplaceService (workspace-server): search hits the
  skills.sh index at runtime (nothing cached to disk); preview
  downloads the repo zip from GitHub codeload, extracts in memory with
  the existing fflate unzipAsync, and returns the full file list +
  contents; install writes the skill directory and records
  {version, installed: {name: {repo}}} in ~/.claude/skills/
  installed.json, whose only purpose is the "Installed" badge.
- Safety: repo refs validated against owner/repo, skill ids against
  the directory-name rules, zip entries screened for zip-slip, binary/
  oversized files excluded from preview, 100 MB archive cap.
- skills.marketplace.* host-router routes (one-line forwards).
- UI: Browse tab in SkillsView with debounced search, install counts,
  "Installed" badges, full file-tree preview before install (PR 1
  components), an explicit warning chip when a skill contains
  scripts/, and a confirm-overwrite reinstall flow.
- No new dependencies; fflate was already in workspace-server.

Note: the GitHub-search fallback mentioned in the plan is omitted —
unauthenticated GitHub code search cannot find SKILL.md directories;
search errors surface in the UI instead.

Generated-By: PostHog Code
Task-Id: f4e84f1a-19c9-490c-9b98-47787a7dddcf
Review feedback on #2608: timeouts on the search and archive fetches,
content-length pre-check plus streaming byte cap on the download, a
decompressed-bytes budget so a small archive cannot zip-bomb the app,
and LRU recency for the archive cache instead of pure insertion order.

Generated-By: PostHog Code
Task-Id: f4e84f1a-19c9-490c-9b98-47787a7dddcf
@k11kirky k11kirky force-pushed the posthog-code/skills-05-marketplace branch from c949c44 to a1c1c5c Compare June 12, 2026 12:20
@k11kirky k11kirky merged commit 7bfa400 into main Jun 12, 2026
24 checks passed
@k11kirky k11kirky deleted the posthog-code/skills-05-marketplace branch June 12, 2026 12:35
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.

2 participants