Skip to content

Commit bb262c9

Browse files
authored
feat(plugin): consolidate to single systematic_skill tool (#28)
Replace systematic_find_* tools with systematic_skill for skill loading and discovery via description. Add logging for plugin initialization with version tracking.
1 parent 904c1c6 commit bb262c9

8 files changed

Lines changed: 602 additions & 256 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ The plugin provides these tools to OpenCode:
8282

8383
| Tool | Description |
8484
|------|-------------|
85-
| `systematic_find_skills` | List available skills |
86-
| `systematic_find_agents` | List available agents |
87-
| `systematic_find_commands` | List available commands |
85+
| `systematic_skill` | Load Systematic bundled skills |
86+
87+
The bootstrap skill instructs OpenCode to use the native `skill` tool to load non-Systematic skills.
8888

8989
## Configuration
9090

docs/plans/2026-01-20-systematic-implementation.md

Lines changed: 14 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -719,14 +719,15 @@ const getBootstrapContent = (config: SystematicConfig, compact = false): string
719719
const configDir = path.join(homeDir, '.config/opencode')
720720

721721
const toolMapping = compact
722-
? `**Tool Mapping:** TodoWrite->update_plan, Task->@mention, Skill->systematic_use_skill
722+
? `**Tool Mapping:** TodoWrite->update_plan, Task->@mention, Skill->systematic_skill (Systematic), skill (native)
723723
724724
**Skills naming (priority order):** project: > user > sys:`
725725
: `**Tool Mapping for OpenCode:**
726726
When skills reference tools you don't have, substitute OpenCode equivalents:
727727
- \`TodoWrite\`\`update_plan\`
728728
- \`Task\` tool with subagents → Use OpenCode's subagent system (@mention)
729-
- \`Skill\` tool → \`systematic_use_skill\` custom tool
729+
- \`Skill\` tool → \`systematic_skill\` (Systematic plugin skills)
730+
- Native \`skill\` tool → non-Systematic skills
730731
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Your native tools
731732
732733
**Skills naming (priority order):**
@@ -738,7 +739,7 @@ When skills reference tools you don't have, substitute OpenCode equivalents:
738739
return `<SYSTEMATIC_WORKFLOWS>
739740
You have access to structured engineering workflows via the systematic plugin.
740741
741-
**IMPORTANT: The using-systematic skill content is included below. It is ALREADY LOADED - you are currently following it. Do NOT use systematic_use_skill to load "using-systematic" - that would be redundant. Use systematic_use_skill only for OTHER skills.**
742+
**IMPORTANT: The using-systematic skill content is included below. It is ALREADY LOADED - you are currently following it. Do NOT use systematic_skill to load "using-systematic" - that would be redundant. Use systematic_skill only for Systematic bundled skills and the native skill tool for everything else.**
742743
743744
${content}
744745
@@ -760,7 +761,7 @@ export const SystematicPlugin = async ({ client, directory }: PluginContext) =>
760761

761762
return {
762763
tool: {
763-
systematic_use_skill: tool({
764+
systematic_skill: tool({
764765
description:
765766
'Load and read a specific skill to guide your work. Skills contain proven workflows, mandatory processes, and expert techniques.',
766767
args: {
@@ -790,9 +791,9 @@ export const SystematicPlugin = async ({ client, directory }: PluginContext) =>
790791
projectSkillsDir
791792
)
792793

793-
if (!resolved) {
794-
return `Error: Skill "${skill_name}" not found.\n\nRun systematic_find_skills to see available skills.`
795-
}
794+
if (!resolved) {
795+
return `Error: Skill "${skill_name}" not found.\n\nUse the systematic_skill tool description to see available Systematic skills.`
796+
}
796797

797798
const fullContent = fs.readFileSync(resolved.skillFile, 'utf8')
798799
const { name, description } = skillsCore.extractFrontmatter(resolved.skillFile)
@@ -826,120 +827,7 @@ export const SystematicPlugin = async ({ client, directory }: PluginContext) =>
826827
},
827828
}),
828829

829-
systematic_find_skills: tool({
830-
description:
831-
'List all available skills in the project, user, and bundled skill libraries.',
832-
args: {},
833-
execute: async (): Promise<string> => {
834-
const projectSkills = skillsCore.findSkillsInDir(projectSkillsDir, 'project', 3)
835-
const userSkills = skillsCore.findSkillsInDir(userSkillsDir, 'user', 3)
836-
const bundledSkills = skillsCore.findSkillsInDir(bundledSkillsDir, 'bundled', 3)
837-
838-
// Filter disabled skills
839-
const filterDisabled = (skills: skillsCore.SkillInfo[]) =>
840-
skills.filter((s) => !config.disabled_skills.includes(s.name))
841-
842-
const allSkills = [
843-
...filterDisabled(projectSkills),
844-
...filterDisabled(userSkills),
845-
...filterDisabled(bundledSkills),
846-
]
847-
848-
if (allSkills.length === 0) {
849-
return `No skills found. Add skills to ${bundledSkillsDir}/ or ${userSkillsDir}/`
850-
}
851-
852-
let output = 'Available skills:\n\n'
853-
854-
for (const skill of allSkills) {
855-
let namespace: string
856-
switch (skill.sourceType) {
857-
case 'project':
858-
namespace = 'project:'
859-
break
860-
case 'user':
861-
namespace = ''
862-
break
863-
default:
864-
namespace = 'sys:'
865-
}
866-
867-
output += `${namespace}${skill.name}\n`
868-
if (skill.description) {
869-
output += ` ${skill.description}\n`
870-
}
871-
output += ` Directory: ${skill.path}\n\n`
872-
}
873-
874-
return output
875-
},
876-
}),
877-
878-
systematic_find_agents: tool({
879-
description: 'List all available review agents.',
880-
args: {},
881-
execute: async (): Promise<string> => {
882-
const projectAgents = skillsCore.findAgentsInDir(projectAgentsDir, 'project')
883-
const userAgents = skillsCore.findAgentsInDir(userAgentsDir, 'user')
884-
const bundledAgents = skillsCore.findAgentsInDir(bundledAgentsDir, 'bundled')
885-
886-
const seen = new Set<string>()
887-
const agents: Array<{ name: string; sourceType: string }> = []
888-
889-
for (const list of [projectAgents, userAgents, bundledAgents]) {
890-
for (const agent of list) {
891-
if (seen.has(agent.name)) continue
892-
if (config.disabled_agents.includes(agent.name)) continue
893-
seen.add(agent.name)
894-
agents.push({ name: agent.name, sourceType: agent.sourceType })
895-
}
896-
}
897-
898-
if (agents.length === 0) {
899-
return 'No agents available.'
900-
}
901-
902-
let output = 'Available agents:\n\n'
903-
for (const agent of agents.sort((a, b) => a.name.localeCompare(b.name))) {
904-
output += `- ${agent.name} (${agent.sourceType})\n`
905-
}
906-
907-
return output
908-
},
909-
}),
910-
911-
systematic_find_commands: tool({
912-
description: 'List all available commands.',
913-
args: {},
914-
execute: async (): Promise<string> => {
915-
const projectCommands = skillsCore.findCommandsInDir(projectCommandsDir, 'project')
916-
const userCommands = skillsCore.findCommandsInDir(userCommandsDir, 'user')
917-
const bundledCommands = skillsCore.findCommandsInDir(bundledCommandsDir, 'bundled')
918-
919-
const seen = new Set<string>()
920-
const commands: Array<{ name: string; sourceType: string }> = []
921-
922-
for (const list of [projectCommands, userCommands, bundledCommands]) {
923-
for (const cmd of list) {
924-
if (seen.has(cmd.name)) continue
925-
if (config.disabled_commands.includes(cmd.name)) continue
926-
seen.add(cmd.name)
927-
commands.push({ name: cmd.name, sourceType: cmd.sourceType })
928-
}
929-
}
930-
931-
if (commands.length === 0) {
932-
return 'No commands available.'
933-
}
934-
935-
let output = 'Available commands:\n\n'
936-
for (const cmd of commands.sort((a, b) => a.name.localeCompare(b.name))) {
937-
output += `- ${cmd.name} (${cmd.sourceType})\n`
938-
}
939-
940-
return output
941-
},
942-
}),
830+
// system lists provided by config hook; no find_* tools
943831
},
944832

945833
event: async () => {
@@ -1837,10 +1725,8 @@ You have access to structured engineering workflows via the systematic plugin. T
18371725

18381726
## Available Tools
18391727

1840-
- `systematic_use_skill` - Load a skill to guide your work
1841-
- `systematic_find_skills` - List all available skills
1842-
- `systematic_find_agents` - List available review agents
1843-
- `systematic_find_commands` - List available commands
1728+
- `systematic_skill` - Load Systematic bundled skills
1729+
- Native `skill` tool - Load non-Systematic skills
18441730

18451731
## Core Workflow
18461732

@@ -1883,7 +1769,7 @@ Each unit of work should make subsequent work easier:
18831769
- Build on prior knowledge
18841770
- Compound your engineering
18851771

1886-
When a skill might apply to your current task, use `systematic_use_skill` to load it. Skills provide proven workflows for common engineering tasks.
1772+
When a skill might apply to your current task, use `systematic_skill` for Systematic bundled skills or the native `skill` tool for everything else.
18871773
```
18881774

18891775
**Step 2: Commit**
@@ -1910,7 +1796,7 @@ Port these skills from Superpowers with full content:
19101796

19111797
**For each skill:**
19121798
1. Fetch the full SKILL.md content from Superpowers repo
1913-
2. Adapt tool references (Skill → systematic_use_skill, TodoWrite → update_plan)
1799+
2. Adapt tool references (Skill → systematic_skill for Systematic skills, Skill → native skill for non-Systematic, TodoWrite → update_plan)
19141800
3. Update namespace references (superpowers: → sys:)
19151801
4. Save to `skills/<name>/SKILL.md`
19161802
5. Commit individually
@@ -2022,7 +1908,7 @@ Full documentation with installation, usage, customization, and credits.
20221908
**Key Differences from Original Plan:**
20231909
- Uses `tool()` from `@opencode-ai/plugin/tool` (matches Superpowers exactly)
20241910
- Uses `experimental.chat.system.transform` for bootstrap injection (workaround for model reset issue per PR #228)
2025-
- `session.prompt()` still used for `systematic_use_skill` tool to inject skills into conversation
1911+
- `session.prompt()` still used for `systematic_skill` tool to inject skills into conversation
20261912
- Comprehensive bash test suite modeled after Superpowers
20271913
- Phase 5 explicitly requires FULL content porting, not placeholders
20281914
- Test setup creates isolated environment with fixtures

docs/plans/2026-01-20-systematic-plugin-design.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,7 @@ export const SystematicPlugin = async ({ client, directory }) => {
229229

230230
return {
231231
tool: {
232-
systematic_use_skill: tool({...}),
233-
systematic_find_skills: tool({...}),
234-
systematic_find_agents: tool({...}),
235-
systematic_find_commands: tool({...}),
232+
systematic_skill: tool({...}),
236233
},
237234

238235
// Workaround for session.prompt() model reset issue
@@ -260,10 +257,9 @@ export default SystematicPlugin
260257

261258
| Tool | Purpose |
262259
| -------------------------- | --------------------------------- |
263-
| `systematic_use_skill` | Load a skill into context |
264-
| `systematic_find_skills` | List available skills (all tiers) |
265-
| `systematic_find_agents` | List available agents |
266-
| `systematic_find_commands` | List available commands |
260+
| `systematic_skill` | Load Systematic bundled skills |
261+
262+
Use the native `skill` tool for non-Systematic skills.
267263

268264
### Bootstrap Injection
269265

skills/using-systematic/SKILL.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This is not negotiable. This is not optional. You cannot rationalize your way ou
1313

1414
## How to Access Skills
1515

16-
Use the `skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly.
16+
Use the `systematic_skill` tool for Systematic bundled skills. Use the native `skill` tool for non-Systematic skills. When you invoke a skill, its content is loaded and presented to you—follow it directly.
1717

1818
# Using Skills
1919

@@ -25,17 +25,17 @@ Use the `skill` tool. When you invoke a skill, its content is loaded and present
2525
digraph skill_flow {
2626
"User message received" [shape=doublecircle];
2727
"Might any skill apply?" [shape=diamond];
28-
"Invoke `skill` tool" [shape=box];
28+
"Invoke `systematic_skill` tool" [shape=box];
2929
"Announce: 'Using [skill] to [purpose]'" [shape=box];
3030
"Has checklist?" [shape=diamond];
3131
"Create todo per item" [shape=box];
3232
"Follow skill exactly" [shape=box];
3333
"Respond (including clarifications)" [shape=doublecircle];
3434
3535
"User message received" -> "Might any skill apply?";
36-
"Might any skill apply?" -> "Invoke `skill` tool" [label="yes, even 1%"];
36+
"Might any skill apply?" -> "Invoke `systematic_skill` tool" [label="yes, even 1%"];
3737
"Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"];
38-
"Invoke `skill` tool" -> "Announce: 'Using [skill] to [purpose]'";
38+
"Invoke `systematic_skill` tool" -> "Announce: 'Using [skill] to [purpose]'";
3939
"Announce: 'Using [skill] to [purpose]'" -> "Has checklist?";
4040
"Has checklist?" -> "Create todo per item" [label="yes"];
4141
"Has checklist?" -> "Follow skill exactly" [label="no"];
@@ -91,4 +91,4 @@ Skills are resolved in priority order:
9191
2. **User skills**: `~/.config/opencode/skills/`
9292
3. **Bundled skills**: Provided by systematic plugin
9393

94-
Use `systematic_find_skills` to see all available skills and their sources.
94+
Systematic bundled skills are listed in the `systematic_skill` tool description. Use the native `skill` tool for skills outside the Systematic plugin.

0 commit comments

Comments
 (0)