opencode-swarm-plugin 0.5.0 → 0.6.1

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/dist/plugin.js CHANGED
@@ -23360,6 +23360,11 @@ var CriterionWeightSchema = exports_external.object({
23360
23360
  last_validated: exports_external.string().optional(),
23361
23361
  half_life_days: exports_external.number().positive().default(90)
23362
23362
  });
23363
+ var DecompositionStrategySchema = exports_external.enum([
23364
+ "file-based",
23365
+ "feature-based",
23366
+ "risk-based"
23367
+ ]);
23363
23368
  var OutcomeSignalsSchema = exports_external.object({
23364
23369
  bead_id: exports_external.string(),
23365
23370
  duration_ms: exports_external.number().int().min(0),
@@ -23367,7 +23372,8 @@ var OutcomeSignalsSchema = exports_external.object({
23367
23372
  retry_count: exports_external.number().int().min(0),
23368
23373
  success: exports_external.boolean(),
23369
23374
  files_touched: exports_external.array(exports_external.string()).default([]),
23370
- timestamp: exports_external.string()
23375
+ timestamp: exports_external.string(),
23376
+ strategy: DecompositionStrategySchema.optional()
23371
23377
  });
23372
23378
  var ScoredOutcomeSchema = exports_external.object({
23373
23379
  signals: OutcomeSignalsSchema,
@@ -23512,6 +23518,167 @@ function detectInstructionConflicts(subtasks) {
23512
23518
  }
23513
23519
  return conflicts;
23514
23520
  }
23521
+ var STRATEGIES = {
23522
+ "file-based": {
23523
+ name: "file-based",
23524
+ description: "Group by file type or directory. Best for refactoring, migrations, and pattern changes across codebase.",
23525
+ keywords: [
23526
+ "refactor",
23527
+ "migrate",
23528
+ "update all",
23529
+ "rename",
23530
+ "replace",
23531
+ "convert",
23532
+ "upgrade",
23533
+ "deprecate",
23534
+ "remove",
23535
+ "cleanup",
23536
+ "lint",
23537
+ "format"
23538
+ ],
23539
+ guidelines: [
23540
+ "Group files by directory or type (e.g., all components, all tests)",
23541
+ "Minimize cross-directory dependencies within a subtask",
23542
+ "Handle shared types/utilities first if they change",
23543
+ "Each subtask should be a complete transformation of its file set",
23544
+ "Consider import/export relationships when grouping"
23545
+ ],
23546
+ antiPatterns: [
23547
+ "Don't split tightly coupled files across subtasks",
23548
+ "Don't group files that have no relationship",
23549
+ "Don't forget to update imports when moving/renaming"
23550
+ ],
23551
+ examples: [
23552
+ "Migrate all components to new API \u2192 split by component directory",
23553
+ "Rename userId to accountId \u2192 split by module (types first, then consumers)",
23554
+ "Update all tests to use new matcher \u2192 split by test directory"
23555
+ ]
23556
+ },
23557
+ "feature-based": {
23558
+ name: "feature-based",
23559
+ description: "Vertical slices with UI + API + data. Best for new features and adding functionality.",
23560
+ keywords: [
23561
+ "add",
23562
+ "implement",
23563
+ "build",
23564
+ "create",
23565
+ "feature",
23566
+ "new",
23567
+ "integrate",
23568
+ "connect",
23569
+ "enable",
23570
+ "support"
23571
+ ],
23572
+ guidelines: [
23573
+ "Each subtask is a complete vertical slice (UI + logic + data)",
23574
+ "Start with data layer/types, then logic, then UI",
23575
+ "Keep related components together (form + validation + submission)",
23576
+ "Separate concerns that can be developed independently",
23577
+ "Consider user-facing features as natural boundaries"
23578
+ ],
23579
+ antiPatterns: [
23580
+ "Don't split a single feature across multiple subtasks",
23581
+ "Don't create subtasks that can't be tested independently",
23582
+ "Don't forget integration points between features"
23583
+ ],
23584
+ examples: [
23585
+ "Add user auth \u2192 [OAuth setup, Session management, Protected routes]",
23586
+ "Build dashboard \u2192 [Data fetching, Chart components, Layout/navigation]",
23587
+ "Add search \u2192 [Search API, Search UI, Results display]"
23588
+ ]
23589
+ },
23590
+ "risk-based": {
23591
+ name: "risk-based",
23592
+ description: "Isolate high-risk changes, add tests first. Best for bug fixes, security issues, and critical changes.",
23593
+ keywords: [
23594
+ "fix",
23595
+ "bug",
23596
+ "security",
23597
+ "vulnerability",
23598
+ "critical",
23599
+ "urgent",
23600
+ "hotfix",
23601
+ "patch",
23602
+ "audit",
23603
+ "review",
23604
+ "investigate"
23605
+ ],
23606
+ guidelines: [
23607
+ "Write tests FIRST to capture expected behavior",
23608
+ "Isolate the risky change to minimize blast radius",
23609
+ "Add monitoring/logging around the change",
23610
+ "Create rollback plan as part of the task",
23611
+ "Audit similar code for the same issue"
23612
+ ],
23613
+ antiPatterns: [
23614
+ "Don't make multiple risky changes in one subtask",
23615
+ "Don't skip tests for 'simple' fixes",
23616
+ "Don't forget to check for similar issues elsewhere"
23617
+ ],
23618
+ examples: [
23619
+ "Fix auth bypass \u2192 [Add regression test, Fix vulnerability, Audit similar endpoints]",
23620
+ "Fix race condition \u2192 [Add test reproducing issue, Implement fix, Add concurrency tests]",
23621
+ "Security audit \u2192 [Scan for vulnerabilities, Fix critical issues, Document remaining risks]"
23622
+ ]
23623
+ }
23624
+ };
23625
+ function selectStrategy(task) {
23626
+ const taskLower = task.toLowerCase();
23627
+ const scores = {
23628
+ "file-based": 0,
23629
+ "feature-based": 0,
23630
+ "risk-based": 0
23631
+ };
23632
+ for (const [strategyName, definition] of Object.entries(STRATEGIES)) {
23633
+ const name = strategyName;
23634
+ for (const keyword of definition.keywords) {
23635
+ if (taskLower.includes(keyword)) {
23636
+ scores[name] += 1;
23637
+ }
23638
+ }
23639
+ }
23640
+ const entries = Object.entries(scores);
23641
+ entries.sort((a, b) => b[1] - a[1]);
23642
+ const [winner, winnerScore] = entries[0];
23643
+ const [runnerUp, runnerUpScore] = entries[1] || [null, 0];
23644
+ const totalScore = entries.reduce((sum, [, score]) => sum + score, 0);
23645
+ const confidence = totalScore > 0 ? Math.min(0.95, 0.5 + (winnerScore - runnerUpScore) / totalScore) : 0.5;
23646
+ let reasoning;
23647
+ if (winnerScore === 0) {
23648
+ reasoning = `No strong keyword signals. Defaulting to feature-based as it's most versatile.`;
23649
+ } else {
23650
+ const matchedKeywords = STRATEGIES[winner].keywords.filter((k) => taskLower.includes(k));
23651
+ reasoning = `Matched keywords: ${matchedKeywords.join(", ")}. ${STRATEGIES[winner].description}`;
23652
+ }
23653
+ const finalStrategy = winnerScore === 0 ? "feature-based" : winner;
23654
+ return {
23655
+ strategy: finalStrategy,
23656
+ confidence,
23657
+ reasoning,
23658
+ alternatives: entries.filter(([s]) => s !== finalStrategy).map(([strategy, score]) => ({ strategy, score }))
23659
+ };
23660
+ }
23661
+ function formatStrategyGuidelines(strategy) {
23662
+ const def = STRATEGIES[strategy];
23663
+ const guidelines = def.guidelines.map((g) => `- ${g}`).join(`
23664
+ `);
23665
+ const antiPatterns = def.antiPatterns.map((a) => `- ${a}`).join(`
23666
+ `);
23667
+ const examples = def.examples.map((e) => `- ${e}`).join(`
23668
+ `);
23669
+ return `## Strategy: ${strategy}
23670
+
23671
+ ${def.description}
23672
+
23673
+ ### Guidelines
23674
+ ${guidelines}
23675
+
23676
+ ### Anti-Patterns (Avoid These)
23677
+ ${antiPatterns}
23678
+
23679
+ ### Examples
23680
+ ${examples}`;
23681
+ }
23515
23682
  var DECOMPOSITION_PROMPT = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
23516
23683
 
23517
23684
  ## Task
@@ -23877,6 +24044,155 @@ function formatCassHistoryForPrompt(history) {
23877
24044
  return lines.join(`
23878
24045
  `);
23879
24046
  }
24047
+ var swarm_select_strategy = tool({
24048
+ description: "Analyze task and recommend decomposition strategy (file-based, feature-based, or risk-based)",
24049
+ args: {
24050
+ task: tool.schema.string().min(1).describe("Task description to analyze"),
24051
+ codebase_context: tool.schema.string().optional().describe("Optional codebase context (file structure, tech stack, etc.)")
24052
+ },
24053
+ async execute(args) {
24054
+ const result = selectStrategy(args.task);
24055
+ let enhancedReasoning = result.reasoning;
24056
+ if (args.codebase_context) {
24057
+ enhancedReasoning += `
24058
+
24059
+ Codebase context considered: ${args.codebase_context.slice(0, 200)}...`;
24060
+ }
24061
+ return JSON.stringify({
24062
+ strategy: result.strategy,
24063
+ confidence: Math.round(result.confidence * 100) / 100,
24064
+ reasoning: enhancedReasoning,
24065
+ description: STRATEGIES[result.strategy].description,
24066
+ guidelines: STRATEGIES[result.strategy].guidelines,
24067
+ anti_patterns: STRATEGIES[result.strategy].antiPatterns,
24068
+ alternatives: result.alternatives.map((alt) => ({
24069
+ strategy: alt.strategy,
24070
+ description: STRATEGIES[alt.strategy].description,
24071
+ score: alt.score
24072
+ }))
24073
+ }, null, 2);
24074
+ }
24075
+ });
24076
+ var STRATEGY_DECOMPOSITION_PROMPT = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
24077
+
24078
+ ## Task
24079
+ {task}
24080
+
24081
+ {strategy_guidelines}
24082
+
24083
+ {context_section}
24084
+
24085
+ {cass_history}
24086
+
24087
+ ## MANDATORY: Beads Issue Tracking
24088
+
24089
+ **Every subtask MUST become a bead.** This is non-negotiable.
24090
+
24091
+ After decomposition, the coordinator will:
24092
+ 1. Create an epic bead for the overall task
24093
+ 2. Create child beads for each subtask
24094
+ 3. Track progress through bead status updates
24095
+ 4. Close beads with summaries when complete
24096
+
24097
+ Agents MUST update their bead status as they work. No silent progress.
24098
+
24099
+ ## Requirements
24100
+
24101
+ 1. **Break into 2-{max_subtasks} independent subtasks** that can run in parallel
24102
+ 2. **Assign files** - each subtask must specify which files it will modify
24103
+ 3. **No file overlap** - files cannot appear in multiple subtasks (they get exclusive locks)
24104
+ 4. **Order by dependency** - if subtask B needs subtask A's output, A must come first in the array
24105
+ 5. **Estimate complexity** - 1 (trivial) to 5 (complex)
24106
+ 6. **Plan aggressively** - break down more than you think necessary, smaller is better
24107
+
24108
+ ## Response Format
24109
+
24110
+ Respond with a JSON object matching this schema:
24111
+
24112
+ \`\`\`typescript
24113
+ {
24114
+ epic: {
24115
+ title: string, // Epic title for the beads tracker
24116
+ description?: string // Brief description of the overall goal
24117
+ },
24118
+ subtasks: [
24119
+ {
24120
+ title: string, // What this subtask accomplishes
24121
+ description?: string, // Detailed instructions for the agent
24122
+ files: string[], // Files this subtask will modify (globs allowed)
24123
+ dependencies: number[], // Indices of subtasks this depends on (0-indexed)
24124
+ estimated_complexity: 1-5 // Effort estimate
24125
+ },
24126
+ // ... more subtasks
24127
+ ]
24128
+ }
24129
+ \`\`\`
24130
+
24131
+ Now decompose the task:`;
24132
+ var swarm_plan_prompt = tool({
24133
+ description: "Generate strategy-specific decomposition prompt. Auto-selects strategy or uses provided one. Queries CASS for similar tasks.",
24134
+ args: {
24135
+ task: tool.schema.string().min(1).describe("Task description to decompose"),
24136
+ strategy: tool.schema.enum(["file-based", "feature-based", "risk-based", "auto"]).optional().describe("Decomposition strategy (default: auto-detect)"),
24137
+ max_subtasks: tool.schema.number().int().min(2).max(10).default(5).describe("Maximum number of subtasks (default: 5)"),
24138
+ context: tool.schema.string().optional().describe("Additional context (codebase info, constraints, etc.)"),
24139
+ query_cass: tool.schema.boolean().optional().describe("Query CASS for similar past tasks (default: true)"),
24140
+ cass_limit: tool.schema.number().int().min(1).max(10).optional().describe("Max CASS results to include (default: 3)")
24141
+ },
24142
+ async execute(args) {
24143
+ let selectedStrategy;
24144
+ let strategyReasoning;
24145
+ if (args.strategy && args.strategy !== "auto") {
24146
+ selectedStrategy = args.strategy;
24147
+ strategyReasoning = `User-specified strategy: ${selectedStrategy}`;
24148
+ } else {
24149
+ const selection = selectStrategy(args.task);
24150
+ selectedStrategy = selection.strategy;
24151
+ strategyReasoning = selection.reasoning;
24152
+ }
24153
+ let cassContext = "";
24154
+ let cassResult = null;
24155
+ if (args.query_cass !== false) {
24156
+ cassResult = await queryCassHistory(args.task, args.cass_limit ?? 3);
24157
+ if (cassResult && cassResult.results.length > 0) {
24158
+ cassContext = formatCassHistoryForPrompt(cassResult);
24159
+ }
24160
+ }
24161
+ const strategyGuidelines = formatStrategyGuidelines(selectedStrategy);
24162
+ const contextSection = args.context ? `## Additional Context
24163
+ ${args.context}` : `## Additional Context
24164
+ (none provided)`;
24165
+ const prompt = STRATEGY_DECOMPOSITION_PROMPT.replace("{task}", args.task).replace("{strategy_guidelines}", strategyGuidelines).replace("{context_section}", contextSection).replace("{cass_history}", cassContext || "").replace("{max_subtasks}", (args.max_subtasks ?? 5).toString());
24166
+ return JSON.stringify({
24167
+ prompt,
24168
+ strategy: {
24169
+ selected: selectedStrategy,
24170
+ reasoning: strategyReasoning,
24171
+ guidelines: STRATEGIES[selectedStrategy].guidelines,
24172
+ anti_patterns: STRATEGIES[selectedStrategy].antiPatterns
24173
+ },
24174
+ expected_schema: "BeadTree",
24175
+ schema_hint: {
24176
+ epic: { title: "string", description: "string?" },
24177
+ subtasks: [
24178
+ {
24179
+ title: "string",
24180
+ description: "string?",
24181
+ files: "string[]",
24182
+ dependencies: "number[]",
24183
+ estimated_complexity: "1-5"
24184
+ }
24185
+ ]
24186
+ },
24187
+ validation_note: "Parse agent response as JSON and validate with swarm_validate_decomposition",
24188
+ cass_history: cassResult ? {
24189
+ queried: true,
24190
+ results_found: cassResult.results.length,
24191
+ included_in_context: cassResult.results.length > 0
24192
+ } : { queried: false, reason: "disabled or unavailable" }
24193
+ }, null, 2);
24194
+ }
24195
+ });
23880
24196
  var swarm_decompose = tool({
23881
24197
  description: "Generate decomposition prompt for breaking task into parallelizable subtasks. Optionally queries CASS for similar past tasks.",
23882
24198
  args: {
@@ -24236,7 +24552,8 @@ var swarm_record_outcome = tool({
24236
24552
  retry_count: tool.schema.number().int().min(0).default(0).describe("Number of retry attempts"),
24237
24553
  success: tool.schema.boolean().describe("Whether the subtask succeeded"),
24238
24554
  files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files that were modified"),
24239
- criteria: tool.schema.array(tool.schema.string()).optional().describe("Criteria to generate feedback for (default: all default criteria)")
24555
+ criteria: tool.schema.array(tool.schema.string()).optional().describe("Criteria to generate feedback for (default: all default criteria)"),
24556
+ strategy: tool.schema.enum(["file-based", "feature-based", "risk-based"]).optional().describe("Decomposition strategy used for this task")
24240
24557
  },
24241
24558
  async execute(args) {
24242
24559
  const signals = {
@@ -24246,7 +24563,8 @@ var swarm_record_outcome = tool({
24246
24563
  retry_count: args.retry_count ?? 0,
24247
24564
  success: args.success,
24248
24565
  files_touched: args.files_touched ?? [],
24249
- timestamp: new Date().toISOString()
24566
+ timestamp: new Date().toISOString(),
24567
+ strategy: args.strategy
24250
24568
  };
24251
24569
  const validated = OutcomeSignalsSchema.parse(signals);
24252
24570
  const scored = scoreImplicitFeedback(validated, DEFAULT_LEARNING_CONFIG);
@@ -24256,7 +24574,13 @@ var swarm_record_outcome = tool({
24256
24574
  "patterns",
24257
24575
  "readable"
24258
24576
  ];
24259
- const feedbackEvents = criteriaToScore.map((criterion) => outcomeToFeedback(scored, criterion));
24577
+ const feedbackEvents = criteriaToScore.map((criterion) => {
24578
+ const event = outcomeToFeedback(scored, criterion);
24579
+ if (args.strategy) {
24580
+ event.context = `${event.context || ""} [strategy: ${args.strategy}]`.trim();
24581
+ }
24582
+ return event;
24583
+ });
24260
24584
  return JSON.stringify({
24261
24585
  success: true,
24262
24586
  outcome: {
@@ -24273,7 +24597,8 @@ var swarm_record_outcome = tool({
24273
24597
  duration_seconds: Math.round(args.duration_ms / 1000),
24274
24598
  error_count: args.error_count ?? 0,
24275
24599
  retry_count: args.retry_count ?? 0,
24276
- success: args.success
24600
+ success: args.success,
24601
+ strategy: args.strategy
24277
24602
  },
24278
24603
  note: "Feedback events should be stored for criterion weight calculation. Use learning.ts functions to apply weights."
24279
24604
  }, null, 2);
@@ -24493,6 +24818,8 @@ var swarm_init = tool({
24493
24818
  });
24494
24819
  var swarmTools = {
24495
24820
  swarm_init,
24821
+ swarm_select_strategy,
24822
+ swarm_plan_prompt,
24496
24823
  swarm_decompose,
24497
24824
  swarm_validate_decomposition,
24498
24825
  swarm_status,
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: swarm-planner
3
+ description: Strategic task decomposition for swarm coordination
4
+ model: claude-sonnet-4-5
5
+ ---
6
+
7
+ You are a swarm planner. Your job is to decompose complex tasks into optimal parallel subtasks.
8
+
9
+ ## Your Role
10
+
11
+ You analyze tasks and create decomposition plans that:
12
+
13
+ - Maximize parallelization (agents work independently)
14
+ - Minimize conflicts (no file overlap between subtasks)
15
+ - Follow the best strategy for the task type
16
+
17
+ ## Workflow
18
+
19
+ 1. **Analyze** - Call `swarm_select_strategy` to understand the task
20
+ 2. **Plan** - Call `swarm_plan_prompt` to get strategy-specific guidance
21
+ 3. **Decompose** - Create a BeadTree following the guidelines
22
+ 4. **Validate** - Ensure no file conflicts or circular dependencies
23
+
24
+ ## Strategy Selection
25
+
26
+ The plugin auto-selects strategies based on task keywords:
27
+
28
+ | Strategy | Best For | Keywords |
29
+ | ----------------- | -------------------------------------------- | -------------------------------------- |
30
+ | **file-based** | Refactoring, migrations, pattern changes | refactor, migrate, rename, update all |
31
+ | **feature-based** | New features, adding functionality | add, implement, build, create, feature |
32
+ | **risk-based** | Bug fixes, security issues, critical changes | fix, bug, security, critical, urgent |
33
+
34
+ You can override with explicit strategy if the auto-detection is wrong.
35
+
36
+ ## Output Format
37
+
38
+ Return ONLY valid JSON matching the BeadTree schema:
39
+
40
+ ```json
41
+ {
42
+ "epic": {
43
+ "title": "Epic title for beads tracker",
44
+ "description": "Brief description of the overall goal"
45
+ },
46
+ "subtasks": [
47
+ {
48
+ "title": "What this subtask accomplishes",
49
+ "description": "Detailed instructions for the agent",
50
+ "files": ["src/path/to/file.ts", "src/path/to/file.test.ts"],
51
+ "dependencies": [],
52
+ "estimated_complexity": 2
53
+ }
54
+ ]
55
+ }
56
+ ```
57
+
58
+ **CRITICAL**: Return ONLY the JSON. No markdown, no explanation, no code blocks.
59
+
60
+ ## Decomposition Rules
61
+
62
+ 1. **2-7 subtasks** - Too few = not parallel, too many = coordination overhead
63
+ 2. **No file overlap** - Each file appears in exactly one subtask
64
+ 3. **Include tests** - Put test files with the code they test
65
+ 4. **Order by dependency** - If B needs A's output, A comes first (lower index)
66
+ 5. **Estimate complexity** - 1 (trivial) to 5 (complex)
67
+
68
+ ## Anti-Patterns to Avoid
69
+
70
+ - Don't split tightly coupled files across subtasks
71
+ - Don't create subtasks that can't be tested independently
72
+ - Don't forget shared types/utilities that multiple files depend on
73
+ - Don't make one subtask do everything while others are trivial
74
+
75
+ ## Example Decomposition
76
+
77
+ **Task**: "Add user authentication with OAuth"
78
+
79
+ **Strategy**: feature-based (detected from "add" keyword)
80
+
81
+ **Result**:
82
+
83
+ ```json
84
+ {
85
+ "epic": {
86
+ "title": "Add user authentication with OAuth",
87
+ "description": "Implement OAuth-based authentication flow with session management"
88
+ },
89
+ "subtasks": [
90
+ {
91
+ "title": "Set up OAuth provider configuration",
92
+ "description": "Configure OAuth provider (Google/GitHub), add environment variables, create auth config",
93
+ "files": ["src/auth/config.ts", "src/auth/providers.ts", ".env.example"],
94
+ "dependencies": [],
95
+ "estimated_complexity": 2
96
+ },
97
+ {
98
+ "title": "Implement session management",
99
+ "description": "Create session store, JWT handling, cookie management",
100
+ "files": [
101
+ "src/auth/session.ts",
102
+ "src/auth/jwt.ts",
103
+ "src/middleware/auth.ts"
104
+ ],
105
+ "dependencies": [0],
106
+ "estimated_complexity": 3
107
+ },
108
+ {
109
+ "title": "Add protected route wrapper",
110
+ "description": "Create HOC/middleware for protecting routes, redirect logic",
111
+ "files": ["src/components/ProtectedRoute.tsx", "src/hooks/useAuth.ts"],
112
+ "dependencies": [1],
113
+ "estimated_complexity": 2
114
+ },
115
+ {
116
+ "title": "Create login/logout UI",
117
+ "description": "Login page, logout button, auth state display",
118
+ "files": ["src/app/login/page.tsx", "src/components/AuthButton.tsx"],
119
+ "dependencies": [0],
120
+ "estimated_complexity": 2
121
+ }
122
+ ]
123
+ }
124
+ ```
125
+
126
+ ## Usage
127
+
128
+ The coordinator invokes you like this:
129
+
130
+ ```
131
+ @swarm-planner "Add user authentication with OAuth"
132
+ ```
133
+
134
+ You respond with the BeadTree JSON. The coordinator then:
135
+
136
+ 1. Validates with `swarm_validate_decomposition`
137
+ 2. Creates beads with `beads_create_epic`
138
+ 3. Spawns worker agents for each subtask