What task are you trying to do?
As a non-technical user, I want to browse, enable, disable, and configure PawWork skills from inside the app — without opening the skills/ directory, editing markdown frontmatter, or knowing what an invocation trigger looks like.
Skills are prompt-level extensions: markdown files with frontmatter that describe "when to use" and "what to do", injected into the model's context on demand. They are the primary way PawWork expresses product knowledge and specialized workflows today (email drafting, meeting summary, document export, Xiaohongshu writing, and so on). They are also the easiest extension surface to make user-facing — pure content, no code execution, no runtime lifecycle problem.
What do you do today?
Skills live as files in skills/ with frontmatter metadata and markdown body. They are auto-loaded by the harness and matched against the user turn via the trigger descriptions. The user has no way to see which skills exist, which are loaded, or which are being matched, and has no way to disable one that is triggering wrongly or enable a skill that ships disabled by default.
There is no GUI surface for skills at all. The Electron app does not render skill state anywhere, not even read-only. If a user wants to add or remove a skill they have to find the skills/ directory in the filesystem and edit markdown files manually. For a non-technical target user this is a blocker.
What would a good result look like?
A dedicated Skills page in the desktop app, reachable from a left sidebar entry. Layout mirrors the plugin management GUI tracked in #30 so users learn one interaction pattern for both: a grid of skill cards with category chips and a search box, each card showing name / short description / enabled state with toggle, skills that declare parameters expose a "settings" entry that opens a form, and an empty state points at the bundled skills list rather than at a directory path.
The work breaks into four layers that mirror the plugin GUI structure but are substantially simpler because skills are content, not code:
Layer 0 — Server / IPC API. The opencode HTTP server exposes skill operations the GUI can call: list available skills with metadata (name, description, category, enabled state), get the full body of a skill for preview, enable / disable, read / write per-skill settings where the skill declares a schema. Mirrors the plugin Layer 0 contract, backed by the existing skill loader.
Layer 1 — Enable / disable protocol. Config schema gains a parallel disabledSkills: string[] field. The skill loader skips entries listed there. GUI toggle adds / removes the skill name. No restart needed because skills are loaded on-demand per turn, not at process start — lifecycle is trivially simpler than plugins.
Layer 2 — Bundled skill registry. A JSON manifest bundled with the app listing PawWork's first-party skills: category i18n key, description i18n key, version, author. Third-party skills added by the user to skills/ still show up in the list via filesystem scan but do not carry the curated metadata. First-party lineup starts from what exists in the repo today and grows with product work.
Layer 3 — Settings forms. Skills that declare parameters in their frontmatter publish a schema (JSON Schema or zod-equivalent) the app renders as a form. Secret fields go through platform-native secret storage per AGENTS.md. Skills without a schema just show description + enable toggle, no form.
Layer 4 — GUI page, sidebar entry. Dedicated Skills page rendering the registry + filesystem skills as cards. Enable toggle, settings form for skills that declare one, preview panel showing the skill body. Sidebar entry appears in the left nav next to Sessions and, eventually, Plugins.
Which audience does this matter to most?
Both
Extra context
Why P2 while plugin GUI is P3. Skills are the actual content surface a non-technical user interacts with — they encode most of PawWork's productized workflows. Plugin GUI is infrastructure for extensions that mostly do not exist yet. Skills GUI has lower technical complexity (no hot reload, no process restart, no dynamic code loading) and higher user-visible value (they see product capability directly). Ship this first.
Relationship to #30. Parallel but separate. Skills and plugins stay as two distinct concepts with two distinct GUI pages and two sidebar entries. Not merged into an "Extensions" tab. Rationale: skill = prompt content, plugin = code extension; merging confuses the mental model for non-technical users. Layer 0 APIs can share HTTP route conventions and Layer 3 form rendering can share UI components, but the config protocol, loader, and lifecycle are independent.
Sidebar entry structure. Long-term plan is two sidebar entries: Skills (this issue) and Plugins (#30). This issue lands the Skills entry first; the Plugins entry lands whenever #30 does. The status popover read-only view for plugins stays until then.
i18n. Skill manifest name / description / category are i18n keys; strings live in packages/app/src/i18n/{zh,en}.ts. Follows the zh + en only policy.
Scheduling. Depends on #26 Phase 2 sidebar redesign for the sidebar entry target, same as #30. Can start the Layer 0-3 backend in parallel with #26 since those are pure server + config work.
Reference paths.
- Skills directory:
skills/
- Opencode skill loader:
packages/opencode/src/ (exact path TBD at implementation — locate via grep for skill loading logic)
- Design reference:
design/src/plugins_view.jsx shows the card grid pattern that Skills GUI should mirror
- Config path: same file that owns
plugin array ownership, extend with disabledSkills
What task are you trying to do?
As a non-technical user, I want to browse, enable, disable, and configure PawWork skills from inside the app — without opening the
skills/directory, editing markdown frontmatter, or knowing what an invocation trigger looks like.Skills are prompt-level extensions: markdown files with frontmatter that describe "when to use" and "what to do", injected into the model's context on demand. They are the primary way PawWork expresses product knowledge and specialized workflows today (email drafting, meeting summary, document export, Xiaohongshu writing, and so on). They are also the easiest extension surface to make user-facing — pure content, no code execution, no runtime lifecycle problem.
What do you do today?
Skills live as files in
skills/with frontmatter metadata and markdown body. They are auto-loaded by the harness and matched against the user turn via the trigger descriptions. The user has no way to see which skills exist, which are loaded, or which are being matched, and has no way to disable one that is triggering wrongly or enable a skill that ships disabled by default.There is no GUI surface for skills at all. The Electron app does not render skill state anywhere, not even read-only. If a user wants to add or remove a skill they have to find the
skills/directory in the filesystem and edit markdown files manually. For a non-technical target user this is a blocker.What would a good result look like?
A dedicated Skills page in the desktop app, reachable from a left sidebar entry. Layout mirrors the plugin management GUI tracked in #30 so users learn one interaction pattern for both: a grid of skill cards with category chips and a search box, each card showing name / short description / enabled state with toggle, skills that declare parameters expose a "settings" entry that opens a form, and an empty state points at the bundled skills list rather than at a directory path.
The work breaks into four layers that mirror the plugin GUI structure but are substantially simpler because skills are content, not code:
Layer 0 — Server / IPC API. The opencode HTTP server exposes skill operations the GUI can call: list available skills with metadata (name, description, category, enabled state), get the full body of a skill for preview, enable / disable, read / write per-skill settings where the skill declares a schema. Mirrors the plugin Layer 0 contract, backed by the existing skill loader.
Layer 1 — Enable / disable protocol. Config schema gains a parallel
disabledSkills: string[]field. The skill loader skips entries listed there. GUI toggle adds / removes the skill name. No restart needed because skills are loaded on-demand per turn, not at process start — lifecycle is trivially simpler than plugins.Layer 2 — Bundled skill registry. A JSON manifest bundled with the app listing PawWork's first-party skills: category i18n key, description i18n key, version, author. Third-party skills added by the user to
skills/still show up in the list via filesystem scan but do not carry the curated metadata. First-party lineup starts from what exists in the repo today and grows with product work.Layer 3 — Settings forms. Skills that declare parameters in their frontmatter publish a schema (JSON Schema or zod-equivalent) the app renders as a form. Secret fields go through platform-native secret storage per AGENTS.md. Skills without a schema just show description + enable toggle, no form.
Layer 4 — GUI page, sidebar entry. Dedicated Skills page rendering the registry + filesystem skills as cards. Enable toggle, settings form for skills that declare one, preview panel showing the skill body. Sidebar entry appears in the left nav next to Sessions and, eventually, Plugins.
Which audience does this matter to most?
Both
Extra context
Why P2 while plugin GUI is P3. Skills are the actual content surface a non-technical user interacts with — they encode most of PawWork's productized workflows. Plugin GUI is infrastructure for extensions that mostly do not exist yet. Skills GUI has lower technical complexity (no hot reload, no process restart, no dynamic code loading) and higher user-visible value (they see product capability directly). Ship this first.
Relationship to #30. Parallel but separate. Skills and plugins stay as two distinct concepts with two distinct GUI pages and two sidebar entries. Not merged into an "Extensions" tab. Rationale: skill = prompt content, plugin = code extension; merging confuses the mental model for non-technical users. Layer 0 APIs can share HTTP route conventions and Layer 3 form rendering can share UI components, but the config protocol, loader, and lifecycle are independent.
Sidebar entry structure. Long-term plan is two sidebar entries: Skills (this issue) and Plugins (#30). This issue lands the Skills entry first; the Plugins entry lands whenever #30 does. The status popover read-only view for plugins stays until then.
i18n. Skill manifest
name/description/categoryare i18n keys; strings live inpackages/app/src/i18n/{zh,en}.ts. Follows thezh + enonly policy.Scheduling. Depends on #26 Phase 2 sidebar redesign for the sidebar entry target, same as #30. Can start the Layer 0-3 backend in parallel with #26 since those are pure server + config work.
Reference paths.
skills/packages/opencode/src/(exact path TBD at implementation — locate via grep for skill loading logic)design/src/plugins_view.jsxshows the card grid pattern that Skills GUI should mirrorpluginarray ownership, extend withdisabledSkills