opencodekit 0.18.5 → 0.18.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.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createRequire } from "node:module";
3
3
  import { cac } from "cac";
4
4
  import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { basename, dirname, join, relative, resolve } from "node:path";
5
+ import { basename, dirname, join, relative } from "node:path";
6
6
  import * as p from "@clack/prompts";
7
7
  import color from "picocolors";
8
8
  import { z } from "zod";
@@ -18,11 +18,35 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
18
18
 
19
19
  //#endregion
20
20
  //#region package.json
21
- var version = "0.18.5";
21
+ var version = "0.18.6";
22
22
 
23
23
  //#endregion
24
24
  //#region src/utils/errors.ts
25
25
  /**
26
+ * Resolve the .opencode directory path.
27
+ * Handles two cases:
28
+ * 1. Standard project: cwd has .opencode/ subdirectory
29
+ * 2. Global config dir: cwd IS the opencode config dir (has opencode.json directly)
30
+ * Returns null if neither case applies.
31
+ */
32
+ function resolveOpencodePath() {
33
+ const nested = join(process.cwd(), ".opencode");
34
+ if (existsSync(nested)) return nested;
35
+ if (existsSync(join(process.cwd(), "opencode.json"))) return process.cwd();
36
+ return null;
37
+ }
38
+ /**
39
+ * Resolve opencode path or show "not initialized" error and return null.
40
+ */
41
+ function requireOpencodePath() {
42
+ const resolved = resolveOpencodePath();
43
+ if (!resolved) {
44
+ notInitialized();
45
+ return null;
46
+ }
47
+ return resolved;
48
+ }
49
+ /**
26
50
  * Display a styled error message with optional fix suggestion
27
51
  */
