Skip to content

feat(skills): redesign skills catalog — full-page editor, repo import, reliable materialization - #148

Merged
DIodide merged 1 commit into
stagingfrom
feat/skills-catalog-v2
Jun 21, 2026
Merged

feat(skills): redesign skills catalog — full-page editor, repo import, reliable materialization#148
DIodide merged 1 commit into
stagingfrom
feat/skills-catalog-v2

Conversation

@DIodide

@DIodide DIodide commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Builds on Skill Packs (#147). Three things: fix the catalog UX, add bulk/repo/template import, and make skills actually reliable across both harness types.

1. UX — no more nested modals

The skill-pack editor was a modal that opened the catalog in a second modal. It's now a full page: /skill-packs/new and /skill-packs/$packId render a shared SkillPackEditor with the form on the left and the catalog embedded on the right — no nesting. The list page is list-only and navigates to those routes. (Only the skill preview and delete remain as single, non-stacked dialogs.)

2. Bulk add — repos & templates

  • Import a whole repo: a new Convex action importSkillRepo lists a repo's skills via the GitHub trees API (skills/<id>/SKILL.md etc.), fetches + caches each SKILL.md and the repo's top-level AGENTS.md / CLAUDE.md, indexes them, and returns them to drop into the pack. The editor has an owner/repo input (e.g. greensock/gsap-skills → 8 GSAP skills + its .md in one click).
  • Pre-built templates: GSAP, Anthropic Skills, Superpowers, Vercel — each runs the same import and pre-fills the pack.
  • Multi-add is natural now that the catalog is an inline panel (toggle many; they accumulate).
  • Repo input is validated and host-pinned to GitHub (no SSRF; ./.. rejected).

3. Reliability (the deep dive) — skills now actually work for Claude Code

The deep dive found a real HIGH bug: the ACP gateway silently skipped any skill whose SKILL.md wasn't cached yet, with no fallback — so a freshly-added skill didn't reach Claude Code until the frontend's ensureSkillDetails happened to finish (a race). The default loop already had a 3-stage GitHub fallback; the gateway didn't.

Fix: extracted the GitHub fetch into app/services/skill_content.py (fetch_skill_md), refactored chat.py to share it (behavior preserved — verified line-by-line), and the gateway now back-fills missing SKILL.md on demand. It's bounded by an 8s budget + 20-skill cap so it can never stall provisioning, and authenticated via GITHUB_TOKEN (5000/hr vs 60/hr). Distinct skills sharing a trailing id no longer collide on one ~/.claude/skills dir.

Verification

3-dimension adversarial review:

  • Backend: confirmed the chat.py refactor is faithful and SSRF-safe; flagged two operational majors — unbounded back-fill (could stall POST /sessions) and an unauthenticated gateway fetch (60/hr shared) — both fixed (budget+cap; GITHUB_TOKEN).
  • Frontend: confirmed-correct — nested-modal gone, editor remounts per pack (no stale state), save/import/list all correct.
  • End-to-end: confirmed-correct — the silent-skip bug is genuinely fixed (no remaining silent-failure path), default loop intact, import idempotent, and the gsap repo layout verified against the live API (8 skills + AGENTS.md/CLAUDE.md).

Tests: new test_skill_content.py (resolver + auth headers) + slug-collision/back-fill-cap tests. Convex 203, FastAPI 350, frontend 235; tsc clean (no new errors).

Notes

  • Set GITHUB_TOKEN on the FastAPI backend env to lift the gateway's GitHub rate limit (the Convex side already uses it).
  • Worth a manual smoke test: open Skill Packs → New → "GSAP" template → confirm 8 skills + AGENTS.md/CLAUDE.md populate, save, attach to a Claude Code harness, and the skills materialize in the sandbox.

…port, reliable materialization

UX: the skill-pack editor was a modal with a NESTED catalog modal. It's now a
full page (routes/skill-packs/new + $packId render a shared SkillPackEditor:
left = form, right = embedded catalog panel — no nested modals). The list page
is list-only and navigates to those routes.

Bulk import: convex/skills.ts importSkillRepo lists a repo's skills via the
GitHub trees API, fetches + caches each SKILL.md (+ the repo's AGENTS.md /
CLAUDE.md), indexes them, and returns them to drop into a pack. The editor adds
an 'import a GitHub repo' input (e.g. greensock/gsap-skills) and four pre-built
templates. Repo input is validated/host-pinned (no SSRF); './..' rejected.

Reliability (the deep dive): the ACP gateway used to SILENTLY SKIP skills whose
SKILL.md wasn't cached, with no fallback — so a freshly-added skill didn't reach
Claude Code until ensureSkillDetails happened to finish. Fix: extracted the
GitHub fetch into app/services/skill_content.py (fetch_skill_md), refactored
chat.py to share it, and the gateway now back-fills missing SKILL.md on demand —
bounded by an 8s budget + a 20-skill cap so it can never stall provisioning, and
authenticated via GITHUB_TOKEN (5000/hr vs 60/hr). Distinct skills sharing a
trailing id no longer collide on one ~/.claude/skills dir.

Adversarially verified (multi-agent): chat.py refactor is faithful; nested-modal
gone; gsap path confirmed against the live repo; fixed the two operational
majors (unbounded back-fill, unauthenticated fetch) the review found. Tests:
new test_skill_content.py + slug/cap tests; Convex 203, FastAPI 350, frontend
235; tsc clean (no new errors).
@DIodide
DIodide merged commit e600f84 into staging Jun 21, 2026
4 checks passed
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