opencode-swarm-plugin 0.12.20 → 0.12.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.beads/issues.jsonl +12 -1
- package/.github/workflows/ci.yml +26 -0
- package/bun.lock +21 -0
- package/dist/index.js +24951 -20070
- package/dist/plugin.js +24937 -20070
- package/examples/skills/beads-workflow/SKILL.md +165 -0
- package/examples/skills/skill-creator/SKILL.md +223 -0
- package/examples/skills/swarm-coordination/SKILL.md +148 -0
- package/global-skills/cli-builder/SKILL.md +344 -0
- package/global-skills/cli-builder/references/advanced-patterns.md +244 -0
- package/global-skills/code-review/SKILL.md +166 -0
- package/global-skills/debugging/SKILL.md +150 -0
- package/global-skills/skill-creator/LICENSE.txt +202 -0
- package/global-skills/skill-creator/SKILL.md +352 -0
- package/global-skills/skill-creator/references/output-patterns.md +82 -0
- package/global-skills/skill-creator/references/workflows.md +28 -0
- package/global-skills/swarm-coordination/SKILL.md +166 -0
- package/package.json +6 -5
- package/scripts/init-skill.ts +222 -0
- package/scripts/validate-skill.ts +204 -0
- package/src/agent-mail.ts +40 -4
- package/src/beads.ts +24 -0
- package/src/index.ts +49 -0
- package/src/schemas/bead.ts +21 -1
- package/src/skills.test.ts +408 -0
- package/src/skills.ts +1364 -0
- package/src/swarm.ts +282 -4
package/src/swarm.ts
CHANGED
|
@@ -48,6 +48,11 @@ import {
|
|
|
48
48
|
formatToolAvailability,
|
|
49
49
|
type ToolName,
|
|
50
50
|
} from "./tool-availability";
|
|
51
|
+
import {
|
|
52
|
+
getSkillsContextForSwarm,
|
|
53
|
+
findRelevantSkills,
|
|
54
|
+
listSkills,
|
|
55
|
+
} from "./skills";
|
|
51
56
|
|
|
52
57
|
// ============================================================================
|
|
53
58
|
// Conflict Detection
|
|
@@ -708,16 +713,27 @@ Only modify these files. Need others? Message the coordinator.
|
|
|
708
713
|
### Agent Mail
|
|
709
714
|
- agentmail_send (thread_id: {epic_id})
|
|
710
715
|
|
|
716
|
+
### Skills (if available)
|
|
717
|
+
- skills_list (discover available skills)
|
|
718
|
+
- skills_use (activate a skill for specialized guidance)
|
|
719
|
+
|
|
711
720
|
### Completion
|
|
712
721
|
- swarm_complete (REQUIRED when done)
|
|
713
722
|
|
|
723
|
+
## [LEARNING]
|
|
724
|
+
As you work, note reusable patterns, best practices, or domain insights:
|
|
725
|
+
- If you discover something that would help future agents, consider creating a skill
|
|
726
|
+
- Use skills_create to codify patterns for the project
|
|
727
|
+
- Good skills have clear "when to use" descriptions with actionable instructions
|
|
728
|
+
- Skills make swarms smarter over time
|
|
729
|
+
|
|
714
730
|
## [OUTPUT]
|
|
715
731
|
1. Read files first
|
|
716
732
|
2. Implement changes
|
|
717
733
|
3. Verify (typecheck)
|
|
718
734
|
4. Complete with swarm_complete
|
|
719
735
|
|
|
720
|
-
Return: Summary of changes made
|
|
736
|
+
Return: Summary of changes made. Note any patterns worth preserving as skills.
|
|
721
737
|
|
|
722
738
|
**Never work silently.** Communicate progress and blockers immediately.
|
|
723
739
|
|
|
@@ -1157,6 +1173,8 @@ const STRATEGY_DECOMPOSITION_PROMPT = `You are decomposing a task into paralleli
|
|
|
1157
1173
|
|
|
1158
1174
|
{cass_history}
|
|
1159
1175
|
|
|
1176
|
+
{skills_context}
|
|
1177
|
+
|
|
1160
1178
|
## MANDATORY: Beads Issue Tracking
|
|
1161
1179
|
|
|
1162
1180
|
**Every subtask MUST become a bead.** This is non-negotiable.
|
|
@@ -1240,6 +1258,10 @@ export const swarm_plan_prompt = tool({
|
|
|
1240
1258
|
|
|
1241
1259
|
.optional()
|
|
1242
1260
|
.describe("Max CASS results to include (default: 3)"),
|
|
1261
|
+
include_skills: tool.schema
|
|
1262
|
+
.boolean()
|
|
1263
|
+
.optional()
|
|
1264
|
+
.describe("Include available skills in context (default: true)"),
|
|
1243
1265
|
},
|
|
1244
1266
|
async execute(args) {
|
|
1245
1267
|
// Select strategy
|
|
@@ -1288,6 +1310,30 @@ export const swarm_plan_prompt = tool({
|
|
|
1288
1310
|
cassResultInfo = { queried: false, reason: "disabled" };
|
|
1289
1311
|
}
|
|
1290
1312
|
|
|
1313
|
+
// Fetch skills context
|
|
1314
|
+
let skillsContext = "";
|
|
1315
|
+
let skillsInfo: { included: boolean; count?: number; relevant?: string[] } = {
|
|
1316
|
+
included: false,
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
if (args.include_skills !== false) {
|
|
1320
|
+
const allSkills = await listSkills();
|
|
1321
|
+
if (allSkills.length > 0) {
|
|
1322
|
+
skillsContext = await getSkillsContextForSwarm();
|
|
1323
|
+
const relevantSkills = await findRelevantSkills(args.task);
|
|
1324
|
+
skillsInfo = {
|
|
1325
|
+
included: true,
|
|
1326
|
+
count: allSkills.length,
|
|
1327
|
+
relevant: relevantSkills,
|
|
1328
|
+
};
|
|
1329
|
+
|
|
1330
|
+
// Add suggestion for relevant skills
|
|
1331
|
+
if (relevantSkills.length > 0) {
|
|
1332
|
+
skillsContext += `\n\n**Suggested skills for this task**: ${relevantSkills.join(", ")}`;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1291
1337
|
// Format strategy guidelines
|
|
1292
1338
|
const strategyGuidelines = formatStrategyGuidelines(selectedStrategy);
|
|
1293
1339
|
|
|
@@ -1301,6 +1347,7 @@ export const swarm_plan_prompt = tool({
|
|
|
1301
1347
|
.replace("{strategy_guidelines}", strategyGuidelines)
|
|
1302
1348
|
.replace("{context_section}", contextSection)
|
|
1303
1349
|
.replace("{cass_history}", cassContext || "")
|
|
1350
|
+
.replace("{skills_context}", skillsContext || "")
|
|
1304
1351
|
.replace("{max_subtasks}", (args.max_subtasks ?? 5).toString());
|
|
1305
1352
|
|
|
1306
1353
|
return JSON.stringify(
|
|
@@ -1328,6 +1375,7 @@ export const swarm_plan_prompt = tool({
|
|
|
1328
1375
|
validation_note:
|
|
1329
1376
|
"Parse agent response as JSON and validate with swarm_validate_decomposition",
|
|
1330
1377
|
cass_history: cassResultInfo,
|
|
1378
|
+
skills: skillsInfo,
|
|
1331
1379
|
},
|
|
1332
1380
|
null,
|
|
1333
1381
|
2,
|
|
@@ -2091,6 +2139,18 @@ export const swarm_complete = tool({
|
|
|
2091
2139
|
? "skipped"
|
|
2092
2140
|
: "no files or ubs unavailable",
|
|
2093
2141
|
},
|
|
2142
|
+
learning_prompt: `## Reflection
|
|
2143
|
+
|
|
2144
|
+
Did you learn anything reusable during this subtask? Consider:
|
|
2145
|
+
|
|
2146
|
+
1. **Patterns**: Any code patterns or approaches that worked well?
|
|
2147
|
+
2. **Gotchas**: Edge cases or pitfalls to warn future agents about?
|
|
2148
|
+
3. **Best Practices**: Domain-specific guidelines worth documenting?
|
|
2149
|
+
4. **Tool Usage**: Effective ways to use tools for this type of task?
|
|
2150
|
+
|
|
2151
|
+
If you discovered something valuable, use \`swarm_learn\` or \`skills_create\` to preserve it as a skill for future swarms.
|
|
2152
|
+
|
|
2153
|
+
Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
|
|
2094
2154
|
},
|
|
2095
2155
|
null,
|
|
2096
2156
|
2,
|
|
@@ -2562,6 +2622,196 @@ export const swarm_evaluation_prompt = tool({
|
|
|
2562
2622
|
},
|
|
2563
2623
|
});
|
|
2564
2624
|
|
|
2625
|
+
// ============================================================================
|
|
2626
|
+
// Swarm Learning
|
|
2627
|
+
// ============================================================================
|
|
2628
|
+
|
|
2629
|
+
/**
|
|
2630
|
+
* Learn from completed work and optionally create a skill
|
|
2631
|
+
*
|
|
2632
|
+
* This tool helps agents reflect on patterns, best practices, or domain
|
|
2633
|
+
* knowledge discovered during task execution and codify them into reusable
|
|
2634
|
+
* skills for future swarms.
|
|
2635
|
+
*
|
|
2636
|
+
* Implements the "learning swarm" pattern where swarms get smarter over time.
|
|
2637
|
+
*/
|
|
2638
|
+
export const swarm_learn = tool({
|
|
2639
|
+
description: `Analyze completed work and optionally create a skill from learned patterns.
|
|
2640
|
+
|
|
2641
|
+
Use after completing a subtask when you've discovered:
|
|
2642
|
+
- Reusable code patterns or approaches
|
|
2643
|
+
- Domain-specific best practices
|
|
2644
|
+
- Gotchas or edge cases to warn about
|
|
2645
|
+
- Effective tool usage patterns
|
|
2646
|
+
|
|
2647
|
+
This tool helps you formalize learnings into a skill that future agents can discover and use.`,
|
|
2648
|
+
args: {
|
|
2649
|
+
summary: tool.schema
|
|
2650
|
+
.string()
|
|
2651
|
+
.describe("Brief summary of what was learned (1-2 sentences)"),
|
|
2652
|
+
pattern_type: tool.schema
|
|
2653
|
+
.enum(["code-pattern", "best-practice", "gotcha", "tool-usage", "domain-knowledge", "workflow"])
|
|
2654
|
+
.describe("Category of the learning"),
|
|
2655
|
+
details: tool.schema
|
|
2656
|
+
.string()
|
|
2657
|
+
.describe("Detailed explanation of the pattern or practice"),
|
|
2658
|
+
example: tool.schema
|
|
2659
|
+
.string()
|
|
2660
|
+
.optional()
|
|
2661
|
+
.describe("Code example or concrete illustration"),
|
|
2662
|
+
when_to_use: tool.schema
|
|
2663
|
+
.string()
|
|
2664
|
+
.describe("When should an agent apply this knowledge?"),
|
|
2665
|
+
files_context: tool.schema
|
|
2666
|
+
.array(tool.schema.string())
|
|
2667
|
+
.optional()
|
|
2668
|
+
.describe("Files that exemplify this pattern"),
|
|
2669
|
+
create_skill: tool.schema
|
|
2670
|
+
.boolean()
|
|
2671
|
+
.optional()
|
|
2672
|
+
.describe("Create a skill from this learning (default: false, just document)"),
|
|
2673
|
+
skill_name: tool.schema
|
|
2674
|
+
.string()
|
|
2675
|
+
.regex(/^[a-z0-9-]+$/)
|
|
2676
|
+
.max(64)
|
|
2677
|
+
.optional()
|
|
2678
|
+
.describe("Skill name if creating (required if create_skill=true)"),
|
|
2679
|
+
skill_tags: tool.schema
|
|
2680
|
+
.array(tool.schema.string())
|
|
2681
|
+
.optional()
|
|
2682
|
+
.describe("Tags for the skill if creating"),
|
|
2683
|
+
},
|
|
2684
|
+
async execute(args) {
|
|
2685
|
+
// Format the learning as structured documentation
|
|
2686
|
+
const learning = {
|
|
2687
|
+
summary: args.summary,
|
|
2688
|
+
type: args.pattern_type,
|
|
2689
|
+
details: args.details,
|
|
2690
|
+
example: args.example,
|
|
2691
|
+
when_to_use: args.when_to_use,
|
|
2692
|
+
files_context: args.files_context,
|
|
2693
|
+
recorded_at: new Date().toISOString(),
|
|
2694
|
+
};
|
|
2695
|
+
|
|
2696
|
+
// If creating a skill, generate and create it
|
|
2697
|
+
if (args.create_skill) {
|
|
2698
|
+
if (!args.skill_name) {
|
|
2699
|
+
return JSON.stringify(
|
|
2700
|
+
{
|
|
2701
|
+
success: false,
|
|
2702
|
+
error: "skill_name is required when create_skill=true",
|
|
2703
|
+
learning: learning,
|
|
2704
|
+
},
|
|
2705
|
+
null,
|
|
2706
|
+
2,
|
|
2707
|
+
);
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
// Build skill body from learning
|
|
2711
|
+
const skillBody = `# ${args.summary}
|
|
2712
|
+
|
|
2713
|
+
## When to Use
|
|
2714
|
+
${args.when_to_use}
|
|
2715
|
+
|
|
2716
|
+
## ${args.pattern_type.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())}
|
|
2717
|
+
|
|
2718
|
+
${args.details}
|
|
2719
|
+
|
|
2720
|
+
${args.example ? `## Example\n\n\`\`\`\n${args.example}\n\`\`\`\n` : ""}
|
|
2721
|
+
${args.files_context && args.files_context.length > 0 ? `## Reference Files\n\n${args.files_context.map((f) => `- \`${f}\``).join("\n")}\n` : ""}
|
|
2722
|
+
|
|
2723
|
+
---
|
|
2724
|
+
*Learned from swarm execution on ${new Date().toISOString().split("T")[0]}*`;
|
|
2725
|
+
|
|
2726
|
+
// Import skills_create functionality
|
|
2727
|
+
const { getSkill, invalidateSkillsCache } = await import("./skills");
|
|
2728
|
+
const { mkdir, writeFile } = await import("fs/promises");
|
|
2729
|
+
const { join } = await import("path");
|
|
2730
|
+
|
|
2731
|
+
// Check if skill exists
|
|
2732
|
+
const existing = await getSkill(args.skill_name);
|
|
2733
|
+
if (existing) {
|
|
2734
|
+
return JSON.stringify(
|
|
2735
|
+
{
|
|
2736
|
+
success: false,
|
|
2737
|
+
error: `Skill '${args.skill_name}' already exists`,
|
|
2738
|
+
existing_path: existing.path,
|
|
2739
|
+
learning: learning,
|
|
2740
|
+
suggestion: "Use skills_update to add to existing skill, or choose a different name",
|
|
2741
|
+
},
|
|
2742
|
+
null,
|
|
2743
|
+
2,
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
// Create skill directory and file
|
|
2748
|
+
const skillDir = join(process.cwd(), ".opencode", "skills", args.skill_name);
|
|
2749
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
2750
|
+
|
|
2751
|
+
const frontmatter = [
|
|
2752
|
+
"---",
|
|
2753
|
+
`name: ${args.skill_name}`,
|
|
2754
|
+
`description: ${args.when_to_use.slice(0, 200)}${args.when_to_use.length > 200 ? "..." : ""}`,
|
|
2755
|
+
"tags:",
|
|
2756
|
+
` - ${args.pattern_type}`,
|
|
2757
|
+
` - learned`,
|
|
2758
|
+
...(args.skill_tags || []).map((t) => ` - ${t}`),
|
|
2759
|
+
"---",
|
|
2760
|
+
].join("\n");
|
|
2761
|
+
|
|
2762
|
+
try {
|
|
2763
|
+
await mkdir(skillDir, { recursive: true });
|
|
2764
|
+
await writeFile(skillPath, `${frontmatter}\n\n${skillBody}`, "utf-8");
|
|
2765
|
+
invalidateSkillsCache();
|
|
2766
|
+
|
|
2767
|
+
return JSON.stringify(
|
|
2768
|
+
{
|
|
2769
|
+
success: true,
|
|
2770
|
+
skill_created: true,
|
|
2771
|
+
skill: {
|
|
2772
|
+
name: args.skill_name,
|
|
2773
|
+
path: skillPath,
|
|
2774
|
+
type: args.pattern_type,
|
|
2775
|
+
},
|
|
2776
|
+
learning: learning,
|
|
2777
|
+
message: `Created skill '${args.skill_name}' from learned pattern. Future agents can discover it with skills_list.`,
|
|
2778
|
+
},
|
|
2779
|
+
null,
|
|
2780
|
+
2,
|
|
2781
|
+
);
|
|
2782
|
+
} catch (error) {
|
|
2783
|
+
return JSON.stringify(
|
|
2784
|
+
{
|
|
2785
|
+
success: false,
|
|
2786
|
+
error: `Failed to create skill: ${error instanceof Error ? error.message : String(error)}`,
|
|
2787
|
+
learning: learning,
|
|
2788
|
+
},
|
|
2789
|
+
null,
|
|
2790
|
+
2,
|
|
2791
|
+
);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
// Just document the learning without creating a skill
|
|
2796
|
+
return JSON.stringify(
|
|
2797
|
+
{
|
|
2798
|
+
success: true,
|
|
2799
|
+
skill_created: false,
|
|
2800
|
+
learning: learning,
|
|
2801
|
+
message: "Learning documented. Use create_skill=true to persist as a skill for future agents.",
|
|
2802
|
+
suggested_skill_name: args.skill_name ||
|
|
2803
|
+
args.summary
|
|
2804
|
+
.toLowerCase()
|
|
2805
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
2806
|
+
.replace(/\s+/g, "-")
|
|
2807
|
+
.slice(0, 64),
|
|
2808
|
+
},
|
|
2809
|
+
null,
|
|
2810
|
+
2,
|
|
2811
|
+
);
|
|
2812
|
+
},
|
|
2813
|
+
});
|
|
2814
|
+
|
|
2565
2815
|
// ============================================================================
|
|
2566
2816
|
// Error Accumulator
|
|
2567
2817
|
// ============================================================================
|
|
@@ -2703,12 +2953,17 @@ export const swarm_resolve_error = tool({
|
|
|
2703
2953
|
/**
|
|
2704
2954
|
* Initialize swarm and check tool availability
|
|
2705
2955
|
*
|
|
2706
|
-
* Call this at the start of a swarm session to see what tools are available
|
|
2707
|
-
* and what features will be degraded.
|
|
2956
|
+
* Call this at the start of a swarm session to see what tools are available,
|
|
2957
|
+
* what skills exist in the project, and what features will be degraded.
|
|
2958
|
+
*
|
|
2959
|
+
* Skills are automatically discovered from:
|
|
2960
|
+
* - .opencode/skills/
|
|
2961
|
+
* - .claude/skills/
|
|
2962
|
+
* - skills/
|
|
2708
2963
|
*/
|
|
2709
2964
|
export const swarm_init = tool({
|
|
2710
2965
|
description:
|
|
2711
|
-
"Initialize swarm session
|
|
2966
|
+
"Initialize swarm session: discovers available skills, checks tool availability. ALWAYS call at swarm start.",
|
|
2712
2967
|
args: {
|
|
2713
2968
|
project_path: tool.schema
|
|
2714
2969
|
.string()
|
|
@@ -2757,6 +3012,26 @@ export const swarm_init = tool({
|
|
|
2757
3012
|
degradedFeatures.push("persistent learning (using in-memory fallback)");
|
|
2758
3013
|
}
|
|
2759
3014
|
|
|
3015
|
+
// Discover available skills
|
|
3016
|
+
const availableSkills = await listSkills();
|
|
3017
|
+
const skillsInfo = {
|
|
3018
|
+
count: availableSkills.length,
|
|
3019
|
+
available: availableSkills.length > 0,
|
|
3020
|
+
skills: availableSkills.map((s) => ({
|
|
3021
|
+
name: s.name,
|
|
3022
|
+
description: s.description,
|
|
3023
|
+
hasScripts: s.hasScripts,
|
|
3024
|
+
})),
|
|
3025
|
+
};
|
|
3026
|
+
|
|
3027
|
+
// Add skills guidance if available
|
|
3028
|
+
let skillsGuidance: string | undefined;
|
|
3029
|
+
if (availableSkills.length > 0) {
|
|
3030
|
+
skillsGuidance = `Found ${availableSkills.length} skill(s). Use skills_list to see details, skills_use to activate.`;
|
|
3031
|
+
} else {
|
|
3032
|
+
skillsGuidance = "No skills found. Add skills to .opencode/skills/ or .claude/skills/ for specialized guidance.";
|
|
3033
|
+
}
|
|
3034
|
+
|
|
2760
3035
|
return JSON.stringify(
|
|
2761
3036
|
{
|
|
2762
3037
|
ready: true,
|
|
@@ -2769,10 +3044,12 @@ export const swarm_init = tool({
|
|
|
2769
3044
|
},
|
|
2770
3045
|
]),
|
|
2771
3046
|
),
|
|
3047
|
+
skills: skillsInfo,
|
|
2772
3048
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
2773
3049
|
degraded_features:
|
|
2774
3050
|
degradedFeatures.length > 0 ? degradedFeatures : undefined,
|
|
2775
3051
|
recommendations: {
|
|
3052
|
+
skills: skillsGuidance,
|
|
2776
3053
|
beads: beadsAvailable
|
|
2777
3054
|
? "✓ Use beads for all task tracking"
|
|
2778
3055
|
: "Install beads: npm i -g @joelhooks/beads",
|
|
@@ -2802,6 +3079,7 @@ export const swarmTools = {
|
|
|
2802
3079
|
swarm_progress: swarm_progress,
|
|
2803
3080
|
swarm_broadcast: swarm_broadcast,
|
|
2804
3081
|
swarm_complete: swarm_complete,
|
|
3082
|
+
swarm_learn: swarm_learn,
|
|
2805
3083
|
swarm_record_outcome: swarm_record_outcome,
|
|
2806
3084
|
swarm_subtask_prompt: swarm_subtask_prompt,
|
|
2807
3085
|
swarm_spawn_subtask: swarm_spawn_subtask,
|