28
52
  function showError(message, fix) {
@@ -79,7 +103,8 @@ const InitOptionsSchema = z.object({
79
103
  yes: z.boolean().optional().default(false),
80
104
  backup: z.boolean().optional().default(false),
81
105
  prune: z.boolean().optional().default(false),
82
- pruneAll: z.boolean().optional().default(false)
106
+ pruneAll: z.boolean().optional().default(false),
107
+ projectOnly: z.boolean().optional().default(false)
83
108
  });
84
109
  const UpgradeOptionsSchema = z.object({
85
110
  force: z.boolean().optional().default(false),
@@ -192,11 +217,8 @@ function parseAction(schema, action) {
192
217
  //#region src/commands/agent.ts
193
218
  async function agentCommand(action) {
194
219
  const validatedAction = parseAction(AgentActionSchema, action);
195
- const opencodePath = join(process.cwd(), ".opencode");
196
- if (!existsSync(opencodePath)) {
197
- notInitialized();
198
- return;
199
- }
220
+ const opencodePath = requireOpencodePath();
221
+ if (!opencodePath) return;
200
222
  const agentPath = join(opencodePath, "agent");
201
223
  switch (validatedAction) {
202
224
  case "list":
@@ -473,11 +495,8 @@ async function removeAgent(agentPath, agentNameArg) {
473
495
  //#region src/commands/command.ts
474
496
  async function commandCommand(action) {
475
497
  const validatedAction = parseAction(CommandActionSchema, action);
476
- const opencodePath = join(process.cwd(), ".opencode");
477
- if (!existsSync(opencodePath)) {
478
- notInitialized();
479
- return;
480
- }
498
+ const opencodePath = requireOpencodePath();
499
+ if (!opencodePath) return;
481
500
  const commandDir = join(opencodePath, "command");
482
501
  switch (validatedAction) {
483
502
  case "list":
@@ -982,12 +1001,9 @@ async function getAgentsFromServer() {
982
1001
  return fetchFromServer("/agent");
983
1002
  }
984
1003
  async function configCommand(action) {
985
- const opencodePath = join(process.cwd(), ".opencode");
1004
+ const opencodePath = requireOpencodePath();
1005
+ if (!opencodePath) return;
986
1006
  const configPath = join(opencodePath, "opencode.json");
987
- if (!existsSync(opencodePath)) {
988
- notInitialized();
989
- return;
990
- }
991
1007
  if (!existsSync(configPath)) {
992
1008
  showError("opencode.json not found", "ock init --force");
993
1009
  return;
@@ -2758,6 +2774,34 @@ const EXCLUDED_FILES = [
2758
2774
  "pnpm-lock.yaml"
2759
2775
  ];
2760
2776
  const PRESERVE_USER_DIRS = ["memory/project", "context"];
2777
+ const SHARED_CONFIG_DIRS = [
2778
+ "agent",
2779
+ "command",
2780
+ "skill",
2781
+ "tool"
2782
+ ];
2783
+ /**
2784
+ * Detect if global config has any of the shared dirs populated.
2785
+ * Returns null if no global config or no shared dirs found.
2786
+ */
2787
+ function detectGlobalConfig() {
2788
+ const globalDir = getGlobalConfigDir();
2789
+ if (!existsSync(globalDir)) return null;
2790
+ const coveredDirs = SHARED_CONFIG_DIRS.filter((d) => {
2791
+ const dirPath = join(globalDir, d);
2792
+ if (!existsSync(dirPath)) return false;
2793
+ try {
2794
+ return readdirSync(dirPath).filter((e) => !e.startsWith(".")).length > 0;
2795
+ } catch {
2796
+ return false;
2797
+ }
2798
+ });
2799
+ if (coveredDirs.length === 0) return null;
2800
+ return {
2801
+ dir: globalDir,
2802
+ coveredDirs
2803
+ };
2804
+ }
2761
2805
  /**
2762
2806
  * Get the global OpenCode config directory based on OS.
2763
2807
  * - macOS/Linux: ~/.config/opencode/ (respects XDG_CONFIG_HOME)
@@ -2801,10 +2845,25 @@ async function copyDir(src, dest) {
2801
2845
  else writeFileSync(destPath, readFileSync(srcPath, "utf-8"));
2802
2846
  }
2803
2847
  }
2804
- async function copyOpenCodeOnly(templateRoot, targetDir) {
2848
+ async function copyOpenCodeOnly(templateRoot, targetDir, skipDirs) {
2805
2849
  const opencodeSrc = join(templateRoot, ".opencode");
2806
2850
  const opencodeDest = join(targetDir, ".opencode");
2807
2851
  if (!existsSync(opencodeSrc)) return false;
2852
+ if (skipDirs && skipDirs.length > 0) {
2853
+ const skipSet = new Set(skipDirs);
2854
+ mkdirSync(opencodeDest, { recursive: true });
2855
+ for (const entry of readdirSync(opencodeSrc, { withFileTypes: true })) {
2856
+ if (EXCLUDED_DIRS.includes(entry.name)) continue;
2857
+ if (!entry.isDirectory() && EXCLUDED_FILES.includes(entry.name)) continue;
2858
+ if (entry.isSymbolicLink()) continue;
2859
+ if (entry.isDirectory() && skipSet.has(entry.name)) continue;
2860
+ const srcPath = join(opencodeSrc, entry.name);
2861
+ const destPath = join(opencodeDest, entry.name);
2862
+ if (entry.isDirectory()) await copyDir(srcPath, destPath);
2863
+ else writeFileSync(destPath, readFileSync(srcPath, "utf-8"));
2864
+ }
2865
+ return true;
2866
+ }
2808
2867
  await copyDir(opencodeSrc, opencodeDest);
2809
2868
  return true;
2810
2869
  }
@@ -3100,18 +3159,36 @@ async function initCommand(rawOptions = {}) {
3100
3159
  }
3101
3160
  projectName = name || projectName;
3102
3161
  }
3162
+ let skipDirs = [];
3163
+ if (!options.global) {
3164
+ const globalConfig = detectGlobalConfig();
3165
+ if (globalConfig && options.projectOnly) {
3166
+ skipDirs = globalConfig.coveredDirs;
3167
+ p.log.info(`Using global config from ${color.cyan(globalConfig.dir)}`);
3168
+ p.log.info(`Skipping: ${skipDirs.map((d) => color.dim(d)).join(", ")}`);
3169
+ } else if (globalConfig && !options.yes) {
3170
+ p.log.info(`Global config found at ${color.cyan(globalConfig.dir)}`);
3171
+ p.log.info(`Available globally: ${globalConfig.coveredDirs.map((d) => color.green(d)).join(", ")}`);
3172
+ const useGlobal = await p.confirm({
3173
+ message: "Skip these (use global config)? Only project-scope files will be created locally.",
3174
+ initialValue: true
3175
+ });
3176
+ if (!p.isCancel(useGlobal) && useGlobal) skipDirs = globalConfig.coveredDirs;
3177
+ } else if (globalConfig && options.yes) p.log.info(`Global config found at ${color.cyan(globalConfig.dir)} — use ${color.bold("--project-only")} to skip shared dirs`);
3178
+ }
3103
3179
  const s = p.spinner();
3104
3180
  if (mode === "scaffold") {
3105
3181
  s.start("Scaffolding project");
3106
3182
  mkdirSync(targetDir, { recursive: true });
3107
3183
  } else if (mode === "add-config") s.start("Adding OpenCodeKit");
3108
3184
  else s.start("Reinitializing");
3109
- if (!await copyOpenCodeOnly(templateRoot, targetDir)) {
3185
+ if (!await copyOpenCodeOnly(templateRoot, targetDir, skipDirs)) {
3110
3186
  s.stop("Failed");
3111
3187
  p.outro(color.red("Template copy failed"));
3112
3188
  process.exit(1);
3113
3189
  }
3114
3190
  s.stop("Done");
3191
+ if (skipDirs.length > 0) p.log.info(`Project-only init: skipped ${skipDirs.map((d) => color.dim(d)).join(", ")} (using global config)`);
3115
3192
  const restoredFileCount = finalizeInstalledFiles(targetDir, getPackageVersion$1(), preservedFiles);
3116
3193
  if (restoredFileCount > 0) p.log.info(`Preserved ${restoredFileCount} user memory files (memory/project/)`);
3117
3194
  if (options.free) {
@@ -3280,11 +3357,8 @@ async function initCommand(rawOptions = {}) {
3280
3357
  //#region src/commands/skill.ts
3281
3358
  async function skillCommand(action) {
3282
3359
  const validatedAction = parseAction(SkillActionSchema, action);
3283
- const opencodePath = join(process.cwd(), ".opencode");
3284
- if (!existsSync(opencodePath)) {
3285
- notInitialized();
3286
- return;
3287
- }
3360
+ const opencodePath = requireOpencodePath();
3361
+ if (!opencodePath) return;
3288
3362
  const skillDir = join(opencodePath, "skill");
3289
3363
  switch (validatedAction) {
3290
3364
  case "list":
@@ -3684,12 +3758,9 @@ function findUpgradeOrphans(installedFiles, templateFiles) {
3684
3758
  async function upgradeCommand(rawOptions = {}) {
3685
3759
  const options = parseOptions(UpgradeOptionsSchema, rawOptions);
3686
3760
  if (process.argv.includes("--quiet")) return;
3687
- const opencodeDir = join(process.cwd(), ".opencode");
3761
+ const opencodeDir = requireOpencodePath();
3762
+ if (!opencodeDir) return;
3688
3763
  const manifest = loadManifest(opencodeDir);
3689
- if (!existsSync(opencodeDir)) {
3690
- notInitialized();
3691
- return;
3692
- }
3693
3764
  p.intro(color.bgCyan(color.black(" Upgrade ")));
3694
3765
  const versionInfo = await checkVersion(opencodeDir);
3695
3766
  console.log();
@@ -4120,11 +4191,8 @@ function displayChecks(checks) {
4120
4191
  async function statusCommand() {
4121
4192
  if (process.argv.includes("--quiet")) return;
4122
4193
  const cwd = process.cwd();
4123
- const opencodeDir = join(cwd, ".opencode");
4124
- if (!existsSync(opencodeDir)) {
4125
- notInitialized();
4126
- return;
4127
- }
4194
+ const opencodeDir = requireOpencodePath();
4195
+ if (!opencodeDir) return;
4128
4196
  const projectName = basename(cwd);
4129
4197
  p.intro(color.bgCyan(color.black(` ${projectName} `)));
4130
4198
  const agentDir = join(opencodeDir, "agent");
@@ -4163,14 +4231,6 @@ async function statusCommand() {
4163
4231
 
4164
4232
  //#endregion
4165
4233
  //#region src/commands/patch.ts
4166
- function getOpencodeDir() {
4167
- const opencodeDir = join(resolve("."), ".opencode");
4168
- if (!existsSync(opencodeDir)) {
4169
- notInitialized();
4170
- return null;
4171
- }
4172
- return opencodeDir;
4173
- }
4174
4234
  function listPatches(opencodeDir) {
4175
4235
  const metadata = loadPatchMetadata(opencodeDir);
4176
4236
  const entries = Object.entries(metadata.patches);
@@ -4399,7 +4459,7 @@ function togglePatch(opencodeDir, disable) {
4399
4459
  }
4400
4460
  }
4401
4461
  async function patchCommand(action) {
4402
- const opencodeDir = getOpencodeDir();
4462
+ const opencodeDir = requireOpencodePath();
4403
4463
  if (!opencodeDir) return;
4404
4464
  const validatedAction = parseAction(PatchActionSchema, action);
4405
4465
  if (!validatedAction) {
@@ -5416,7 +5476,7 @@ function stripAnsi(str) {
5416
5476
  //#region src/tui/hooks/useData.ts
5417
5477
  function loadProjectData() {
5418
5478
  const cwd = process.cwd();
5419
- const opencodeDir = join(cwd, ".opencode");
5479
+ const opencodeDir = resolveOpencodePath() ?? join(cwd, ".opencode");
5420
5480
  const projectName = basename(cwd);
5421
5481
  const agentDir = join(opencodeDir, "agent");
5422
5482
  let agents = [];
@@ -5445,9 +5505,6 @@ function loadProjectData() {
5445
5505
  mcpServers
5446
5506
  };
5447
5507
  }
5448
- function isInitialized() {
5449
- return existsSync(join(process.cwd(), ".opencode"));
5450
- }
5451
5508
 
5452
5509
  //#endregion
5453
5510
  //#region src/tui/index.ts
@@ -5455,10 +5512,7 @@ function isInitialized() {
5455
5512
  * Launch the TUI dashboard with interactive navigation.
5456
5513
  */
5457
5514
  async function launchTUI() {
5458
- if (!isInitialized()) {
5459
- notInitialized();
5460
- return;
5461
- }
5515
+ if (!requireOpencodePath()) return;
5462
5516
  await mainMenu();
5463
5517
  }
5464
5518
  async function mainMenu() {
@@ -5561,7 +5615,7 @@ const cli = cac("ock");
5561
5615
  cli.option("--verbose", "Enable verbose logging");
5562
5616
  cli.option("--quiet", "Suppress all output");
5563
5617
  cli.version(`${packageVersion}`);
5564
- cli.command("init", "Initialize OpenCodeKit in current directory").option("--force", "Reinitialize even if already exists").option("--beads", "Also initialize .beads/ for multi-agent coordination").option("--global", "Install to global OpenCode config (~/.config/opencode/)").option("--free", "Use free models (default)").option("--recommend", "Use recommended premium models").option("-y, --yes", "Skip prompts, use defaults (for CI)").option("--backup", "Backup existing .opencode before overwriting").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").action(initCommand);
5618
+ cli.command("init", "Initialize OpenCodeKit in current directory").option("--force", "Reinitialize even if already exists").option("--beads", "Also initialize .beads/ for multi-agent coordination").option("--global", "Install to global OpenCode config (~/.config/opencode/)").option("--free", "Use free models (default)").option("--recommend", "Use recommended premium models").option("-y, --yes", "Skip prompts, use defaults (for CI)").option("--backup", "Backup existing .opencode before overwriting").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").option("--project-only", "Only init project-scope files (skip if global config has agents/skills/commands/tools)").action(initCommand);
5565
5619
  cli.command("agent [action]", "Manage agents (list, add, view)").action(async (action) => {
5566
5620
  if (!action) {
5567
5621
  console.log("\nUsage: ock agent <action>\n");
@@ -11,6 +11,9 @@ tools:
11
11
  memory-update: false
12
12
  observation: false
13
13
  question: false
14
+ websearch: false
15
+ webfetch: false
16
+ codesearch: false
14
17
  ---
15
18
 
16
19
  You are OpenCode, the best coding agent on the planet.
@@ -19,8 +22,6 @@ You are OpenCode, the best coding agent on the planet.
19
22
 
20
23
  **Purpose**: Read-only codebase cartographer — you map terrain, you don't build on it.
21
24
 
22
- > _"Agency is knowing where the levers are before you pull them."_
23
-
24
25
  ## Identity
25
26
 
26
27
  You are a read-only codebase explorer. You output concise, evidence-backed findings with absolute paths only.
@@ -29,45 +30,41 @@ You are a read-only codebase explorer. You output concise, evidence-backed findi
29
30
 
30
31
  Find relevant files, symbols, and usage paths quickly for the caller.
31
32
 
32
- ## Skills
33
+ ## Tools — Use These for Local Code Search
33
34
 
34
- Always load:
35
+ | Tool | Use For | Example |
36
+ |------|---------|--------|
37
+ | `grep` | Find text/regex patterns in files | `grep(pattern: "PatchEntry", include: "*.ts")` |
38
+ | `glob` | Find files by name/pattern | `glob(pattern: "src/**/*.ts")` |
39
+ | `lsp` (goToDefinition) | Jump to symbol definition | `lsp(operation: "goToDefinition", filePath: "...", line: N, character: N)` |
40
+ | `lsp` (findReferences) | Find all usages of a symbol | `lsp(operation: "findReferences", ...)` |
41
+ | `lsp` (hover) | Get type info and docs | `lsp(operation: "hover", ...)` |
42
+ | `read` | Read file content | `read(filePath: "src/utils/patch.ts")` |
35
43
 
36
- ```typescript
37
- skill({ name: "code-navigation" });
38
- ```
44
+ **NEVER** use `websearch`, `webfetch`, or `codesearch` — those search the internet, not your project.
39
45
 
40
46
  ## Rules
41
47
 
42
48
  - Never modify files — read-only is a hard constraint
43
49
  - Return absolute paths in final output
44
50
  - Cite `file:line` evidence whenever possible
45
- - Prefer semantic lookup (LSP) before broad text search when it improves precision
46
- - Stop when you can answer with concrete evidence or when additional search only repeats confirmed paths
51
+ - **Always start with `grep` or `glob`** to locate files and symbols — do NOT read directories to browse
52
+ - Use LSP for precise navigation after finding candidate locations
53
+ - Stop when you can answer with concrete evidence
47
54
 
48
- ## Before You Explore
55
+ ## Navigation Patterns
49
56
 
50
- - **Be certain**: Only explore what's needed for the task at hand
51
- - **Don't over-explore**: Stop when you have enough evidence to answer
52
- - **Use LSP first**: Start with goToDefinition/findReferences before grep
53
- - **Stay scoped**: Don't explore files outside the task scope
54
- - **Cite evidence**: Every finding needs file:line reference
57
+ 1. **Search first, read second**: `grep` to find where a symbol lives, then `read` only that section
58
+ 2. **Don't re-read**: If you already read a file, reference what you learned don't read it again
59
+ 3. **Follow the chain**: definition usages callers via LSP findReferences
60
+ 4. **Target ≤3 tool calls per symbol**: grep read section done
55
61
 
56
62
  ## Workflow
57
63
 
58
- 1. Discover candidate files with `glob` or `workspaceSymbol`
59
- 2. Validate symbol flow with LSP (`goToDefinition`, `findReferences`)
60
- 3. Use `grep` for targeted pattern checks
61
- 4. Read only relevant sections
62
- 5. Return findings + next steps
63
-
64
- ## Thoroughness Levels
65
-
66
- | Level | Scope | Use When |
67
- | --------------- | ----------------------------- | ------------------------------------------ |
68
- | `quick` | 1-3 files, direct answer | Simple lookups, known symbol names |
69
- | `medium` | 3-6 files, include call paths | Understanding feature flow |
70
- | `very thorough` | Dependency map + edge cases | Complex refactor prep, architecture review |
64
+ 1. `grep` or `glob` to discover candidate files
65
+ 2. `lsp` goToDefinition/findReferences for precise symbol navigation
66
+ 3. `read` only the relevant sections (use offset/limit)
67
+ 4. Return findings with file:line evidence
71
68
 
72
69
  ## Output
73
70
 
@@ -75,13 +75,15 @@ ls .beads/artifacts/$ARGUMENTS/
75
75
 
76
76
  If `plan.md` exists with dependency graph:
77
77
 
78
- 1. **Parse waves** from plan header (Wave 1, Wave 2, etc.)
79
- 2. **Execute Wave 1** (independent tasks) in parallel using `task()` subagents
80
- 3. **Wait for Wave 1 completion** — all tasks pass or report failures
81
- 4. **Execute Wave 2** (depends on Wave 1) in parallel
78
+ 1. **Load skill:** `skill({ name: "executing-plans" })`
79
+ 2. **Parse waves** from dependency graph section
80
+ 3. **Execute wave-by-wave:**
81
+ - Single-task wave execute directly (no subagent overhead)
82
+ - Multi-task wave → dispatch parallel `task({ subagent_type: "general" })` subagents, one per task
83
+ 4. **Review after each wave** — run verification gates, report, wait for feedback
82
84
  5. **Continue** until all waves complete
83
85
 
84
- **Parallel safety:** Only tasks within same wave run in parallel. Tasks in Wave N+1 wait for Wave N.
86
+ **Parallel safety:** Only tasks within same wave run in parallel. Tasks must NOT share files. Tasks in Wave N+1 wait for Wave N.
85
87
 
86
88
  ### Phase 3A: PRD Task Loop (Sequential Fallback)
87
89
 
Binary file
@@ -1,29 +1,31 @@
1
1
  ---
2
2
  name: executing-plans
3
- description: Use when partner provides a complete implementation plan to execute in controlled batches with review checkpoints - loads plan, reviews critically, executes tasks in batches, reports for review between batches
4
- version: 1.0.0
5
- tags: [workflow, planning]
3
+ description: Use when a complete implementation plan exists parses dependency waves, executes independent tasks in parallel via subagents, runs review checkpoints between waves
4
+ version: 2.0.0
5
+ tags: [workflow, planning, parallel]
6
6
  dependencies: [writing-plans]
7
7
  ---
8
8
 
9
9
  # Executing Plans
10
10
 
11
- > **Replaces** unstructured implementation where the agent jumps between tasks without review checkpoints or batch control
11
+ > **Replaces** sequential task-by-task implementation detects parallelizable waves and dispatches subagents concurrently within each wave
12
+
12
13
  ## When to Use
13
14
 
14
- - A complete implementation plan exists and you need to execute it in batches with checkpoints
15
- - You must follow a plan precisely and report between waves for feedback
15
+ - A complete implementation plan exists (`.beads/artifacts/<id>/plan.md` or provided directly)
16
+ - The plan has a dependency graph with wave assignments from `/plan`
16
17
 
17
18
  ## When NOT to Use
18
19
 
19
- - There is no plan yet or requirements are still unclear
20
- - You need to create the plan or tasks first (use writing-plans or prd)
20
+ - No plan yet (use `writing-plans` or `prd` first)
21
+ - All tasks are tightly sequential with no parallelism opportunity
22
+ - Fewer than 3 tasks (just execute directly, overhead not worth it)
21
23
 
22
24
  ## Overview
23
25
 
24
- Load plan, review critically, execute tasks in batches, report for review between batches.
26
+ Load plan parse dependency waves → execute each wave (parallel within, sequential between) review after each wave → next wave.
25
27
 
26
- **Core principle:** Batch execution with checkpoints for architect review.
28
+ **Core principle:** Parallel within waves, sequential between waves, review at wave boundaries.
27
29
 
28
30
  **Announce at start:** "I'm using the executing-plans skill to implement this plan."
29
31
 
@@ -36,104 +38,149 @@ Load plan, review critically, execute tasks in batches, report for review betwee
36
38
  - [ ] Read the plan file end-to-end
37
39
  - [ ] Identify goal, deliverables, risks, and missing pieces
38
40
  - [ ] If concerns, ask via `question()` and wait for decision
39
- - [ ] If no concerns, create TodoWrite and proceed
41
+ - [ ] If no concerns, proceed to wave parsing
40
42
 
41
43
  1. Read plan file
42
- 2. Review critically - identify any questions or concerns about the plan
43
- 3. If concerns: Use question tool to raise them:
44
+ 2. Review critically identify any questions or concerns
45
+ 3. If concerns: raise them with `question()` tool
46
+ 4. If no concerns: proceed
44
47
 
45
- ```typescript
46
- question({
47
- questions: [
48
- {
49
- header: "Concerns",
50
- question: "Plan review complete. Any concerns before proceeding?",
51
- options: [
52
- {
53
- label: "No concerns (Recommended)",
54
- description: "Plan looks good, execute batches",
55
- },
56
- {
57
- label: "Has concerns",
58
- description: "Need clarification before starting",
59
- },
60
- ],
61
- },
62
- ],
63
- });
64
- ```
48
+ ### Step 2: Parse Dependency Graph
65
49
 
66
- 4. Read plan and identify:
67
- - What is the goal?
68
- - What are the deliverables?
69
- - What are the risks?
70
- - Does the approach make sense?
71
- - Are there missing pieces?
50
+ Look for the dependency graph section in the plan. The `/plan` command generates this format:
72
51
 
73
- If no concerns: Create TodoWrite and proceed
74
- If concerns: Wait for human to decide and resubmit
52
+ ```
53
+ ## Dependency Graph
75
54
 
76
- ### Step 2: Execute Batch
55
+ Task A: needs nothing, creates src/models/X.ts
56
+ Task B: needs Task A, creates src/api/X.ts
57
+ Task C: needs nothing, creates src/utils/Y.ts
58
+ Task D: needs Task B + Task C, creates src/routes/Z.ts
77
59
 
78
- **Default: First 3 tasks**
60
+ Wave 1: A, C (independent)
61
+ Wave 2: B (depends on A)
62
+ Wave 3: D (depends on B, C)
63
+ ```
79
64
 
80
- **Before starting a batch**: create a wave-start git tag for safe rollback:
65
+ **Extract:**
66
+ - Which tasks belong to each wave
67
+ - Which files each task modifies (for conflict detection)
68
+ - Dependencies between tasks
81
69
 
82
- ```bash
83
- # Tag the safe point before this batch/wave
84
- git tag wave-${BATCH_NUMBER}-start
85
- ```
70
+ **If no dependency graph found:** Fall back to sequential execution (batch of 3 tasks).
86
71
 
87
- #### Batch Execution Checklist
72
+ **File conflict check:** Tasks in the same wave MUST NOT modify the same files. If they do, move one to the next wave.
88
73
 
89
- - [ ] Create wave start tag: `git tag wave-${BATCH_NUMBER}-start`
90
- - [ ] Mark each task in_progress
91
- - [ ] Follow each step exactly as written
92
- - [ ] Run all specified verifications
93
- - [ ] Mark tasks completed
94
- - [ ] Create wave complete tag: `git tag wave-${BATCH_NUMBER}-complete`
74
+ ### Step 3: Create TodoWrite
95
75
 
96
- For each task:
76
+ Create todos for all tasks, grouped by wave:
97
77
 
98
- 1. Mark as in_progress
99
- 2. Follow each step exactly (plan has bite-sized steps)
100
- 3. Run verifications as specified
101
- 4. Mark as completed
78
+ ```typescript
79
+ todowrite({
80
+ todos: [
81
+ { content: "Wave 1: Task A — [description]", status: "pending", priority: "high" },
82
+ { content: "Wave 1: Task C — [description]", status: "pending", priority: "high" },
83
+ { content: "Wave 1 review checkpoint", status: "pending", priority: "high" },
84
+ { content: "Wave 2: Task B — [description]", status: "pending", priority: "high" },
85
+ { content: "Wave 2 review checkpoint", status: "pending", priority: "high" },
86
+ // ...
87
+ ]
88
+ });
89
+ ```
90
+
91
+ ### Step 4: Execute Wave
102
92
 
103
- **After batch passes all gates**: create a wave-complete tag:
93
+ **Before starting a wave:** create a git tag for safe rollback:
104
94
 
105
95
  ```bash
106
- # Seal the completed wave - confirms all gates passed
107
- git tag wave-${BATCH_NUMBER}-complete
96
+ git tag wave-${WAVE_NUMBER}-start
108
97
  ```
109
98
 
110
- ### Step 3: Report
99
+ #### Single-task wave (no parallelism needed)
111
100
 
112
- #### Batch Report Checklist
101
+ Execute directly in the current agent context. No subagent overhead.
113
102
 
114
- - [ ] Summarize what was implemented
115
- - [ ] Include verification output
116
- - [ ] Confirm wave tag created (e.g., `wave-1-complete`)
117
- - [ ] Ask for feedback before continuing
103
+ #### Multi-task wave (2+ independent tasks)
118
104
 
119
- When batch complete:
105
+ Dispatch parallel subagents — one per task:
120
106
 
121
- - Show what was implemented
122
- - Show verification output
123
- - Show wave tag created (e.g., `wave-1-complete`)
124
- - Say: "Ready for feedback."
107
+ ```typescript
108
+ // Dispatch all tasks in this wave simultaneously
109
+ task({
110
+ subagent_type: "general",
111
+ description: `Wave ${N}: Task A — ${taskTitle}`,
112
+ prompt: `You are implementing Task A from the plan.
113
+
114
+ ## Task
115
+ ${taskDescription}
116
+
117
+ ## Files to modify
118
+ ${taskFiles.join('\n')}
119
+
120
+ ## Constraints
121
+ - ONLY modify files listed above
122
+ - Follow each step exactly as written in the task
123
+ - Run verification commands specified in the task
124
+ - Commit your changes: git add <specific-files> && git commit -m "feat: ${taskTitle}"
125
+
126
+ ## Report back
127
+ - What you implemented
128
+ - Files changed
129
+ - Verification results (pass/fail)
130
+ - Commit hash
131
+ - Any issues or blockers`
132
+ });
133
+ // ...dispatch other tasks in this wave simultaneously
134
+ ```
125
135
 
126
- ### Step 4: Continue
136
+ **Critical rules for parallel dispatch:**
137
+
138
+ | Rule | Why |
139
+ | --- | --- |
140
+ | Non-overlapping files | Subagents editing same file = merge conflicts |
141
+ | Exact file list per subagent | Prevents scope creep into other tasks |
142
+ | Each subagent commits independently | Clean git history per task |
143
+ | Never `git add .` | Only stage files from this task |
144
+
145
+ #### Wave Execution Checklist
146
+
147
+ - [ ] Create wave start tag: `git tag wave-${WAVE_NUMBER}-start`
148
+ - [ ] Dispatch subagents for all tasks in this wave (parallel)
149
+ - [ ] Collect results from all subagents
150
+ - [ ] Check for failures — if any task failed, stop and report
151
+ - [ ] Run verification gates (typecheck + lint in parallel, then tests)
152
+ - [ ] Create wave complete tag: `git tag wave-${WAVE_NUMBER}-complete`
153
+ - [ ] Mark wave tasks as completed in TodoWrite
154
+
155
+ ### Step 5: Review Wave
156
+
157
+ After each wave completes:
158
+
159
+ 1. **Synthesize results** from all subagents
160
+ 2. **Run verification gates** on the combined changes:
161
+ ```bash
162
+ # Parallel: typecheck + lint
163
+ npm run typecheck & npm run lint & wait
164
+ # Sequential: tests
165
+ npm test
166
+ ```
167
+ 3. **Report to user:**
168
+ - Tasks completed in this wave
169
+ - Verification results
170
+ - Wave tag created
171
+ - Any issues found
172
+ 4. **Wait for feedback** before proceeding to next wave
173
+
174
+ ### Step 6: Next Wave
127
175
 
128
176
  Based on feedback:
177
+ - Apply corrections if needed
178
+ - Execute next wave (repeat Steps 4-5)
179
+ - Continue until all waves complete
129
180
 
130
- - Apply changes if needed
131
- - Execute next batch
132
- - Repeat until complete
133
-
134
- ### Step 5: Complete Development
181
+ ### Step 7: Complete Development
135
182
 
136
- After all tasks complete and verified:
183
+ After all waves complete and verified:
137
184
 
138
185
  - Announce: "I'm using finishing-a-development-branch skill to complete this work."
139
186
  - **REQUIRED SUB-SKILL:** Use skill({ name: "finishing-a-development-branch" })
@@ -147,84 +194,54 @@ Git tags act as checkpoints between waves. If a wave fails irrecoverably, roll b
147
194
 
148
195
  | When | Command | Purpose |
149
196
  | ---------------------------- | ------------------------------- | ------------------------- |
150
- | Before starting any batch | `git tag wave-N-start` | Mark rollback point |
151
- | After batch passes all gates | `git tag wave-N-complete` | Seal confirmed-good state |
197
+ | Before starting any wave | `git tag wave-N-start` | Mark rollback point |
198
+ | After wave passes all gates | `git tag wave-N-complete` | Seal confirmed-good state |
152
199
  | On irrecoverable failure | `git reset --hard wave-N-start` | Restore to pre-wave state |
153
200
  | Listing all wave checkpoints | `git tag --list "wave-*"` | Audit trail of execution |
154
201
 
155
202
  ### When to Rollback
156
203
 
157
204
  Roll back (with user confirmation) when:
158
-
159
- - Build gates fail twice consecutively in the same wave
160
- - Unexpected destructive changes were made
161
- - Drift check detects unrecoverable scope creep
205
+ - Verification gates fail twice consecutively in the same wave
206
+ - Subagent made destructive changes outside its file scope
162
207
  - Tests were broken and the cause is unclear
163
208
 
164
- **Always ask the user before running `git reset --hard`** - it discards uncommitted changes irreversibly.
165
-
166
- ### Rollback Steps
167
-
168
- ```bash
169
- # 1. Identify safe point
170
- git tag --list "wave-*"
171
- # e.g.: wave-1-complete wave-2-start wave-2-complete wave-3-start
172
-
173
- # 2. Confirm with user: rollback to which tag?
174
- # e.g.: git reset --hard wave-2-complete (last known good)
209
+ **Always ask the user before running `git reset --hard`** it discards uncommitted changes irreversibly.
175
210
 
176
- # 3. Execute rollback (ONLY after user confirms)
177
- git reset --hard wave-2-complete
211
+ ## Sequential Fallback
178
212
 
179
- # 4. Verify state is clean
180
- npm run typecheck && npm run lint
213
+ If the plan has no dependency graph or waves:
181
214
 
182
- # 5. Re-plan the failed batch with new approach
183
- ```
215
+ 1. Group tasks into batches of 3
216
+ 2. Execute each batch sequentially (no parallel subagents)
217
+ 3. Review between batches
218
+ 4. Same wave-tag protocol applies
184
219
 
185
- ### Tag Naming Convention
186
-
187
- ```
188
- wave-1-start # Before batch 1 starts
189
- wave-1-complete # After batch 1 passes all gates
190
- wave-2-start # Before batch 2 starts
191
- wave-2-complete # After batch 2 passes all gates
192
- ...
193
- ```
194
-
195
- Use numeric batch numbers, not task names, for predictable reference.
220
+ This preserves backward compatibility with plans that don't have wave assignments.
196
221
 
197
222
  ## When to Stop and Ask for Help
198
223
 
199
224
  **STOP executing immediately when:**
200
-
201
- - Hit a blocker mid-batch (missing dependency, test fails, instruction unclear)
225
+ - Subagent reports a blocker or failure
226
+ - File conflict detected between parallel tasks
227
+ - Verification fails twice in the same wave
202
228
  - Plan has critical gaps preventing starting
203
- - You don't understand an instruction
204
- - Verification fails repeatedly
205
229
 
206
230
  **Ask for clarification rather than guessing.**
207
231
 
208
- ## When to Revisit Earlier Steps
209
-
210
- **Return to Review (Step 1) when:**
211
-
212
- - Partner updates the plan based on your feedback
213
- - Fundamental approach needs rethinking
214
-
215
- **Don't force through blockers** - stop and ask.
216
-
217
- ## Remember
232
+ ## Anti-Patterns
218
233
 
219
- - Review plan critically first
220
- - Follow plan steps exactly
221
- - Don't skip verifications
222
- - Reference skills when plan says to
223
- - Between batches: just report and wait
224
- - Stop when blocked, don't guess
234
+ | Anti-Pattern | Why It Fails | Instead |
235
+ | --- | --- | --- |
236
+ | Dispatching parallel subagents for tasks that share files | Edit conflicts, lost changes, merge chaos | Move conflicting tasks to separate waves |
237
+ | Skipping verification between waves | Broken code compounds across waves | Run all gates after each wave before proceeding |
238
+ | Giving subagents the full plan instead of their task | Context pollution, scope creep | Extract only the specific task + file list |
239
+ | Running all tasks in one wave regardless of dependencies | Later tasks fail because prerequisites aren't ready | Respect the dependency graph strictly |
240
+ | Not committing per-task | Can't rollback individual tasks, messy git history | Each subagent commits its own changes |
225
241
 
226
242
  ## See Also
227
243
 
228
- - `writing-plans` - Create detailed, zero-ambiguity implementation plans before execution
229
- - `swarm-coordination` - Coordinate parallel execution when many independent tasks can run concurrently
230
- - `verification-before-completion` - Run final verification gates before claiming completion
244
+ - `writing-plans` Create detailed, zero-ambiguity implementation plans before execution
245
+ - `swarm-coordination` For 10+ task scenarios with full PARL orchestration
246
+ - `subagent-driven-development` For sequential per-task execution with review between each
247
+ - `verification-before-completion` — Run final verification gates before claiming completion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencodekit",
3
- "version": "0.18.5",
3
+ "version": "0.18.6",
4
4
  "description": "CLI tool for bootstrapping and managing OpenCodeKit projects",
5
5
  "keywords": [
6
6
  "agents",