|
| 1 | +# CEO Plan: $skill Mentions — Manual Skill Inclusion |
| 2 | + |
| 3 | +Generated by /plan-ceo-review on 2026-03-19 |
| 4 | +Branch: prax-dev | Mode: SCOPE EXPANSION |
| 5 | +Repo: praxstack/opencode |
| 6 | + |
| 7 | +## Vision |
| 8 | + |
| 9 | +### 10x Check |
| 10 | +The 10x version is a **full skill workspace** — not just `$mention` but a complete skill management system. Users curate per-project skill profiles, skills compose with each other, the system learns which skills work best for which tasks. `$` becomes the entry point to a rich skill ecosystem, not just a one-shot loader. Skill presets, usage analytics, conflict detection, and a skill marketplace. |
| 11 | + |
| 12 | +### Platonic Ideal |
| 13 | +The user opens a session. A small badge strip shows 3 skills auto-loaded for this project (from `.opencode/skills.json`). They type `$` and a beautiful popover shows all skills — grouped by category, with usage frequency indicators. They select `$brainstorming` and it appears as a warm-toned pill. The skill persists for the session. They can see via "skill trace" metadata exactly how the skill affected behavior. When they find a great combo (`$brainstorming` + `$tdd`), they save it as a **skill preset**. The next developer on the project gets those skills suggested. The system **feels** like having a mentor who knows exactly what tools to bring to each conversation. |
| 14 | + |
| 15 | +## Implementation Approach |
| 16 | + |
| 17 | +**Approach B: Server-Side Skill State** — full persistence via SessionSkills Effect service (like SessionSteer), new SQL table, sync channel. Skills survive page reload, visible across CLI/web/desktop. |
| 18 | + |
| 19 | +### Architecture |
| 20 | + |
| 21 | +``` |
| 22 | +┌─────────────────────────────────────────────────────────────────┐ |
| 23 | +│ FRONTEND (SolidJS) │ |
| 24 | +│ │ |
| 25 | +│ prompt-input.tsx slash-popover.tsx │ |
| 26 | +│ ┌────────────┐ $ or Cmd+Shift+S ┌──────────────────┐ │ |
| 27 | +│ │ Editor │ ──────────────────▶ │ PromptPopover │ │ |
| 28 | +│ │ (pills + │ │ mode="skill" │ │ |
| 29 | +│ │ tooltip) │ ◀── select ──────── │ [Recent] [All] │ │ |
| 30 | +│ └────────────┘ │ [Empty state] │ │ |
| 31 | +│ │ └──────────────────┘ │ |
| 32 | +│ │ submit │ |
| 33 | +│ ▼ │ |
| 34 | +│ prompt context (ContentPart[]) badge-strip │ |
| 35 | +│ [SkillPart, SkillPart, TextPart] ┌──────────────────┐ │ |
| 36 | +│ │ │ [skill ×] [skill ×]│ │ |
| 37 | +│ │ │ + context warning │ │ |
| 38 | +│ │ POST /session/:id/chat └──────────────────┘ │ |
| 39 | +└───────┼───────────────────────────────────┼───────────────────────┘ |
| 40 | + │ │ POST/DELETE /session/:id/skill |
| 41 | + ▼ ▼ |
| 42 | +┌─────────────────────────────────────────────────────────────────┐ |
| 43 | +│ BACKEND (Effect) │ |
| 44 | +│ │ |
| 45 | +│ session.ts (routes) SessionSkills (Effect service) │ |
| 46 | +│ ┌────────────────┐ ┌────────────────────────┐ │ |
| 47 | +│ │ POST /:id/skill│ ────────▶ │ .add(sessionID, name) │ │ |
| 48 | +│ │ DELETE /:id/ │ │ .remove(sessionID, name)│ │ |
| 49 | +│ │ skill/:name │ │ .list(sessionID) │ │ |
| 50 | +│ │ GET /:id/skill │ │ .clear(sessionID) │ │ |
| 51 | +│ └────────────────┘ └───────────┬────────────┘ │ |
| 52 | +│ │ │ |
| 53 | +│ prompt.ts │ SQL │ |
| 54 | +│ ┌────────────────────┐ ▼ │ |
| 55 | +│ │ createUserMessage() │ ┌──────────────────────────┐ │ |
| 56 | +│ │ → extract SkillPart│ │ session_skills table │ │ |
| 57 | +│ │ → synthetic log │ │ session_id | skill_name │ │ |
| 58 | +│ │ │ │ added_at | token_est │ │ |
| 59 | +│ │ loop() │ └──────────────────────────┘ │ |
| 60 | +│ │ → SessionSkills │ │ |
| 61 | +│ │ .list(sessionID) │ In-memory LRU cache (5-min TTL) │ |
| 62 | +│ │ → LRU cache get │ ──▶ Skill.load(name) on miss │ |
| 63 | +│ │ → wrap <skill> XML │ │ |
| 64 | +│ │ → append to system │ sync channel: "session_skill" │ |
| 65 | +│ │ prompt array │ ──▶ frontend reads via useSync() │ |
| 66 | +│ └────────────────────┘ │ |
| 67 | +│ Skill.available() ← UNCHANGED │ |
| 68 | +│ SystemPrompt.skills() ← UNCHANGED │ |
| 69 | +│ SkillTool ← UNCHANGED │ |
| 70 | +└─────────────────────────────────────────────────────────────────┘ |
| 71 | +``` |
| 72 | + |
| 73 | +### Key Decisions |
| 74 | + |
| 75 | +| Decision | Choice | Reasoning | |
| 76 | +|----------|--------|-----------| |
| 77 | +| Caching strategy | In-memory LRU with 5-min TTL | Fast after first load, handles edits within 5 min, evicts on session end | |
| 78 | +| $ regex guard | Require whitespace/SOL before `$`, exclude `$$` | Prevents false positives with bash `$$` and `$VAR` in shell mode | |
| 79 | +| `$-removal` scope | Only removes user-added skills (session_skills table) | Don't interfere with AI auto-loaded skills | |
| 80 | +| Shortcut focus guard | `Cmd+Shift+S` only fires when prompt editor focused | Prevents accidental triggers from message history | |
| 81 | + |
| 82 | +## Scope Decisions |
| 83 | + |
| 84 | +| # | Proposal | Effort | Decision | Reasoning | |
| 85 | +|---|----------|--------|----------|-----------| |
| 86 | +| 1 | Skill preview tooltip (hover to see description) | S | ACCEPTED | 10 min CC, makes pills feel alive | |
| 87 | +| 2 | Recent skills section in popover (last 5 used) | S | ACCEPTED | Data free with Approach B, intelligent picker | |
| 88 | +| 3 | Keyboard shortcut `Cmd+Shift+S` | S | ACCEPTED | 10 min CC, keyboard-first UX | |
| 89 | +| 4 | Skill categories in popover (group by source) | S | DEFERRED | Flat list fine for v1 | |
| 90 | +| 5 | Context budget warning toast (>30K tokens) | S | ACCEPTED | Prevents subtle context cliff | |
| 91 | +| 6 | Empty state UX (no skills installed) | S | ACCEPTED | 5 min CC, design hygiene | |
| 92 | + |
| 93 | +## Accepted Scope (added to plan) |
| 94 | +- Core: `$` prefix detection, skill popover, pill creation, badge strip, `$-removal` syntax |
| 95 | +- Core: Server-side persistence via SessionSkills Effect service + session_skills SQL table |
| 96 | +- Core: System prompt injection with `<skill>` XML tags + dedup with auto-loaded |
| 97 | +- Core: CLI `$name` prefix parsing |
| 98 | +- Expansion 1: Skill preview tooltip on hover (pills + badge strip) |
| 99 | +- Expansion 2: Recent skills section at top of popover (last 5 from session_skills) |
| 100 | +- Expansion 3: `Cmd+Shift+S` keyboard shortcut to open skill picker |
| 101 | +- Expansion 5: Context budget warning toast when estimated tokens exceed 30K |
| 102 | +- Expansion 6: Empty state UX in popover when no skills are installed |
| 103 | +- Fix: `$$` guard in regex, shell mode guard, focus guard on shortcut, ParseError handling |
| 104 | + |
| 105 | +## Deferred to TODOS.md |
| 106 | +- Skill categories in popover (group by source directory) — revisit when skill count commonly exceeds 20 |
| 107 | +- Skill presets (save/load skill combos) — Phase 2 |
| 108 | +- Per-project skill profiles (`.opencode/skills.json`) — Phase 2 |
| 109 | +- Skill marketplace / install-from-web — Phase 3 |
| 110 | +- Skill analytics / usage patterns — Phase 3 |
| 111 | +- Skill composition (skill A references skill B) — research needed |
| 112 | +- Refactor `handleInput()` conditional chain to dispatch table — separate PR |
| 113 | + |
| 114 | +## Dream State Delta |
| 115 | +This plan gets us to **~40% of the 12-month ideal.** Foundation: user-initiated loading, server persistence, badge strip, removal, recent skills, tooltip, shortcut, budget warning, empty state. Remaining: presets, per-project profiles, marketplace, analytics, composition. |
| 116 | + |
| 117 | +## Design Specifications (from /plan-design-review) |
| 118 | + |
| 119 | +### Popover Layout |
| 120 | +``` |
| 121 | +┌─────────────────────────────┐ |
| 122 | +│ ⌕ Filter... │ ← search input (auto-focused) |
| 123 | +├─────────────────────────────┤ |
| 124 | +│ RECENT │ ← text-color-dimmed header |
| 125 | +│ ★ brainstorming │ |
| 126 | +│ ★ tdd │ |
| 127 | +├─────────────────────────────┤ |
| 128 | +│ ALL SKILLS │ ← text-color-dimmed header |
| 129 | +│ api-design │ |
| 130 | +│ brainstorming │ |
| 131 | +│ clean-code │ |
| 132 | +│ ... │ |
| 133 | +└─────────────────────────────┘ |
| 134 | +Max-width: 400px. Skeleton shimmer (3 rows) on first load. |
| 135 | +``` |
| 136 | + |
| 137 | +### Empty State |
| 138 | +``` |
| 139 | +┌─────────────────────────────┐ |
| 140 | +│ 💡 No skills installed │ |
| 141 | +│ │ |
| 142 | +│ Skills customize how the │ |
| 143 | +│ AI works — add them to │ |
| 144 | +│ ~/.claude/skills/ │ |
| 145 | +│ │ |
| 146 | +│ 📚 Browse available skills │ |
| 147 | +└─────────────────────────────┘ |
| 148 | +``` |
| 149 | + |
| 150 | +### Interaction State Table |
| 151 | +``` |
| 152 | +FEATURE | LOADING | EMPTY | ERROR | SUCCESS |
| 153 | +---------------------|-----------------|-----------------|-----------------|-------- |
| 154 | +$ Popover | Skeleton shimmer| 💡 Empty state | N/A (local) | Skill list |
| 155 | +Badge strip | N/A | Hidden | N/A | [skill ×] |
| 156 | +Skill pill | N/A | N/A | N/A | $name pill |
| 157 | +Tooltip (hover) | "Loading..." | N/A | "Unavailable" | Rich popover |
| 158 | +Context warning | N/A | N/A | N/A | ⚠️ toast |
| 159 | +``` |
| 160 | + |
| 161 | +### Color Tokens |
| 162 | +- Skill pill text: `text-syntax-string` (warm/amber — distinct from file=property, agent=type) |
| 163 | +- Section headers: `text-color-dimmed` |
| 164 | +- Badge strip: matches steer queue badge styling |
| 165 | +- Popover: inherits `PromptPopover` theme |
| 166 | + |
| 167 | +### Rich Tooltip Popover (on hover) |
| 168 | +- 300ms hover delay, disappears on mouse leave |
| 169 | +- Shows: skill name (bold), description (2-3 lines), source path (dimmed), token estimate (dimmed) |
| 170 | +- `role="tooltip"` + `aria-describedby` linking |
| 171 | + |
| 172 | +### Accessibility |
| 173 | +- Badge `×` buttons: `aria-label="Remove skill {name}"`, min 44x44px touch target |
| 174 | +- Popover: `role="listbox"` + `aria-label="Select a skill"` |
| 175 | +- Keyboard: arrows navigate, Enter selects, Escape closes (inherited from PromptPopover) |
| 176 | + |
| 177 | +## Review Status |
| 178 | +- CEO Review: CLEAR (2026-03-19, SCOPE EXPANSION, 0 critical gaps) |
| 179 | +- Eng Review: CLEAR (2026-03-19, FULL_REVIEW, 0 critical gaps) |
| 180 | +- Design Review: CLEAR (2026-03-19, 6/10 → 9/10, 2 decisions made) |
0 commit comments