opencodekit 0.16.4 → 0.16.6

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.
Files changed (56) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/template/.opencode/AGENTS.md +106 -384
  3. package/dist/template/.opencode/README.md +170 -104
  4. package/dist/template/.opencode/agent/build.md +39 -32
  5. package/dist/template/.opencode/agent/explore.md +2 -0
  6. package/dist/template/.opencode/agent/review.md +3 -0
  7. package/dist/template/.opencode/agent/scout.md +22 -11
  8. package/dist/template/.opencode/command/create.md +164 -106
  9. package/dist/template/.opencode/command/design.md +5 -1
  10. package/dist/template/.opencode/command/handoff.md +6 -4
  11. package/dist/template/.opencode/command/init.md +1 -1
  12. package/dist/template/.opencode/command/plan.md +26 -23
  13. package/dist/template/.opencode/command/research.md +13 -6
  14. package/dist/template/.opencode/command/resume.md +8 -6
  15. package/dist/template/.opencode/command/ship.md +1 -1
  16. package/dist/template/.opencode/command/start.md +30 -25
  17. package/dist/template/.opencode/command/status.md +9 -42
  18. package/dist/template/.opencode/command/verify.md +11 -11
  19. package/dist/template/.opencode/memory/README.md +67 -37
  20. package/dist/template/.opencode/memory/_templates/prd.md +102 -18
  21. package/dist/template/.opencode/memory/project/gotchas.md +31 -0
  22. package/dist/template/.opencode/memory.db +0 -0
  23. package/dist/template/.opencode/memory.db-shm +0 -0
  24. package/dist/template/.opencode/memory.db-wal +0 -0
  25. package/dist/template/.opencode/opencode.json +0 -10
  26. package/dist/template/.opencode/package.json +1 -1
  27. package/dist/template/.opencode/skill/beads/SKILL.md +164 -380
  28. package/dist/template/.opencode/skill/beads/references/BOUNDARIES.md +23 -22
  29. package/dist/template/.opencode/skill/beads/references/DEPENDENCIES.md +23 -29
  30. package/dist/template/.opencode/skill/beads/references/RESUMABILITY.md +5 -8
  31. package/dist/template/.opencode/skill/beads/references/WORKFLOWS.md +43 -39
  32. package/dist/template/.opencode/skill/beads-bridge/SKILL.md +80 -53
  33. package/dist/template/.opencode/skill/brainstorming/SKILL.md +19 -5
  34. package/dist/template/.opencode/skill/context-engineering/SKILL.md +30 -63
  35. package/dist/template/.opencode/skill/context-management/SKILL.md +115 -0
  36. package/dist/template/.opencode/skill/deep-research/SKILL.md +4 -4
  37. package/dist/template/.opencode/skill/development-lifecycle/SKILL.md +305 -0
  38. package/dist/template/.opencode/skill/memory-system/SKILL.md +3 -3
  39. package/dist/template/.opencode/skill/prd/SKILL.md +47 -122
  40. package/dist/template/.opencode/skill/prd-task/SKILL.md +48 -4
  41. package/dist/template/.opencode/skill/prd-task/references/prd-schema.json +120 -24
  42. package/dist/template/.opencode/skill/swarm-coordination/SKILL.md +79 -61
  43. package/dist/template/.opencode/skill/tool-priority/SKILL.md +31 -22
  44. package/dist/template/.opencode/tool/context7.ts +183 -0
  45. package/dist/template/.opencode/tool/memory-admin.ts +445 -0
  46. package/dist/template/.opencode/tool/swarm.ts +572 -0
  47. package/package.json +1 -1
  48. package/dist/template/.opencode/memory/_templates/spec.md +0 -66
  49. package/dist/template/.opencode/tool/beads-sync.ts +0 -657
  50. package/dist/template/.opencode/tool/context7-query-docs.ts +0 -89
  51. package/dist/template/.opencode/tool/context7-resolve-library-id.ts +0 -113
  52. package/dist/template/.opencode/tool/memory-maintain.ts +0 -167
  53. package/dist/template/.opencode/tool/memory-migrate.ts +0 -319
  54. package/dist/template/.opencode/tool/swarm-delegate.ts +0 -180
  55. package/dist/template/.opencode/tool/swarm-monitor.ts +0 -388
  56. package/dist/template/.opencode/tool/swarm-plan.ts +0 -697
