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
@@ -1,657 +0,0 @@
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
- // OpenCode's native todo storage location
10
- const OPENCODE_TODO_DIR = path.join(
11
- process.env.HOME || "",
12
- ".local",
13
- "share",
14
- "opencode",
15
- "storage",
16
- "todo",
17
- );
18
-
19
- // Shared task lists for cross-session coordination
20
- const SHARED_TASKS_DIR = path.join(
21
- process.env.HOME || "",
22
- ".local",
23
- "share",
24
- "opencode",
25
- "storage",
26
- "shared-tasks",
27
- );
28
-
29
- interface BeadTask {
30
- id: string;
31
- title: string;
32
- status: "open" | "in_progress" | "closed";
33
- priority: number;
34
- labels: string[];
35
- blockedBy: string[];
36
- blocks: string[];
37
- }
38
-
39
- interface TodoItem {
40
- id: string;
41
- content: string;
42
- status: "pending" | "in_progress" | "completed" | "cancelled";
43
- priority: "high" | "medium" | "low";
44
- blockedBy?: string[];
45
- blocks?: string[];
46
- beadId?: string;
47
- phase?: string;
48
- }
49
-
50
- interface SharedTaskList {
51
- id: string;
52
- name: string;
53
- created: string;
54
- updated: string;
55
- tasks: TodoItem[];
56
- }
57
-
58
- export default tool({
59
- description: `Bridge between Beads git-backed tasks and OpenCode's native todo system.
60
-
61
- Operations:
62
- - push: Sync Beads tasks to OpenCode todos (makes them visible to subagents via todoread)
63
- - pull: Update Beads from completed OpenCode todos
64
- - create_shared: Create a shared task list for cross-session swarm coordination
65
- - get_shared: Get a shared task list by ID
66
- - update_shared: Update tasks in a shared list
67
- - list_shared: List all shared task lists for this project
68
-
69
- Usage:
70
- beads-sync({ operation: "push" }) // Push current Beads to OpenCode todos
71
- beads-sync({ operation: "create_shared", name: "api-refactor-swarm", tasks: [...] })
72
- beads-sync({ operation: "get_shared", list_id: "abc123" })`,
73
-
74
- args: {
75
- operation: tool.schema
76
- .enum([
77
- "push",
78
- "pull",
79
- "create_shared",
80
- "get_shared",
81
- "update_shared",
82
- "list_shared",
83
- ])
84
- .describe("Sync operation to perform"),
85
- session_id: tool.schema
86
- .string()
87
- .optional()
88
- .describe("Target session ID (defaults to current)"),
89
- list_id: tool.schema
90
- .string()
91
- .optional()
92
- .describe("Shared task list ID (for get_shared/update_shared)"),
93
- name: tool.schema
94
- .string()
95
- .optional()
96
- .describe("Name for new shared task list"),
97
- tasks: tool.schema
98
- .string()
99
- .optional()
100
- .describe("JSON array of tasks (for create_shared/update_shared)"),
101
- filter: tool.schema
102
- .string()
103
- .optional()
104
- .describe("Filter by status: open, in_progress, all (default: open)"),
105
- },
106
-
107
- execute: async (args, ctx) => {
108
- const worktree = ctx.worktree || process.cwd();
109
-
110
- switch (args.operation) {
111
- case "push":
112
- return await pushBeadsToTodos(
113
- worktree,
114
- args.session_id,
115
- args.filter || "open",
116
- );
117
- case "pull":
118
- return await pullTodosToBeads(worktree, args.session_id);
119
- case "create_shared":
120
- return await createSharedList(args.name, args.tasks, worktree);
121
- case "get_shared":
122
- return await getSharedList(args.list_id);
123
- case "update_shared":
124
- return await updateSharedList(args.list_id, args.tasks);
125
- case "list_shared":
126
- return await listSharedLists(worktree);
127
- default:
128
- return `Error: Unknown operation: ${args.operation}`;
129
- }
130
- },
131
- });
132
-
133
- /**
134
- * Push Beads tasks to OpenCode's native todo storage
135
- * This makes Beads visible to subagents via todoread
136
- */
137
- async function pushBeadsToTodos(
138
- worktree: string,
139
- sessionId?: string,
140
- filter = "open",
141
- ): Promise<string> {
142
- try {
143
- // Get beads tasks using br list (beads_rust)
144
- const statusFilter = filter === "all" ? "" : `--status ${filter}`;
145
- const { stdout } = await execAsync(`br list ${statusFilter} --json`, {
146
- cwd: worktree,
147
- });
148
-
149
- let beadTasks: BeadTask[];
150
- try {
151
- beadTasks = JSON.parse(stdout);
152
- } catch {
153
- // br list might not support --json, parse text output
154
- beadTasks = parseBeadListOutput(stdout);
155
- }
156
-
157
- if (beadTasks.length === 0) {
158
- return JSON.stringify(
159
- {
160
- success: true,
161
- message: "No Beads tasks to sync",
162
- synced: 0,
163
- },
164
- null,
165
- 2,
166
- );
167
- }
168
-
169
- // Convert to OpenCode todo format
170
- const todos: TodoItem[] = beadTasks.map((bead) => ({
171
- id: bead.id,
172
- content: `[Bead] ${bead.title}`,
173
- status: mapBeadStatus(bead.status),
174
- priority: mapBeadPriority(bead.priority),
175
- blockedBy: bead.blockedBy || [],
176
- blocks: bead.blocks || [],
177
- beadId: bead.id,
178
- }));
179
-
180
- // Write to OpenCode todo storage
181
- const targetSessionId = sessionId || (await getCurrentSessionId(worktree));
182
- const todoPath = path.join(OPENCODE_TODO_DIR, `${targetSessionId}.json`);
183
-
184
- await fs.mkdir(OPENCODE_TODO_DIR, { recursive: true });
185
- await fs.writeFile(todoPath, JSON.stringify(todos, null, 2), "utf-8");
186
-
187
- return JSON.stringify(
188
- {
189
- success: true,
190
- message: `Synced ${todos.length} Beads tasks to OpenCode todos`,
191
- synced: todos.length,
192
- session_id: targetSessionId,
193
- todo_path: todoPath,
194
- tasks: todos.map((t) => ({
195
- id: t.id,
196
- content: t.content,
197
- status: t.status,
198
- blockedBy: t.blockedBy,
199
- })),
200
- },
201
- null,
202
- 2,
203
- );
204
- } catch (error: any) {
205
- return JSON.stringify(
206
- {
207
- success: false,
208
- error: `Failed to push Beads to todos: ${error.message}`,
209
- },
210
- null,
211
- 2,
212
- );
213
- }
214
- }
215
-
216
- /**
217
- * Pull completed todos back to Beads
218
- */
219
- async function pullTodosToBeads(
220
- worktree: string,
221
- sessionId?: string,
222
- ): Promise<string> {
223
- try {
224
- const targetSessionId = sessionId || (await getCurrentSessionId(worktree));
225
- const todoPath = path.join(OPENCODE_TODO_DIR, `${targetSessionId}.json`);
226
-
227
- let todos: TodoItem[];
228
- try {
229
- const content = await fs.readFile(todoPath, "utf-8");
230
- todos = JSON.parse(content);
231
- } catch {
232
- return JSON.stringify(
233
- {
234
- success: true,
235
- message: "No todos to pull",
236
- updated: 0,
237
- },
238
- null,
239
- 2,
240
- );
241
- }
242
-
243
- // Find completed todos that came from Beads
244
- const completedBeadTodos = todos.filter(
245
- (t) => t.beadId && t.status === "completed",
246
- );
247
-
248
- if (completedBeadTodos.length === 0) {
249
- return JSON.stringify(
250
- {
251
- success: true,
252
- message: "No completed Bead todos to update",
253
- updated: 0,
254
- },
255
- null,
256
- 2,
257
- );
258
- }
259
-
260
- // Update Beads status
261
- const updated: string[] = [];
262
- for (const todo of completedBeadTodos) {
263
- try {
264
- await execAsync(
265
- `br close ${todo.beadId} --reason "Completed via todo"`,
266
- { cwd: worktree },
267
- );
268
- updated.push(todo.beadId!);
269
- } catch {
270
- // Bead might already be closed
271
- }
272
- }
273
-
274
- return JSON.stringify(
275
- {
276
- success: true,
277
- message: `Updated ${updated.length} Beads from completed todos`,
278
- updated: updated.length,
279
- bead_ids: updated,
280
- },
281
- null,
282
- 2,
283
- );
284
- } catch (error: any) {
285
- return JSON.stringify(
286
- {
287
- success: false,
288
- error: `Failed to pull todos to Beads: ${error.message}`,
289
- },
290
- null,
291
- 2,
292
- );
293
- }
294
- }
295
-
296
- /**
297
- * Create a shared task list for cross-session swarm coordination
298
- */
299
- async function createSharedList(
300
- name?: string,
301
- tasksJson?: string,
302
- worktree?: string,
303
- ): Promise<string> {
304
- if (!name) {
305
- return JSON.stringify({ success: false, error: "name is required" });
306
- }
307
-
308
- try {
309
- const listId = generateListId();
310
- let tasks: TodoItem[] = [];
311
-
312
- if (tasksJson) {
313
- try {
314
- tasks = JSON.parse(tasksJson);
315
- } catch {
316
- return JSON.stringify({
317
- success: false,
318
- error: "Invalid tasks JSON",
319
- });
320
- }
321
- }
322
-
323
- const sharedList: SharedTaskList = {
324
- id: listId,
325
- name,
326
- created: new Date().toISOString(),
327
- updated: new Date().toISOString(),
328
- tasks,
329
- };
330
-
331
- // Get project ID from worktree
332
- const projectDir = worktree || process.cwd();
333
- const projectId = path.basename(projectDir);
334
- const listPath = path.join(SHARED_TASKS_DIR, projectId, `${listId}.json`);
335
-
336
- await fs.mkdir(path.dirname(listPath), { recursive: true });
337
- await fs.writeFile(listPath, JSON.stringify(sharedList, null, 2), "utf-8");
338
-
339
- return JSON.stringify(
340
- {
341
- success: true,
342
- message: `Created shared task list: ${name}`,
343
- list_id: listId,
344
- path: listPath,
345
- task_count: tasks.length,
346
- usage: `Share with workers: SHARED_TASK_LIST_ID=${listId}`,
347
- },
348
- null,
349
- 2,
350
- );
351
- } catch (error: any) {
352
- return JSON.stringify(
353
- {
354
- success: false,
355
- error: `Failed to create shared list: ${error.message}`,
356
- },
357
- null,
358
- 2,
359
- );
360
- }
361
- }
362
-
363
- /**
364
- * Get a shared task list by ID
365
- */
366
- async function getSharedList(listId?: string): Promise<string> {
367
- if (!listId) {
368
- return JSON.stringify({ success: false, error: "list_id is required" });
369
- }
370
-
371
- try {
372
- // Search in all project directories
373
- const projectDirs = await fs.readdir(SHARED_TASKS_DIR).catch(() => []);
374
-
375
- for (const projectId of projectDirs) {
376
- const listPath = path.join(SHARED_TASKS_DIR, projectId, `${listId}.json`);
377
- try {
378
- const content = await fs.readFile(listPath, "utf-8");
379
- const list: SharedTaskList = JSON.parse(content);
380
-
381
- // Calculate stats
382
- const completed = list.tasks.filter(
383
- (t) => t.status === "completed",
384
- ).length;
385
- const pending = list.tasks.filter((t) => t.status === "pending").length;
386
- const inProgress = list.tasks.filter(
387
- (t) => t.status === "in_progress",
388
- ).length;
389
-
390
- return JSON.stringify(
391
- {
392
- success: true,
393
- list,
394
- stats: {
395
- total: list.tasks.length,
396
- completed,
397
- pending,
398
- in_progress: inProgress,
399
- progress_percent: Math.round(
400
- (completed / list.tasks.length) * 100,
401
- ),
402
- },
403
- },
404
- null,
405
- 2,
406
- );
407
- } catch {
408
- continue;
409
- }
410
- }
411
-
412
- return JSON.stringify({
413
- success: false,
414
- error: `Shared list not found: ${listId}`,
415
- });
416
- } catch (error: any) {
417
- return JSON.stringify(
418
- {
419
- success: false,
420
- error: `Failed to get shared list: ${error.message}`,
421
- },
422
- null,
423
- 2,
424
- );
425
- }
426
- }
427
-
428
- /**
429
- * Update tasks in a shared list
430
- */
431
- async function updateSharedList(
432
- listId?: string,
433
- tasksJson?: string,
434
- ): Promise<string> {
435
- if (!listId) {
436
- return JSON.stringify({ success: false, error: "list_id is required" });
437
- }
438
-
439
- try {
440
- // Find the list
441
- const projectDirs = await fs.readdir(SHARED_TASKS_DIR).catch(() => []);
442
- let listPath: string | null = null;
443
- let existingList: SharedTaskList | null = null;
444
-
445
- for (const projectId of projectDirs) {
446
- const candidatePath = path.join(
447
- SHARED_TASKS_DIR,
448
- projectId,
449
- `${listId}.json`,
450
- );
451
- try {
452
- const content = await fs.readFile(candidatePath, "utf-8");
453
- existingList = JSON.parse(content);
454
- listPath = candidatePath;
455
- break;
456
- } catch {
457
- continue;
458
- }
459
- }
460
-
461
- if (!listPath || !existingList) {
462
- return JSON.stringify({
463
- success: false,
464
- error: `Shared list not found: ${listId}`,
465
- });
466
- }
467
-
468
- // Parse and merge updates
469
- if (tasksJson) {
470
- try {
471
- const updates: Partial<TodoItem>[] = JSON.parse(tasksJson);
472
-
473
- for (const update of updates) {
474
- if (!update.id) continue;
475
-
476
- const existingTask = existingList.tasks.find(
477
- (t) => t.id === update.id,
478
- );
479
- if (existingTask) {
480
- Object.assign(existingTask, update);
481
- } else {
482
- existingList.tasks.push(update as TodoItem);
483
- }
484
- }
485
- } catch {
486
- return JSON.stringify({
487
- success: false,
488
- error: "Invalid tasks JSON",
489
- });
490
- }
491
- }
492
-
493
- existingList.updated = new Date().toISOString();
494
- await fs.writeFile(
495
- listPath,
496
- JSON.stringify(existingList, null, 2),
497
- "utf-8",
498
- );
499
-
500
- return JSON.stringify(
501
- {
502
- success: true,
503
- message: `Updated shared list: ${existingList.name}`,
504
- list_id: listId,
505
- task_count: existingList.tasks.length,
506
- },
507
- null,
508
- 2,
509
- );
510
- } catch (error: any) {
511
- return JSON.stringify(
512
- {
513
- success: false,
514
- error: `Failed to update shared list: ${error.message}`,
515
- },
516
- null,
517
- 2,
518
- );
519
- }
520
- }
521
-
522
- /**
523
- * List all shared task lists for this project
524
- */
525
- async function listSharedLists(worktree?: string): Promise<string> {
526
- try {
527
- const projectDir = worktree || process.cwd();
528
- const projectId = path.basename(projectDir);
529
- const projectListDir = path.join(SHARED_TASKS_DIR, projectId);
530
-
531
- let files: string[];
532
- try {
533
- files = await fs.readdir(projectListDir);
534
- } catch {
535
- return JSON.stringify(
536
- {
537
- success: true,
538
- lists: [],
539
- message: "No shared task lists found for this project",
540
- },
541
- null,
542
- 2,
543
- );
544
- }
545
-
546
- const lists: Array<{
547
- id: string;
548
- name: string;
549
- task_count: number;
550
- completed: number;
551
- updated: string;
552
- }> = [];
553
-
554
- for (const file of files) {
555
- if (!file.endsWith(".json")) continue;
556
-
557
- try {
558
- const content = await fs.readFile(
559
- path.join(projectListDir, file),
560
- "utf-8",
561
- );
562
- const list: SharedTaskList = JSON.parse(content);
563
- lists.push({
564
- id: list.id,
565
- name: list.name,
566
- task_count: list.tasks.length,
567
- completed: list.tasks.filter((t) => t.status === "completed").length,
568
- updated: list.updated,
569
- });
570
- } catch {
571
- continue;
572
- }
573
- }
574
-
575
- return JSON.stringify(
576
- {
577
- success: true,
578
- lists,
579
- count: lists.length,
580
- },
581
- null,
582
- 2,
583
- );
584
- } catch (error: any) {
585
- return JSON.stringify(
586
- {
587
- success: false,
588
- error: `Failed to list shared lists: ${error.message}`,
589
- },
590
- null,
591
- 2,
592
- );
593
- }
594
- }
595
-
596
- // Helper functions
597
-
598
- function parseBeadListOutput(output: string): BeadTask[] {
599
- const lines = output.trim().split("\n").filter(Boolean);
600
- const tasks: BeadTask[] = [];
601
-
602
- for (const line of lines) {
603
- // Parse lines like: #123 [open] (P2) Task title
604
- const match = line.match(/^#?(\S+)\s+\[(\w+)\]\s+(?:\(P(\d)\))?\s*(.+)$/);
605
- if (match) {
606
- tasks.push({
607
- id: match[1],
608
- status: match[2] as BeadTask["status"],
609
- priority: match[3] ? Number.parseInt(match[3]) : 2,
610
- title: match[4],
611
- labels: [],
612
- blockedBy: [],
613
- blocks: [],
614
- });
615
- }
616
- }
617
-
618
- return tasks;
619
- }
620
-
621
- function mapBeadStatus(
622
- status: string,
623
- ): "pending" | "in_progress" | "completed" | "cancelled" {
624
- switch (status) {
625
- case "open":
626
- return "pending";
627
- case "in_progress":
628
- return "in_progress";
629
- case "closed":
630
- return "completed";
631
- default:
632
- return "pending";
633
- }
634
- }
635
-
636
- function mapBeadPriority(priority: number): "high" | "medium" | "low" {
637
- if (priority <= 1) return "high";
638
- if (priority <= 2) return "medium";
639
- return "low";
640
- }
641
-
642
- async function getCurrentSessionId(worktree: string): Promise<string> {
643
- // Try to get from environment or generate based on project
644
- const envSessionId = process.env.OPENCODE_SESSION_ID;
645
- if (envSessionId) return envSessionId;
646
-
647
- // Generate a session-like ID based on project path
648
- const projectName = path.basename(worktree);
649
- const timestamp = Date.now().toString(36);
650
- return `ses_${timestamp}_${projectName.slice(0, 10)}`;
651
- }
652
-
653
- function generateListId(): string {
654
- const timestamp = Date.now().toString(36);
655
- const random = Math.random().toString(36).slice(2, 8);
656
- return `shl_${timestamp}${random}`;
657
- }