opencode-swarm-plugin 0.12.19 → 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 +25271 -20278
- package/dist/plugin.js +25257 -20278
- 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.integration.test.ts +108 -0
- package/src/agent-mail.ts +222 -4
- package/src/beads.ts +74 -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 +300 -14
package/src/swarm.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
type SpawnedAgent,
|
|
26
26
|
type Bead,
|
|
27
27
|
} from "./schemas";
|
|
28
|
-
import { mcpCall, requireState } from "./agent-mail";
|
|
28
|
+
import { mcpCall, mcpCallWithAutoInit, requireState } from "./agent-mail";
|
|
29
29
|
import {
|
|
30
30
|
OutcomeSignalsSchema,
|
|
31
31
|
DecompositionStrategySchema,
|
|
@@ -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.
|
|
@@ -1222,7 +1240,7 @@ export const swarm_plan_prompt = tool({
|
|
|
1222
1240
|
.number()
|
|
1223
1241
|
.int()
|
|
1224
1242
|
.min(2)
|
|
1225
|
-
|
|
1243
|
+
|
|
1226
1244
|
.default(5)
|
|
1227
1245
|
.describe("Maximum number of subtasks (default: 5)"),
|
|
1228
1246
|
context: tool.schema
|
|
@@ -1237,9 +1255,13 @@ export const swarm_plan_prompt = tool({
|
|
|
1237
1255
|
.number()
|
|
1238
1256
|
.int()
|
|
1239
1257
|
.min(1)
|
|
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,
|
|
@@ -1352,7 +1400,7 @@ export const swarm_decompose = tool({
|
|
|
1352
1400
|
.number()
|
|
1353
1401
|
.int()
|
|
1354
1402
|
.min(2)
|
|
1355
|
-
|
|
1403
|
+
|
|
1356
1404
|
.default(5)
|
|
1357
1405
|
.describe("Maximum number of subtasks (default: 5)"),
|
|
1358
1406
|
context: tool.schema
|
|
@@ -1367,7 +1415,7 @@ export const swarm_decompose = tool({
|
|
|
1367
1415
|
.number()
|
|
1368
1416
|
.int()
|
|
1369
1417
|
.min(1)
|
|
1370
|
-
|
|
1418
|
+
|
|
1371
1419
|
.optional()
|
|
1372
1420
|
.describe("Max CASS results to include (default: 3)"),
|
|
1373
1421
|
},
|
|
@@ -1721,9 +1769,10 @@ export const swarm_progress = tool({
|
|
|
1721
1769
|
? args.bead_id.split(".")[0]
|
|
1722
1770
|
: args.bead_id;
|
|
1723
1771
|
|
|
1724
|
-
// Send progress message to thread
|
|
1725
|
-
await
|
|
1772
|
+
// Send progress message to thread (with auto-reinit on server restart)
|
|
1773
|
+
await mcpCallWithAutoInit("send_message", {
|
|
1726
1774
|
project_key: args.project_key,
|
|
1775
|
+
agent_name: args.agent_name,
|
|
1727
1776
|
sender_name: args.agent_name,
|
|
1728
1777
|
to: [], // Coordinator will pick it up from thread
|
|
1729
1778
|
subject: `Progress: ${args.bead_id} - ${args.status}`,
|
|
@@ -1892,8 +1941,10 @@ export const swarm_broadcast = tool({
|
|
|
1892
1941
|
: "normal";
|
|
1893
1942
|
|
|
1894
1943
|
// Send as broadcast to thread (empty 'to' = all agents in thread)
|
|
1895
|
-
|
|
1944
|
+
// Uses auto-reinit wrapper to handle server restarts gracefully
|
|
1945
|
+
await mcpCallWithAutoInit("send_message", {
|
|
1896
1946
|
project_key: state.projectKey,
|
|
1947
|
+
agent_name: state.agentName,
|
|
1897
1948
|
sender_name: state.agentName,
|
|
1898
1949
|
to: [], // Broadcast to thread
|
|
1899
1950
|
subject: `[${args.importance.toUpperCase()}] Context update from ${state.agentName}`,
|
|
@@ -2020,12 +2071,16 @@ export const swarm_complete = tool({
|
|
|
2020
2071
|
}
|
|
2021
2072
|
|
|
2022
2073
|
// Release file reservations for this agent
|
|
2074
|
+
// Uses auto-reinit wrapper to handle server restarts - this was the original
|
|
2075
|
+
// failure point that prompted the self-healing implementation
|
|
2023
2076
|
try {
|
|
2024
|
-
await
|
|
2077
|
+
await mcpCallWithAutoInit("release_file_reservations", {
|
|
2025
2078
|
project_key: args.project_key,
|
|
2026
2079
|
agent_name: args.agent_name,
|
|
2027
2080
|
});
|
|
2028
2081
|
} catch (error) {
|
|
2082
|
+
// Even with auto-reinit, release might fail (e.g., no reservations existed)
|
|
2083
|
+
// This is non-fatal - log and continue
|
|
2029
2084
|
console.warn(
|
|
2030
2085
|
`[swarm] Failed to release file reservations for ${args.agent_name}:`,
|
|
2031
2086
|
error,
|
|
@@ -2053,8 +2108,9 @@ export const swarm_complete = tool({
|
|
|
2053
2108
|
.filter(Boolean)
|
|
2054
2109
|
.join("\n");
|
|
2055
2110
|
|
|
2056
|
-
await
|
|
2111
|
+
await mcpCallWithAutoInit("send_message", {
|
|
2057
2112
|
project_key: args.project_key,
|
|
2113
|
+
agent_name: args.agent_name,
|
|
2058
2114
|
sender_name: args.agent_name,
|
|
2059
2115
|
to: [], // Thread broadcast
|
|
2060
2116
|
subject: `Complete: ${args.bead_id}`,
|
|
@@ -2083,6 +2139,18 @@ export const swarm_complete = tool({
|
|
|
2083
2139
|
? "skipped"
|
|
2084
2140
|
: "no files or ubs unavailable",
|
|
2085
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"}`,
|
|
2086
2154
|
},
|
|
2087
2155
|
null,
|
|
2088
2156
|
2,
|
|
@@ -2554,6 +2622,196 @@ export const swarm_evaluation_prompt = tool({
|
|
|
2554
2622
|
},
|
|
2555
2623
|
});
|
|
2556
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
|
+
|
|
2557
2815
|
// ============================================================================
|
|
2558
2816
|
// Error Accumulator
|
|
2559
2817
|
// ============================================================================
|
|
@@ -2695,12 +2953,17 @@ export const swarm_resolve_error = tool({
|
|
|
2695
2953
|
/**
|
|
2696
2954
|
* Initialize swarm and check tool availability
|
|
2697
2955
|
*
|
|
2698
|
-
* Call this at the start of a swarm session to see what tools are available
|
|
2699
|
-
* 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/
|
|
2700
2963
|
*/
|
|
2701
2964
|
export const swarm_init = tool({
|
|
2702
2965
|
description:
|
|
2703
|
-
"Initialize swarm session
|
|
2966
|
+
"Initialize swarm session: discovers available skills, checks tool availability. ALWAYS call at swarm start.",
|
|
2704
2967
|
args: {
|
|
2705
2968
|
project_path: tool.schema
|
|
2706
2969
|
.string()
|
|
@@ -2749,6 +3012,26 @@ export const swarm_init = tool({
|
|
|
2749
3012
|
degradedFeatures.push("persistent learning (using in-memory fallback)");
|
|
2750
3013
|
}
|
|
2751
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
|
+
|
|
2752
3035
|
return JSON.stringify(
|
|
2753
3036
|
{
|
|
2754
3037
|
ready: true,
|
|
@@ -2761,10 +3044,12 @@ export const swarm_init = tool({
|
|
|
2761
3044
|
},
|
|
2762
3045
|
]),
|
|
2763
3046
|
),
|
|
3047
|
+
skills: skillsInfo,
|
|
2764
3048
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
2765
3049
|
degraded_features:
|
|
2766
3050
|
degradedFeatures.length > 0 ? degradedFeatures : undefined,
|
|
2767
3051
|
recommendations: {
|
|
3052
|
+
skills: skillsGuidance,
|
|
2768
3053
|
beads: beadsAvailable
|
|
2769
3054
|
? "✓ Use beads for all task tracking"
|
|
2770
3055
|
: "Install beads: npm i -g @joelhooks/beads",
|
|
@@ -2794,6 +3079,7 @@ export const swarmTools = {
|
|
|
2794
3079
|
swarm_progress: swarm_progress,
|
|
2795
3080
|
swarm_broadcast: swarm_broadcast,
|
|
2796
3081
|
swarm_complete: swarm_complete,
|
|
3082
|
+
swarm_learn: swarm_learn,
|
|
2797
3083
|
swarm_record_outcome: swarm_record_outcome,
|
|
2798
3084
|
swarm_subtask_prompt: swarm_subtask_prompt,
|
|
2799
3085
|
swarm_spawn_subtask: swarm_spawn_subtask,
|