@@ -0,0 +1,572 @@
1
+ import { exec } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { tool } from "@opencode-ai/plugin";
6
+
7
+ const execAsync = promisify(exec);
8
+
9
+ /**
10
+ * Unified swarm orchestration tool.
11
+ * Consolidates: swarm-plan, swarm-monitor, swarm-delegate, beads-sync
12
+ */
13
+ export default tool({
14
+ description: `Swarm orchestration for parallel task execution.
15
+
16
+ Operations:
17
+ - plan: Analyze task for parallel execution
18
+ - monitor: Track worker progress
19
+ - delegate: Create delegation packet
20
+ - sync: Bridge Beads tasks to OpenCode todos
21
+
22
+ Usage:
23
+ swarm({ op: "plan", task: "..." })
24
+ swarm({ op: "monitor", team: "...", action: "status" })
25
+ swarm({ op: "delegate", bead_id: "...", outcome: "..." })
26
+ swarm({ op: "sync", action: "push" })`,
27
+
28
+ args: {
29
+ op: tool.schema
30
+ .enum(["plan", "monitor", "delegate", "sync"])
31
+ .describe("Operation: plan, monitor, delegate, sync"),
32
+ // Plan args
33
+ task: tool.schema.string().optional().describe("Task description (plan)"),
34
+ files: tool.schema
35
+ .string()
36
+ .optional()
37
+ .describe("Comma-separated files (plan)"),
38
+ // Monitor args
39
+ team: tool.schema.string().optional().describe("Team name (monitor)"),
40
+ action: tool.schema
41
+ .string()
42
+ .optional()
43
+ .describe(
44
+ "Monitor action: update, render, status, clear | Sync action: push, pull",
45
+ ),
46
+ worker_id: tool.schema.string().optional().describe("Worker ID (monitor)"),
47
+ phase: tool.schema.string().optional().describe("Phase name (monitor)"),
48
+ progress: tool.schema
49
+ .number()
50
+ .min(0)
51
+ .max(100)
52
+ .optional()
53
+ .describe("Progress 0-100 (monitor)"),
54
+ status: tool.schema.string().optional().describe("Worker status (monitor)"),
55
+ file: tool.schema.string().optional().describe("Current file (monitor)"),
56
+ // Delegate args
57
+ bead_id: tool.schema.string().optional().describe("Bead ID (delegate)"),
58
+ title: tool.schema.string().optional().describe("Task title (delegate)"),
59
+ outcome: tool.schema
60
+ .string()
61
+ .optional()
62
+ .describe("Expected outcome (delegate)"),
63
+ must_do: tool.schema
64
+ .string()
65
+ .optional()
66
+ .describe("Must do list (delegate)"),
67
+ must_not: tool.schema
68
+ .string()
69
+ .optional()
70
+ .describe("Must not do list (delegate)"),
71
+ checks: tool.schema
72
+ .string()
73
+ .optional()
74
+ .describe("Acceptance checks (delegate)"),
75
+ context: tool.schema
76
+ .string()
77
+ .optional()
78
+ .describe("Extra context (delegate)"),
79
+ write: tool.schema
80
+ .boolean()
81
+ .optional()
82
+ .describe("Write to file (delegate)"),
83
+ // Sync args
84
+ filter: tool.schema
85
+ .string()
86
+ .optional()
87
+ .describe("Filter: open, in_progress, all (sync)"),
88
+ },
89
+
90
+ execute: async (args, ctx) => {
91
+ const worktree = ctx.worktree || process.cwd();
92
+
93
+ switch (args.op) {
94
+ case "plan":
95
+ return planOperation(args.task || "", args.files);
96
+ case "monitor":
97
+ return monitorOperation(args, worktree);
98
+ case "delegate":
99
+ return delegateOperation(args);
100
+ case "sync":
101
+ return syncOperation(args, worktree);
102
+ default:
103
+ return `Error: Unknown operation: ${args.op}`;
104
+ }
105
+ },
106
+ });
107
+
108
+ // ============================================================
109
+ // PLAN OPERATION (from swarm-plan.ts)
110
+ // ============================================================
111
+
112
+ interface TaskClassification {
113
+ type: "search" | "batch" | "writing" | "sequential" | "mixed";
114
+ coupling: "high" | "medium" | "low";
115
+ recommended_agents: number;
116
+ reasoning: string;
117
+ }
118
+
119
+ function planOperation(task: string, files?: string): string {
120
+ const fileList = files?.split(",").filter(Boolean) || [];
121
+ const fileCount = Number.parseInt(files || "0") || fileList.length;
122
+
123
+ const classification = classifyTask(task, fileList);
124
+ const collapseCheck = detectSerialCollapse(
125
+ task,
126
+ fileCount,
127
+ classification.recommended_agents,
128
+ );
129
+
130
+ let recommendation: string;
131
+ if (collapseCheck.is_collapse) {
132
+ recommendation = `Swarm: ${Math.min(fileCount, 5)} agents (serial collapse detected)`;
133
+ } else if (classification.recommended_agents > 1) {
134
+ recommendation = `Swarm: ${classification.recommended_agents} agents`;
135
+ } else {
136
+ recommendation = "Single agent sufficient";
137
+ }
138
+
139
+ return JSON.stringify(
140
+ {
141
+ task: task.slice(0, 100),
142
+ file_count: fileCount,
143
+ classification,
144
+ serial_collapse: collapseCheck,
145
+ recommendation,
146
+ },
147
+ null,
148
+ 2,
149
+ );
150
+ }
151
+
152
+ function classifyTask(task: string, files: string[]): TaskClassification {
153
+ const searchPatterns = /research|find|search|explore|investigate/i;
154
+ const batchPatterns = /refactor|update|migrate|convert.*all|batch/i;
155
+ const sequentialPatterns = /debug|fix.*issue|optimize|complex/i;
156
+
157
+ const coupling = analyzeCoupling(files);
158
+
159
+ if (searchPatterns.test(task)) {
160
+ return {
161
+ type: "search",
162
+ coupling: "low",
163
+ recommended_agents: Math.min(Math.max(files.length, 3), 5),
164
+ reasoning: "Search tasks benefit from parallel exploration",
165
+ };
166
+ }
167
+
168
+ if ((batchPatterns.test(task) || files.length > 3) && files.length > 0) {
169
+ return {
170
+ type: "batch",
171
+ coupling,
172
+ recommended_agents: Math.min(files.length, 8),
173
+ reasoning: `Batch processing ${files.length} files`,
174
+ };
175
+ }
176
+
177
+ if (
178
+ sequentialPatterns.test(task) ||
179
+ coupling === "high" ||
180
+ files.length <= 2
181
+ ) {
182
+ return {
183
+ type: "sequential",
184
+ coupling: "high",
185
+ recommended_agents: 1,
186
+ reasoning: "High coupling requires sequential execution",
187
+ };
188
+ }
189
+
190
+ return {
191
+ type: "mixed",
192
+ coupling,
193
+ recommended_agents: Math.min(files.length || 2, 4),
194
+ reasoning: "Mixed approach with verification",
195
+ };
196
+ }
197
+
198
+ function analyzeCoupling(files: string[]): "high" | "medium" | "low" {
199
+ if (files.length <= 1) return "high";
200
+ if (files.length <= 3) return "medium";
201
+ const dirs = files.map((f) => path.dirname(f));
202
+ const uniqueDirs = new Set(dirs);
203
+ if (uniqueDirs.size === 1) return "high";
204
+ if (uniqueDirs.size <= files.length / 2) return "medium";
205
+ return "low";
206
+ }
207
+
208
+ function detectSerialCollapse(
209
+ task: string,
210
+ fileCount: number,
211
+ agents: number,
212
+ ): { is_collapse: boolean; warnings: string[] } {
213
+ const warnings: string[] = [];
214
+ if (fileCount >= 5 && agents === 1) warnings.push("Many files, single agent");
215
+ if (/research|search/i.test(task) && agents === 1)
216
+ warnings.push("Search with single agent");
217
+ if (/refactor.*all|update.*all/i.test(task) && agents === 1)
218
+ warnings.push("Batch with single agent");
219
+
220
+ return {
221
+ is_collapse:
222
+ warnings.length >= 2 || (warnings.length === 1 && fileCount > 8),
223
+ warnings,
224
+ };
225
+ }
226
+
227
+ // ============================================================
228
+ // MONITOR OPERATION (from swarm-monitor.ts)
229
+ // ============================================================
230
+
231
+ const PROGRESS_FILE = ".beads/swarm-progress.jsonl";
232
+
233
+ interface ProgressEntry {
234
+ timestamp: string;
235
+ team_name: string;
236
+ worker_id: string;
237
+ phase: string;
238
+ progress: number;
239
+ status: string;
240
+ file?: string;
241
+ }
242
+
243
+ async function monitorOperation(
244
+ args: {
245
+ team?: string;
246
+ action?: string;
247
+ worker_id?: string;
248
+ phase?: string;
249
+ progress?: number;
250
+ status?: string;
251
+ file?: string;
252
+ },
253
+ worktree: string,
254
+ ): Promise<string> {
255
+ const team = args.team || "default";
256
+ const action = args.action || "status";
257
+
258
+ switch (action) {
259
+ case "update":
260
+ return updateProgress(
261
+ {
262
+ timestamp: new Date().toISOString(),
263
+ team_name: team,
264
+ worker_id: args.worker_id || "unknown",
265
+ phase: args.phase || "unknown",
266
+ progress: args.progress || 0,
267
+ status: args.status || "idle",
268
+ file: args.file,
269
+ },
270
+ worktree,
271
+ );
272
+ case "render":
273
+ return renderProgress(team, worktree);
274
+ case "status":
275
+ return getFullStatus(team, worktree);
276
+ case "clear":
277
+ return clearTeam(team, worktree);
278
+ default:
279
+ return `Unknown action: ${action}`;
280
+ }
281
+ }
282
+
283
+ async function updateProgress(
284
+ entry: ProgressEntry,
285
+ worktree: string,
286
+ ): Promise<string> {
287
+ const progressPath = path.join(worktree, PROGRESS_FILE);
288
+ await fs.mkdir(path.dirname(progressPath), { recursive: true });
289
+ await fs.appendFile(progressPath, `${JSON.stringify(entry)}\n`, "utf-8");
290
+ return JSON.stringify({ success: true, record: entry }, null, 2);
291
+ }
292
+
293
+ async function getProgress(
294
+ team: string,
295
+ worktree: string,
296
+ ): Promise<{ workers: ProgressEntry[] }> {
297
+ const progressPath = path.join(worktree, PROGRESS_FILE);
298
+ try {
299
+ const content = await fs.readFile(progressPath, "utf-8");
300
+ const entries = content
301
+ .trim()
302
+ .split("\n")
303
+ .filter(Boolean)
304
+ .map((l) => JSON.parse(l) as ProgressEntry)
305
+ .filter((e) => e.team_name === team);
306
+ const workerMap = new Map<string, ProgressEntry>();
307
+ for (const e of entries) workerMap.set(e.worker_id, e);
308
+ return { workers: Array.from(workerMap.values()) };
309
+ } catch {
310
+ return { workers: [] };
311
+ }
312
+ }
313
+
314
+ async function renderProgress(team: string, worktree: string): Promise<string> {
315
+ const { workers } = await getProgress(team, worktree);
316
+ if (workers.length === 0) return `No progress for team: ${team}`;
317
+
318
+ let output = `## Swarm: ${team}\n\n| Worker | Phase | Progress | Status |\n|---|---|---|---|\n`;
319
+ for (const w of workers) {
320
+ output += `| ${w.worker_id} | ${w.phase} | ${w.progress}% | ${w.status} |\n`;
321
+ }
322
+ return output;
323
+ }
324
+
325
+ async function getFullStatus(team: string, worktree: string): Promise<string> {
326
+ const { workers } = await getProgress(team, worktree);
327
+ return JSON.stringify(
328
+ {
329
+ team,
330
+ workers: workers.length,
331
+ completed: workers.filter((w) => w.status === "completed").length,
332
+ working: workers.filter((w) => w.status === "working").length,
333
+ errors: workers.filter((w) => w.status === "error").length,
334
+ details: workers,
335
+ },
336
+ null,
337
+ 2,
338
+ );
339
+ }
340
+
341
+ async function clearTeam(team: string, worktree: string): Promise<string> {
342
+ const progressPath = path.join(worktree, PROGRESS_FILE);
343
+ try {
344
+ const content = await fs.readFile(progressPath, "utf-8");
345
+ const entries = content
346
+ .trim()
347
+ .split("\n")
348
+ .filter(Boolean)
349
+ .map((l) => JSON.parse(l) as ProgressEntry);
350
+ const other = entries.filter((e) => e.team_name !== team);
351
+ await fs.writeFile(
352
+ progressPath,
353
+ other.map((e) => JSON.stringify(e)).join("\n") +
354
+ (other.length ? "\n" : ""),
355
+ "utf-8",
356
+ );
357
+ return JSON.stringify({
358
+ success: true,
359
+ cleared: entries.length - other.length,
360
+ });
361
+ } catch {
362
+ return JSON.stringify({ success: true, cleared: 0 });
363
+ }
364
+ }
365
+
366
+ // ============================================================
367
+ // DELEGATE OPERATION (from swarm-delegate.ts)
368
+ // ============================================================
369
+
370
+ function delegateOperation(args: {
371
+ bead_id?: string;
372
+ title?: string;
373
+ outcome?: string;
374
+ must_do?: string;
375
+ must_not?: string;
376
+ checks?: string;
377
+ context?: string;
378
+ write?: boolean;
379
+ }): string {
380
+ if (!args.bead_id) return "Error: bead_id required";
381
+ if (!args.outcome) return "Error: outcome required";
382
+
383
+ const split = (s?: string) =>
384
+ s
385
+ ?.split(/[,\n]/)
386
+ .map((x) => x.trim())
387
+ .filter(Boolean) || [];
388
+ const bullets = (items: string[]) =>
389
+ items.length ? items.map((i) => `- ${i}`).join("\n") : "- (none)";
390
+
391
+ const packet = [
392
+ "# Delegation Packet",
393
+ "",
394
+ `- TASK: ${args.bead_id}${args.title ? ` - ${args.title}` : ""}`,
395
+ `- EXPECTED OUTCOME: ${args.outcome}`,
396
+ "- MUST DO:",
397
+ bullets(split(args.must_do)),
398
+ "- MUST NOT DO:",
399
+ bullets(split(args.must_not)),
400
+ "- ACCEPTANCE CHECKS:",
401
+ bullets(split(args.checks)),
402
+ "- CONTEXT:",
403
+ args.context || "(none)",
404
+ ].join("\n");
405
+
406
+ if (!args.write) return packet;
407
+
408
+ // Write to artifact file (sync for simplicity)
409
+ const artifactDir = path.join(
410
+ process.cwd(),
411
+ ".beads",
412
+ "artifacts",
413
+ args.bead_id,
414
+ );
415
+ const outPath = path.join(artifactDir, "delegation.md");
416
+ fs.mkdir(artifactDir, { recursive: true })
417
+ .then(() =>
418
+ fs.appendFile(
419
+ outPath,
420
+ `\n---\nGenerated: ${new Date().toISOString()}\n---\n\n${packet}\n`,
421
+ ),
422
+ )
423
+ .catch(() => {});
424
+
425
+ return `✓ Delegation packet written to ${outPath}\n\n${packet}`;
426
+ }
427
+
428
+ // ============================================================
429
+ // SYNC OPERATION (from beads-sync.ts)
430
+ // ============================================================
431
+
432
+ const OPENCODE_TODO_DIR = path.join(
433
+ process.env.HOME || "",
434
+ ".local",
435
+ "share",
436
+ "opencode",
437
+ "storage",
438
+ "todo",
439
+ );
440
+
441
+ interface BeadTask {
442
+ id: string;
443
+ title: string;
444
+ status: string;
445
+ priority: number;
446
+ }
447
+
448
+ async function syncOperation(
449
+ args: { action?: string; filter?: string },
450
+ worktree: string,
451
+ ): Promise<string> {
452
+ const action = args.action || "push";
453
+
454
+ if (action === "push") {
455
+ return pushBeadsToTodos(worktree, args.filter || "open");
456
+ }
457
+ if (action === "pull") {
458
+ return pullTodosToBeads(worktree);
459
+ }
460
+ return `Unknown sync action: ${action}`;
461
+ }
462
+
463
+ async function pushBeadsToTodos(
464
+ worktree: string,
465
+ filter: string,
466
+ ): Promise<string> {
467
+ try {
468
+ const statusFilter = filter === "all" ? "" : `--status ${filter}`;
469
+ const { stdout } = await execAsync(`br list ${statusFilter} --json`, {
470
+ cwd: worktree,
471
+ });
472
+
473
+ let tasks: BeadTask[];
474
+ try {
475
+ tasks = JSON.parse(stdout);
476
+ } catch {
477
+ tasks = parseBeadListOutput(stdout);
478
+ }
479
+
480
+ if (tasks.length === 0) {
481
+ return JSON.stringify({
482
+ success: true,
483
+ message: "No tasks to sync",
484
+ synced: 0,
485
+ });
486
+ }
487
+
488
+ const todos = tasks.map((t) => ({
489
+ id: t.id,
490
+ content: `[Bead] ${t.title}`,
491
+ status:
492
+ t.status === "closed"
493
+ ? "completed"
494
+ : t.status === "in_progress"
495
+ ? "in_progress"
496
+ : "pending",
497
+ priority: t.priority <= 1 ? "high" : t.priority <= 2 ? "medium" : "low",
498
+ beadId: t.id,
499
+ }));
500
+
501
+ const sessionId = `ses_${Date.now().toString(36)}_${path.basename(worktree).slice(0, 10)}`;
502
+ const todoPath = path.join(OPENCODE_TODO_DIR, `${sessionId}.json`);
503
+
504
+ await fs.mkdir(OPENCODE_TODO_DIR, { recursive: true });
505
+ await fs.writeFile(todoPath, JSON.stringify(todos, null, 2), "utf-8");
506
+
507
+ return JSON.stringify({
508
+ success: true,
509
+ synced: todos.length,
510
+ session_id: sessionId,
511
+ });
512
+ } catch (error: unknown) {
513
+ const message = error instanceof Error ? error.message : String(error);
514
+ return JSON.stringify({ success: false, error: message });
515
+ }
516
+ }
517
+
518
+ async function pullTodosToBeads(worktree: string): Promise<string> {
519
+ // Simplified: scan todo files and close completed beads
520
+ try {
521
+ const files = await fs.readdir(OPENCODE_TODO_DIR).catch(() => []);
522
+ let updated = 0;
523
+
524
+ for (const file of files) {
525
+ if (!file.endsWith(".json")) continue;
526
+ const content = await fs.readFile(
527
+ path.join(OPENCODE_TODO_DIR, file),
528
+ "utf-8",
529
+ );
530
+ const todos = JSON.parse(content);
531
+
532
+ for (const todo of todos) {
533
+ if (todo.beadId && todo.status === "completed") {
534
+ try {
535
+ await execAsync(
536
+ `br close ${todo.beadId} --reason "Completed via todo"`,
537
+ {
538
+ cwd: worktree,
539
+ },
540
+ );
541
+ updated++;
542
+ } catch {
543
+ // Already closed
544
+ }
545
+ }
546
+ }
547
+ }
548
+
549
+ return JSON.stringify({ success: true, updated });
550
+ } catch (error: unknown) {
551
+ const message = error instanceof Error ? error.message : String(error);
552
+ return JSON.stringify({ success: false, error: message });
553
+ }
554
+ }
555
+
556
+ function parseBeadListOutput(output: string): BeadTask[] {
557
+ const lines = output.trim().split("\n").filter(Boolean);
558
+ const tasks: BeadTask[] = [];
559
+
560
+ for (const line of lines) {
561
+ const match = line.match(/^#?(\S+)\s+\[(\w+)\]\s+(?:\(P(\d)\))?\s*(.+)$/);
562
+ if (match) {
563
+ tasks.push({
564
+ id: match[1],
565
+ status: match[2],
566
+ priority: match[3] ? Number.parseInt(match[3]) : 2,
567
+ title: match[4],
568
+ });
569
+ }
570
+ }
571
+ return tasks;
572
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencodekit",
3
- "version": "0.16.4",
3
+ "version": "0.16.6",
4
4
  "description": "CLI tool for bootstrapping and managing OpenCodeKit projects",
5
5
  "keywords": ["agents", "cli", "mcp", "opencode", "opencodekit", "template"],
6
6
  "license": "MIT",
@@ -1,66 +0,0 @@
1
- # Specification
2
-
3
- **Bead:** [bead-id]
4
- **Proposal:** [link to proposal]
5
- **Date:** [YYYY-MM-DD]
6
-
7
- ## Bead Metadata
8
-
9
- ```yaml
10
- depends_on: [] # Bead IDs that must complete before this one
11
- parallel: true # Can run concurrently with other parallel beads
12
- conflicts_with: [] # Bead IDs that modify same files (cannot parallelize)
13
- blocks: [] # Bead IDs that are waiting on this one
14
- estimated_hours: 2 # Time estimate for planning
15
- ```
16
-
17
- ---
18
-
19
- ## Requirements
20
-
21
- ### [Requirement Name]
22
-
23
- Brief description of what must be true.
24
-
25
- #### Scenarios
26
-
27
- **WHEN** [precondition or trigger]
28
- **THEN** [expected outcome]
29
-
30
- **WHEN** [edge case condition]
31
- **THEN** [expected behavior]
32
-
33
- ### [Requirement Name]
34
-
35
- Brief description.
36
-
37
- #### Scenarios
38
-
39
- **WHEN** [condition]
40
- **THEN** [outcome]
41
-
42
- ---
43
-
44
- ## Non-Functional Requirements
45
-
46
- - **Performance:** [constraint if applicable]
47
- - **Security:** [constraint if applicable]
48
- - **Compatibility:** [constraint if applicable]
49
-
50
- ---
51
-
52
- ## Affected Files
53
-
54
- List files this bead will modify (for conflict detection with other beads):
55
-
56
- ```yaml
57
- files:
58
- - src/path/to/file.ts # Why
59
- - src/path/to/other.ts # Why
60
- ```
61
-
62
- ---
63
-
64
- ## Out of Scope
65
-
66
- - [Explicitly excluded capability]