Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .opencode/agent/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
description: ALWAYS use this when writing docs
color: "#38A3EE"
---

You are an expert technical documentation writer

You are not verbose

Use a relaxed and friendly tone

The title of the page should be a word or a 2-3 word phrase

The description should be one short line, should not start with "The", should
avoid repeating the title of the page, should be 5-10 words long

Chunks of text should not be more than 2 sentences long

Each section is separated by a divider of 3 dashes

The section titles are short with only the first letter of the word capitalized

The section titles are in the imperative mood

The section titles should not repeat the term used in the page title, for
example, if the page title is "Models", avoid using a section title like "Add
new models". This might be unavoidable in some cases, but try to avoid it.

Check out the /packages/web/src/content/docs/docs/index.mdx as an example.

For JS or TS code snippets remove trailing semicolons and any trailing commas
that might not be needed.

If you are making a commit prefix the commit message with `docs:`
180 changes: 180 additions & 0 deletions docs/designs/dollar-skill-mentions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# CEO Plan: $skill Mentions — Manual Skill Inclusion

Generated by /plan-ceo-review on 2026-03-19
Branch: prax-dev | Mode: SCOPE EXPANSION
Repo: praxstack/opencode

## Vision

### 10x Check
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.

### Platonic Ideal
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.

## Implementation Approach

**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.

### Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│ FRONTEND (SolidJS) │
│ │
│ prompt-input.tsx slash-popover.tsx │
│ ┌────────────┐ $ or Cmd+Shift+S ┌──────────────────┐ │
│ │ Editor │ ──────────────────▶ │ PromptPopover │ │
│ │ (pills + │ │ mode="skill" │ │
│ │ tooltip) │ ◀── select ──────── │ [Recent] [All] │ │
│ └────────────┘ │ [Empty state] │ │
│ │ └──────────────────┘ │
│ │ submit │
│ ▼ │
│ prompt context (ContentPart[]) badge-strip │
│ [SkillPart, SkillPart, TextPart] ┌──────────────────┐ │
│ │ │ [skill ×] [skill ×]│ │
│ │ │ + context warning │ │
│ │ POST /session/:id/chat └──────────────────┘ │
└───────┼───────────────────────────────────┼───────────────────────┘
│ │ POST/DELETE /session/:id/skill
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ BACKEND (Effect) │
│ │
│ session.ts (routes) SessionSkills (Effect service) │
│ ┌────────────────┐ ┌────────────────────────┐ │
│ │ POST /:id/skill│ ────────▶ │ .add(sessionID, name) │ │
│ │ DELETE /:id/ │ │ .remove(sessionID, name)│ │
│ │ skill/:name │ │ .list(sessionID) │ │
│ │ GET /:id/skill │ │ .clear(sessionID) │ │
│ └────────────────┘ └───────────┬────────────┘ │
│ │ │
│ prompt.ts │ SQL │
│ ┌────────────────────┐ ▼ │
│ │ createUserMessage() │ ┌──────────────────────────┐ │
│ │ → extract SkillPart│ │ session_skills table │ │
│ │ → synthetic log │ │ session_id | skill_name │ │
│ │ │ │ added_at | token_est │ │
│ │ loop() │ └──────────────────────────┘ │
│ │ → SessionSkills │ │
│ │ .list(sessionID) │ In-memory LRU cache (5-min TTL) │
│ │ → LRU cache get │ ──▶ Skill.load(name) on miss │
│ │ → wrap <skill> XML │ │
│ │ → append to system │ sync channel: "session_skill" │
│ │ prompt array │ ──▶ frontend reads via useSync() │
│ └────────────────────┘ │
│ Skill.available() ← UNCHANGED │
│ SystemPrompt.skills() ← UNCHANGED │
│ SkillTool ← UNCHANGED │
└─────────────────────────────────────────────────────────────────┘
```

### Key Decisions

| Decision | Choice | Reasoning |
|----------|--------|-----------|
| Caching strategy | In-memory LRU with 5-min TTL | Fast after first load, handles edits within 5 min, evicts on session end |
| $ regex guard | Require whitespace/SOL before `$`, exclude `$$` | Prevents false positives with bash `$$` and `$VAR` in shell mode |
| `$-removal` scope | Only removes user-added skills (session_skills table) | Don't interfere with AI auto-loaded skills |
| Shortcut focus guard | `Cmd+Shift+S` only fires when prompt editor focused | Prevents accidental triggers from message history |

## Scope Decisions

| # | Proposal | Effort | Decision | Reasoning |
|---|----------|--------|----------|-----------|
| 1 | Skill preview tooltip (hover to see description) | S | ACCEPTED | 10 min CC, makes pills feel alive |
| 2 | Recent skills section in popover (last 5 used) | S | ACCEPTED | Data free with Approach B, intelligent picker |
| 3 | Keyboard shortcut `Cmd+Shift+S` | S | ACCEPTED | 10 min CC, keyboard-first UX |
| 4 | Skill categories in popover (group by source) | S | DEFERRED | Flat list fine for v1 |
| 5 | Context budget warning toast (>30K tokens) | S | ACCEPTED | Prevents subtle context cliff |
| 6 | Empty state UX (no skills installed) | S | ACCEPTED | 5 min CC, design hygiene |

## Accepted Scope (added to plan)
- Core: `$` prefix detection, skill popover, pill creation, badge strip, `$-removal` syntax
- Core: Server-side persistence via SessionSkills Effect service + session_skills SQL table
- Core: System prompt injection with `<skill>` XML tags + dedup with auto-loaded
- Core: CLI `$name` prefix parsing
- Expansion 1: Skill preview tooltip on hover (pills + badge strip)
- Expansion 2: Recent skills section at top of popover (last 5 from session_skills)
- Expansion 3: `Cmd+Shift+S` keyboard shortcut to open skill picker
- Expansion 5: Context budget warning toast when estimated tokens exceed 30K
- Expansion 6: Empty state UX in popover when no skills are installed
- Fix: `$$` guard in regex, shell mode guard, focus guard on shortcut, ParseError handling

## Deferred to TODOS.md
- Skill categories in popover (group by source directory) — revisit when skill count commonly exceeds 20
- Skill presets (save/load skill combos) — Phase 2
- Per-project skill profiles (`.opencode/skills.json`) — Phase 2
- Skill marketplace / install-from-web — Phase 3
- Skill analytics / usage patterns — Phase 3
- Skill composition (skill A references skill B) — research needed
- Refactor `handleInput()` conditional chain to dispatch table — separate PR

## Dream State Delta
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.

## Design Specifications (from /plan-design-review)

### Popover Layout
```
┌─────────────────────────────┐
│ ⌕ Filter... │ ← search input (auto-focused)
├─────────────────────────────┤
│ RECENT │ ← text-color-dimmed header
│ ★ brainstorming │
│ ★ tdd │
├─────────────────────────────┤
│ ALL SKILLS │ ← text-color-dimmed header
│ api-design │
│ brainstorming │
│ clean-code │
│ ... │
└─────────────────────────────┘
Max-width: 400px. Skeleton shimmer (3 rows) on first load.
```

### Empty State
```
┌─────────────────────────────┐
│ 💡 No skills installed │
│ │
│ Skills customize how the │
│ AI works — add them to │
│ ~/.claude/skills/ │
│ │
│ 📚 Browse available skills │
└─────────────────────────────┘
```

### Interaction State Table
```
FEATURE | LOADING | EMPTY | ERROR | SUCCESS
---------------------|-----------------|-----------------|-----------------|--------
$ Popover | Skeleton shimmer| 💡 Empty state | N/A (local) | Skill list
Badge strip | N/A | Hidden | N/A | [skill ×]
Skill pill | N/A | N/A | N/A | $name pill
Tooltip (hover) | "Loading..." | N/A | "Unavailable" | Rich popover
Context warning | N/A | N/A | N/A | ⚠️ toast
```

### Color Tokens
- Skill pill text: `text-syntax-string` (warm/amber — distinct from file=property, agent=type)
- Section headers: `text-color-dimmed`
- Badge strip: matches steer queue badge styling
- Popover: inherits `PromptPopover` theme

### Rich Tooltip Popover (on hover)
- 300ms hover delay, disappears on mouse leave
- Shows: skill name (bold), description (2-3 lines), source path (dimmed), token estimate (dimmed)
- `role="tooltip"` + `aria-describedby` linking

### Accessibility
- Badge `×` buttons: `aria-label="Remove skill {name}"`, min 44x44px touch target
- Popover: `role="listbox"` + `aria-label="Select a skill"`
- Keyboard: arrows navigate, Enter selects, Escape closes (inherited from PromptPopover)

## Review Status
- CEO Review: CLEAR (2026-03-19, SCOPE EXPANSION, 0 critical gaps)
- Eng Review: CLEAR (2026-03-19, FULL_REVIEW, 0 critical gaps)
- Design Review: CLEAR (2026-03-19, 6/10 → 9/10, 2 decisions made)
Loading
Loading