create-harness-vibe-coding 0.8.7 → 0.8.9

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 (126) hide show
  1. package/README-CN.md +163 -105
  2. package/README.md +179 -244
  3. package/bin/create-harness-vibe-coding.js +2 -2
  4. package/docs/images/harness-architecture-light.png +0 -0
  5. package/docs/images/harness-architecture.drawio +164 -0
  6. package/docs/images/harness-icon.png +0 -0
  7. package/package.json +47 -44
  8. package/src/generator.js +41 -5
  9. package/src/index.js +86 -13
  10. package/src/prompts.js +37 -37
  11. package/templates/common/.claude/agents/architect-manager.md +45 -45
  12. package/templates/common/.claude/agents/architect.md +31 -31
  13. package/templates/common/.claude/agents/codebase-explorer.md +45 -0
  14. package/templates/common/.claude/agents/context-master.md +75 -75
  15. package/templates/common/.claude/agents/debugger.md +41 -41
  16. package/templates/common/.claude/agents/docs-researcher.md +41 -41
  17. package/templates/common/.claude/agents/explore-manager.md +41 -41
  18. package/templates/common/.claude/agents/implement-manager.md +49 -49
  19. package/templates/common/.claude/agents/implementer.md +40 -40
  20. package/templates/common/.claude/agents/memory-master.md +82 -64
  21. package/templates/common/.claude/agents/planner.md +34 -34
  22. package/templates/common/.claude/agents/researcher.md +41 -41
  23. package/templates/common/.claude/agents/review-manager.md +56 -56
  24. package/templates/common/.claude/agents/reviewer.md +34 -34
  25. package/templates/common/.claude/agents/task-scribe.md +70 -0
  26. package/templates/common/.claude/agents/verifier.md +29 -29
  27. package/templates/common/.claude/commands/wf-help.md +9 -5
  28. package/templates/common/.claude/commands/wf-update.md +24 -0
  29. package/templates/common/.claude/rules/ecc/common.md +57 -44
  30. package/templates/common/.claude/settings.json +13 -0
  31. package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +8 -4
  32. package/templates/common/.claude/skills/wf/SKILL.md +15 -8
  33. package/templates/common/.claude/skills/wf-auto/SKILL.md +10 -7
  34. package/templates/common/.claude/skills/wf-learn/SKILL.md +9 -2
  35. package/templates/common/.claude/skills/wf-max/SKILL.md +23 -5
  36. package/templates/common/.claude/skills/wf-readme/SKILL.md +49 -49
  37. package/templates/common/.claude/skills/wf-remove/SKILL.md +7 -7
  38. package/templates/common/.claude/skills/wf-update/SKILL.md +15 -2
  39. package/templates/common/.codex/hooks.json +17 -0
  40. package/templates/common/.harness-version +130 -45
  41. package/templates/common/.opencode/agents/architect-manager.md +52 -0
  42. package/templates/common/.opencode/agents/architect.md +35 -0
  43. package/templates/common/.opencode/agents/codebase-explorer.md +45 -0
  44. package/templates/common/.opencode/agents/context-master.md +81 -0
  45. package/templates/common/.opencode/agents/debugger.md +43 -0
  46. package/templates/common/.opencode/agents/docs-researcher.md +42 -0
  47. package/templates/common/.opencode/agents/explore-manager.md +49 -0
  48. package/templates/common/.opencode/agents/implement-manager.md +56 -0
  49. package/templates/common/.opencode/agents/implementer.md +42 -0
  50. package/templates/common/.opencode/agents/memory-master.md +88 -0
  51. package/templates/common/.opencode/agents/planner.md +38 -0
  52. package/templates/common/.opencode/agents/reflector.md +39 -0
  53. package/templates/common/.opencode/agents/researcher.md +42 -0
  54. package/templates/common/.opencode/agents/review-manager.md +63 -0
  55. package/templates/common/.opencode/agents/reviewer.md +37 -0
  56. package/templates/common/.opencode/agents/task-scribe.md +70 -0
  57. package/templates/common/.opencode/agents/tdd-guide.md +83 -0
  58. package/templates/common/.opencode/agents/test-writer.md +54 -0
  59. package/templates/common/.opencode/agents/verifier.md +37 -0
  60. package/templates/common/.opencode/commands/wf-auto-spark.md +15 -0
  61. package/templates/common/.opencode/commands/wf-auto.md +15 -0
  62. package/templates/common/.opencode/commands/wf-help.md +27 -0
  63. package/templates/common/.opencode/commands/wf-learn.md +15 -0
  64. package/templates/common/.opencode/commands/wf-max.md +15 -0
  65. package/templates/common/.opencode/commands/wf-readme.md +15 -0
  66. package/templates/common/.opencode/commands/wf-remove.md +15 -0
  67. package/templates/common/.opencode/commands/wf-review.md +15 -0
  68. package/templates/common/.opencode/commands/wf-update.md +24 -0
  69. package/templates/common/.opencode/commands/wf.md +15 -0
  70. package/templates/common/.opencode/plugins/harness-wf-status.mjs +135 -0
  71. package/templates/common/AGENTS.md +2 -29
  72. package/templates/common/CLAUDE.md +114 -88
  73. package/templates/common/Harness/ACCEPTANCE_PROTOCOL.md +2 -2
  74. package/templates/common/{MEMORY.md → Harness/MEMORY.md} +17 -4
  75. package/templates/common/Harness/MEMORY_PROTOCOL.md +80 -30
  76. package/templates/common/Harness/PROGRESS.md +17 -17
  77. package/templates/common/Harness/README.md +58 -19
  78. package/templates/common/{SETUP.md → Harness/SETUP.md} +278 -276
  79. package/templates/common/Harness/TASK_ARCHIVE.md +56 -0
  80. package/templates/common/Harness/WF-AUTO-ANGLES.md +170 -0
  81. package/templates/common/Harness/WF-AUTO-SPARK.md +10 -19
  82. package/templates/common/Harness/WF-AUTO.md +93 -167
  83. package/templates/common/Harness/WF-KERNEL.md +189 -0
  84. package/templates/common/Harness/WF-MAX.md +60 -328
  85. package/templates/common/Harness/WF-STATE.md +83 -0
  86. package/templates/common/Harness/WF.md +117 -237
  87. package/templates/common/Harness/agent-workflow.md +2 -2
  88. package/templates/common/Harness/architecture.md +124 -124
  89. package/templates/common/Harness/context-loading.md +111 -111
  90. package/templates/common/Harness/dispatch.md +43 -35
  91. package/templates/common/Harness/extension.md +66 -66
  92. package/templates/common/Harness/lifecycle.md +20 -20
  93. package/templates/common/Harness/research/PRD.md +56 -56
  94. package/templates/common/Harness/research/README.md +169 -169
  95. package/templates/common/Harness/research/research-results.md +66 -66
  96. package/templates/common/Harness/scripts/archive-tasks.mjs +239 -0
  97. package/templates/common/{scripts → Harness/scripts}/scan-clean.mjs +443 -416
  98. package/templates/common/{scripts → Harness/scripts}/validate-harness.mjs +691 -452
  99. package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +258 -0
  100. package/templates/common/{scripts → Harness/scripts}/wf-remove.mjs +56 -39
  101. package/templates/common/{scripts → Harness/scripts}/wf-update-check.mjs +632 -599
  102. package/templates/common/Harness/subagents.md +215 -214
  103. package/templates/common/Harness/tasks/_template/ARTIFACTS.md +2 -2
  104. package/templates/common/Harness/tasks/_template/NOTES.md +2 -2
  105. package/templates/common/Harness/tasks/_template/PLAN.md +5 -0
  106. package/templates/common/Harness/tasks/_template/STATE.json +23 -0
  107. package/templates/common/README.md +37 -37
  108. package/templates/common/memory/agent-lessons-patterns.md +22 -21
  109. package/templates/common/memory/routes.md +43 -0
  110. package/templates/common/memory/startup-hints.md +32 -0
  111. package/templates/common/memory/tool-usage-reflections.md +22 -21
  112. package/templates/common/memory/user-corrections-preferences.md +23 -21
  113. package/templates/common/opencode.json +19 -0
  114. package/templates/optional/catalog.json +49 -33
  115. package/templates/optional/skills/browser-e2e/.claude/skills/browser-e2e/SKILL.md +42 -42
  116. package/templates/optional/skills/browser-e2e/.claude/skills/wf-browser/SKILL.md +193 -193
  117. package/templates/optional/skills/browser-e2e/.opencode/commands/wf-browser.md +15 -0
  118. package/templates/optional/skills/browser-e2e/Harness/workflows/browser-e2e.md +48 -48
  119. package/templates/optional/skills/github-pr-review/.claude/skills/github-pr-review/SKILL.md +40 -40
  120. package/templates/optional/skills/github-pr-review/Harness/workflows/github-pr-review.md +28 -28
  121. package/templates/optional/skills/python-backend/.claude/skills/python-backend/SKILL.md +40 -40
  122. package/templates/optional/skills/python-backend/Harness/workflows/python-backend.md +34 -34
  123. package/templates/optional/skills/ts-react-frontend/.claude/skills/ts-react-frontend/SKILL.md +43 -43
  124. package/templates/optional/skills/ts-react-frontend/Harness/workflows/ts-react-frontend.md +34 -34
  125. package/templates/optional/skills/ui-ux-review/.claude/skills/ui-ux-review/SKILL.md +40 -40
  126. package/templates/optional/skills/ui-ux-review/Harness/workflows/ui-ux-review.md +26 -26
Binary file
package/package.json CHANGED
@@ -1,44 +1,47 @@
1
- {
2
- "name": "create-harness-vibe-coding",
3
- "version": "0.8.7",
4
- "description": "Scaffold a 0-1 product harness for AI-assisted research, PRD, planning, architecture, build, test, and feedback loops",
5
- "type": "module",
6
- "bin": {
7
- "create-harness-vibe-coding": "bin/create-harness-vibe-coding.js"
8
- },
9
- "files": [
10
- "bin/",
11
- "src/",
12
- "templates/",
13
- "README-CN.md"
14
- ],
15
- "scripts": {
16
- "start": "node src/index.js",
17
- "build:version": "node scripts/build-version.mjs",
18
- "prepush": "node scripts/pre-push-check.mjs",
19
- "test": "node --test tests/*.test.js",
20
- "test:smoke": "node --test tests/cli-smoke.test.js",
21
- "pack:smoke": "node --test tests/pack-smoke.test.js"
22
- },
23
- "dependencies": {
24
- "@clack/prompts": "^0.7.0",
25
- "picocolors": "^1.1.0"
26
- },
27
- "engines": {
28
- "node": ">=18"
29
- },
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/zingspark/create-harness-vibe-coding.git"
33
- },
34
- "keywords": [
35
- "claude-code",
36
- "harness",
37
- "scaffold",
38
- "vibe-coding",
39
- "agentic",
40
- "ecc",
41
- "superpowers"
42
- ],
43
- "license": "MIT"
44
- }
1
+ {
2
+ "name": "create-harness-vibe-coding",
3
+ "version": "0.8.9",
4
+ "description": "Scaffold a 0-1 product harness for AI-assisted research, PRD, planning, architecture, build, test, and feedback loops",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-harness-vibe-coding": "bin/create-harness-vibe-coding.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "src/",
12
+ "templates/",
13
+ "docs/images/harness-icon.png",
14
+ "docs/images/harness-architecture-light.png",
15
+ "docs/images/harness-architecture.drawio",
16
+ "README-CN.md"
17
+ ],
18
+ "scripts": {
19
+ "start": "node src/index.js",
20
+ "build:version": "node scripts/build-version.mjs",
21
+ "prepush": "node scripts/pre-push-check.mjs",
22
+ "test": "node --test tests/*.test.js",
23
+ "test:smoke": "node --test tests/cli-smoke.test.js",
24
+ "pack:smoke": "node --test tests/pack-smoke.test.js"
25
+ },
26
+ "dependencies": {
27
+ "@clack/prompts": "^0.7.0",
28
+ "picocolors": "^1.1.0"
29
+ },
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/zingspark/create-harness-vibe-coding.git"
36
+ },
37
+ "keywords": [
38
+ "claude-code",
39
+ "harness",
40
+ "scaffold",
41
+ "vibe-coding",
42
+ "agentic",
43
+ "ecc",
44
+ "superpowers"
45
+ ],
46
+ "license": "MIT"
47
+ }
package/src/generator.js CHANGED
@@ -254,6 +254,28 @@ function duplicateDests(fileSpecs) {
254
254
  return [...duplicates].sort();
255
255
  }
256
256
 
257
+ /**
258
+ * Walk the directory chain from rootDir down to the parent of absPath.
259
+ * Returns the first ancestor that is a symbolic link, or null if none.
260
+ * Prevents writes from escaping the workspace via symlinked parent dirs
261
+ * (e.g. .claude/, Harness/, .opencode/ pointing outside the project).
262
+ */
263
+ function symlinkInChain(rootDir, absPath) {
264
+ const root = path.resolve(rootDir);
265
+ const target = path.resolve(absPath);
266
+ const rel = path.relative(root, target);
267
+ if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return null;
268
+ const parts = rel.split(path.sep).filter(Boolean);
269
+ let cur = root;
270
+ for (let i = 0; i < parts.length - 1; i += 1) {
271
+ cur = path.join(cur, parts[i]);
272
+ try {
273
+ if (fs.lstatSync(cur).isSymbolicLink()) return cur;
274
+ } catch (_) { /* directory not created yet */ }
275
+ }
276
+ return null;
277
+ }
278
+
257
279
  function createPlan(resolvedDir, fileSpecs) {
258
280
  const plan = {
259
281
  create: [],
@@ -381,11 +403,11 @@ function registerOptionalContent(file, content, selectedSkills) {
381
403
  return `${next.trimEnd()}\n\n## Installed Optional Workflows\n\n${lines.join('\n')}\n`;
382
404
  }
383
405
 
384
- if (file === '.claude/commands/wf-help.md' && hasBrowserE2e) {
385
- return content.replace(
386
- '| `/wf-readme <task>` |',
387
- '| `/wf-browser <task>` | optional workflow skill | `/wf-browser verify checkout flow` | Browser automation/E2E workflow with real UI interaction, screenshots, traces, and CDP/network evidence. |\n| `/wf-readme <task>` |',
388
- );
406
+ if ((file === '.claude/commands/wf-help.md' || file === '.opencode/commands/wf-help.md') && hasBrowserE2e) {
407
+ return content.replace(
408
+ '| `/wf-readme <task>` |',
409
+ '| `/wf-browser <task>` | optional workflow skill | `/wf-browser verify checkout flow` | Browser automation/E2E workflow with real UI interaction, screenshots, traces, and CDP/network evidence. |\n| `/wf-readme <task>` |',
410
+ );
389
411
  }
390
412
 
391
413
  return content;
@@ -528,12 +550,20 @@ export function generate({
528
550
  const dirPath = dir === './'
529
551
  ? resolvedDir
530
552
  : path.join(resolvedDir, ...dir.slice(0, -1).split('/'));
553
+ const __slMkdir = symlinkInChain(resolvedDir, dirPath);
554
+ if (__slMkdir) throw new Error(`Cannot create directory ${dir}: symlinked directory in path (${__slMkdir})`);
531
555
  fs.mkdirSync(dirPath, { recursive: true });
532
556
  created.push(dir === './' ? './ (directory)' : `${dir} (directory)`);
533
557
  }
534
558
 
535
559
  for (const file of plan.backup) {
536
560
  const destPath = path.join(resolvedDir, ...file.split('/'));
561
+ const __slBackup = symlinkInChain(resolvedDir, destPath);
562
+ if (__slBackup) throw new Error(`Cannot backup ${file}: symlinked directory in path (${__slBackup})`);
563
+ // Symlink safety: never rename or follow symlinks to avoid path traversal
564
+ if (fs.existsSync(destPath)) {
565
+ try { if (fs.lstatSync(destPath).isSymbolicLink()) throw new Error('Symlink rejected'); } catch (e) { throw new Error(`Cannot backup ${file}: ${e.message}`); }
566
+ }
537
567
  fs.renameSync(destPath, nextBackupPath(destPath));
538
568
  }
539
569
 
@@ -554,6 +584,12 @@ export function generate({
554
584
  }
555
585
  const spec = specsByDest.get(file);
556
586
  const destPath = path.join(resolvedDir, ...file.split('/'));
587
+ const __slWrite = symlinkInChain(resolvedDir, destPath);
588
+ if (__slWrite) throw new Error(`Cannot create/overwrite ${file}: symlinked directory in path (${__slWrite})`);
589
+ // Symlink safety: reject symlinks before writing to prevent path traversal outside workspace
590
+ if (fs.existsSync(destPath)) {
591
+ try { if (fs.lstatSync(destPath).isSymbolicLink()) throw new Error('Symlink rejected'); } catch (e) { throw new Error(`Cannot create/overwrite ${file}: ${e.message}`); }
592
+ }
557
593
  let content = spec.type === 'empty'
558
594
  ? ''
559
595
  : renderTemplate(spec.src, vars);
package/src/index.js CHANGED
@@ -89,6 +89,12 @@ if (generationOptions.json) {
89
89
  const projectName = argName || DEFAULT_NAME;
90
90
  const targetDir = argDir || `./${projectName}`;
91
91
  const scan = scanTarget(targetDir);
92
+ if (needsScaffoldRecovery(scan)) {
93
+ const recovery = createScaffoldRecoveryResult({ projectName, targetDir, options: generationOptions, scan });
94
+ printJsonResult(recovery);
95
+ process.exit(recovery.success ? 0 : 1);
96
+ }
97
+
92
98
  if (scan.hasHarness) {
93
99
  printJsonResult(createUpdateSwitchResult(scan, { json: true }));
94
100
  process.exit(0);
@@ -121,6 +127,10 @@ if (argName || skipPrompts) {
121
127
  targetDir = argDir || `./${projectName}`;
122
128
  const scan = scanTarget(targetDir);
123
129
 
130
+ if (needsScaffoldRecovery(scan)) {
131
+ process.exit(runScaffoldRecovery({ projectName, targetDir, options: generationOptions, scan }));
132
+ }
133
+
124
134
  if (scan.hasHarness) {
125
135
  process.exit(runUpdateSwitch(scan, { json: false }));
126
136
  }
@@ -128,9 +138,9 @@ if (argName || skipPrompts) {
128
138
  console.log(pc.dim('────────────────────────────────────────────'));
129
139
  console.log(` Project ${pc.green(projectName)}`);
130
140
  console.log(` Directory ${pc.green(targetDir)}`);
131
- console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, tests/')}`);
141
+ console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, .opencode/, opencode.json, tests/')}`);
132
142
  if (generationOptions.dryRun) {
133
- console.log(` Mode ${pc.yellow('dry-run')}`);
143
+ console.log(` Mode ${pc.yellow('dry-run')}`);
134
144
  }
135
145
  console.log(` Conflicts ${pc.cyan(generationOptions.onConflict)}`);
136
146
  if (generationOptions.withOptions.length > 0) {
@@ -172,6 +182,10 @@ if (argName || skipPrompts) {
172
182
  const scan = scanTarget(targetDir);
173
183
  printScan(scan);
174
184
 
185
+ if (needsScaffoldRecovery(scan)) {
186
+ process.exit(runScaffoldRecovery({ projectName, targetDir, options: generationOptions, scan }));
187
+ }
188
+
175
189
  if (scan.hasHarness) {
176
190
  process.exit(runUpdateSwitch(scan, { json: false }));
177
191
  }
@@ -206,7 +220,7 @@ if (argName || skipPrompts) {
206
220
  console.log(pc.dim('────────────────────────────────────────────'));
207
221
  console.log(` Project ${pc.green(projectName)}`);
208
222
  console.log(` Directory ${pc.green(targetDir)}`);
209
- console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, tests/')}`);
223
+ console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, .opencode/, opencode.json, tests/')}`);
210
224
  console.log(` Conflicts ${pc.cyan(generationOptions.onConflict)}`);
211
225
  if (generationOptions.withOptions.length > 0) {
212
226
  console.log(` Optional ${pc.cyan(generationOptions.withOptions.join(','))}`);
@@ -276,8 +290,9 @@ function printResult(result, targetDir) {
276
290
 
277
291
  console.log(pc.bold('Next steps:'));
278
292
  console.log(` ${pc.cyan(`cd ${targetDir}`)}`);
279
- console.log(` ${pc.cyan('claude')} # Start Claude Code`);
280
- console.log(` ${pc.cyan('codex')} # Or start Codex`);
293
+ console.log(` ${pc.cyan('claude')} # Start Claude Code`);
294
+ console.log(` ${pc.cyan('codex')} # Or start Codex`);
295
+ console.log(` ${pc.cyan('opencode')} # Or start OpenCode`);
281
296
  console.log(` Tell your agent: "${pc.yellow('Read Harness/SETUP.md. Bootstrap this project from idea to first vertical slice.')}"`);
282
297
  console.log('');
283
298
  console.log(pc.dim(' Harness/SETUP.md is temporary. Delete it after initialization.'));
@@ -438,12 +453,55 @@ function printWarnings(result) {
438
453
  }
439
454
 
440
455
  function printJsonResult(result) {
441
- // Remove `created` array from output — it is already in the plan, avoid duplication
442
- const { created, ...rest } = result;
456
+ // Remove `created` array from output — it is already in the plan, avoid duplication
457
+ const { created, ...rest } = result;
443
458
  console.log(JSON.stringify(rest, null, 2));
444
459
  if (!result.success) {
445
460
  process.exit(1);
446
- }
461
+ }
462
+ }
463
+
464
+ function updateScriptPath(scan) {
465
+ return path.join(scan.resolvedDir, 'Harness', 'scripts', 'wf-update-check.mjs');
466
+ }
467
+
468
+ function missingRecoveryReasons(scan) {
469
+ const reasons = [];
470
+ if (!fs.existsSync(updateScriptPath(scan))) reasons.push('Harness/scripts/wf-update-check.mjs');
471
+ return reasons;
472
+ }
473
+
474
+ function needsScaffoldRecovery(scan) {
475
+ return scan.hasHarness && missingRecoveryReasons(scan).length > 0;
476
+ }
477
+
478
+ function recoveryOptions(options) {
479
+ return {
480
+ ...options,
481
+ onConflict: 'skip',
482
+ };
483
+ }
484
+
485
+ function createScaffoldRecoveryResult({ projectName, targetDir, options, scan }) {
486
+ const reasons = missingRecoveryReasons(scan);
487
+ const recovery = generate({
488
+ projectName,
489
+ targetDir,
490
+ ...recoveryOptions(options),
491
+ });
492
+ recovery.mode = 'recovery';
493
+ recovery.recoveryNote = `Missing ${reasons.join(' and ')}. Re-ran generate with --on-conflict ${recoveryOptions(options).onConflict} to restore missing Harness infrastructure without overwriting existing files.`;
494
+ return recovery;
495
+ }
496
+
497
+ function runScaffoldRecovery({ projectName, targetDir, options, scan }) {
498
+ const reasons = missingRecoveryReasons(scan);
499
+ console.log('');
500
+ console.log(pc.yellow(`Old Harness detected: missing ${reasons.join(' and ')}. Running safe recovery (--on-conflict ${recoveryOptions(options).onConflict}).`));
501
+ const recovery = createScaffoldRecoveryResult({ projectName, targetDir, options, scan });
502
+ printResult(recovery, targetDir);
503
+ if (recovery.success) console.log(pc.green('Recovery complete. Now run: node Harness/scripts/wf-update-check.mjs'));
504
+ return recovery.success ? 0 : 1;
447
505
  }
448
506
 
449
507
  function runUpdateSwitch(scan, { json }) {
@@ -512,8 +570,19 @@ function createUpdateSwitchResult(scan, { json }) {
512
570
  if (!fs.existsSync(scriptPath)) {
513
571
  return {
514
572
  ...base,
515
- error: 'Existing Harness detected, but Harness/scripts/wf-update-check.mjs was not found. Install writes were skipped; inspect the existing Harness before updating manually.',
573
+ success: false,
574
+ error: 'Existing Harness detected, but Harness/scripts/wf-update-check.mjs was not found. The Harness install is incomplete or was created by an older version that did not include the update checker script. Install writes were skipped — no files were overwritten.',
516
575
  errors: ['Harness/scripts/wf-update-check.mjs not found'],
576
+ agent: {
577
+ ...base.agent,
578
+ next: [{
579
+ action: 'recovery',
580
+ reason: 'This Harness install predates the update-checker script.',
581
+ recoveryPath: 'Run npx create-harness-vibe-coding@latest <project-name> . -y --on-conflict skip --json from the project root. Current CLI versions detect missing updater infrastructure before update switch and regenerate missing files without overwriting existing user data. After recovery, run node Harness/scripts/wf-update-check.mjs --json to check for updates.',
582
+ command: 'npx create-harness-vibe-coding@latest <project-name> . -y --on-conflict skip',
583
+ note: 'The --on-conflict skip policy preserves all existing files and only creates missing ones. You may still need to merge CLAUDE.md changes manually after recovery.',
584
+ }],
585
+ },
517
586
  };
518
587
  }
519
588
 
@@ -564,7 +633,8 @@ function scanTarget(targetDir) {
564
633
  const hasClaude = isDirectory && fs.existsSync(path.join(resolvedDir, 'CLAUDE.md'));
565
634
  const hasAgents = isDirectory && fs.existsSync(path.join(resolvedDir, 'AGENTS.md'));
566
635
  const hasAgentSkills = isDirectory && fs.existsSync(path.join(resolvedDir, '.agents'));
567
- const hasCodex = isDirectory && fs.existsSync(path.join(resolvedDir, '.codex'));
636
+ const hasCodex = isDirectory && fs.existsSync(path.join(resolvedDir, '.codex'));
637
+ const hasOpencode = isDirectory && (fs.existsSync(path.join(resolvedDir, '.opencode')) || fs.existsSync(path.join(resolvedDir, 'opencode.json')));
568
638
  const hasDocs = isDirectory && fs.existsSync(path.join(resolvedDir, 'docs'));
569
639
  const hasReadme = isDirectory && fs.existsSync(path.join(resolvedDir, 'README.md'));
570
640
  const hasPackageJson = isDirectory && fs.existsSync(path.join(resolvedDir, 'package.json'));
@@ -582,7 +652,8 @@ function scanTarget(targetDir) {
582
652
  hasClaude,
583
653
  hasAgents,
584
654
  hasAgentSkills,
585
- hasCodex,
655
+ hasCodex,
656
+ hasOpencode,
586
657
  hasDocs,
587
658
  hasReadme,
588
659
  hasPackageJson,
@@ -610,7 +681,8 @@ function createJsonScan(scan) {
610
681
  hasClaude: scan.hasClaude,
611
682
  hasAgents: scan.hasAgents,
612
683
  hasAgentSkills: scan.hasAgentSkills,
613
- hasCodex: scan.hasCodex,
684
+ hasCodex: scan.hasCodex,
685
+ hasOpencode: scan.hasOpencode,
614
686
  hasDocs: scan.hasDocs,
615
687
  hasReadme: scan.hasReadme,
616
688
  hasPackageJson: scan.hasPackageJson,
@@ -784,6 +856,7 @@ function printScan(scan) {
784
856
  if (scan.hasClaude) console.log(` CLAUDE.md ${pc.yellow('exists')}`);
785
857
  if (scan.hasAgents) console.log(` AGENTS.md ${pc.yellow('exists')}`);
786
858
  if (scan.hasAgentSkills) console.log(` .agents/ ${pc.yellow('exists')}`);
787
- if (scan.hasCodex) console.log(` .codex/ ${pc.yellow('exists')}`);
859
+ if (scan.hasCodex) console.log(` .codex/ ${pc.yellow('exists')}`);
860
+ if (scan.hasOpencode) console.log(` opencode ${pc.yellow('exists')} ${pc.dim('(.opencode/ or opencode.json)')}`);
788
861
  if (scan.hasDocs) console.log(` docs/ ${pc.dim('project-owned; Harness stays in Harness/')}`);
789
862
  }
package/src/prompts.js CHANGED
@@ -1,41 +1,41 @@
1
- import * as p from '@clack/prompts';
2
- import pc from 'picocolors';
3
-
4
- export async function askProjectName() {
5
- const name = await p.text({
6
- message: 'Project name?',
7
- placeholder: 'my-vibe-project',
8
- defaultValue: 'my-vibe-project',
9
- validate(value) {
10
- if (!value.trim()) return 'Project name is required';
11
- if (!/^[a-zA-Z0-9_-]+$/.test(value)) return 'Only letters, numbers, hyphens, and underscores allowed';
12
- return;
13
- },
14
- });
15
-
16
- if (p.isCancel(name)) {
17
- p.cancel('Cancelled');
18
- process.exit(0);
19
- }
20
-
21
- return name.trim();
22
- }
23
-
1
+ import * as p from '@clack/prompts';
2
+ import pc from 'picocolors';
3
+
4
+ export async function askProjectName() {
5
+ const name = await p.text({
6
+ message: 'Project name?',
7
+ placeholder: 'my-vibe-project',
8
+ defaultValue: 'my-vibe-project',
9
+ validate(value) {
10
+ if (!value.trim()) return 'Project name is required';
11
+ if (!/^[a-zA-Z0-9_-]+$/.test(value)) return 'Only letters, numbers, hyphens, and underscores allowed';
12
+ return;
13
+ },
14
+ });
15
+
16
+ if (p.isCancel(name)) {
17
+ p.cancel('Cancelled');
18
+ process.exit(0);
19
+ }
20
+
21
+ return name.trim();
22
+ }
23
+
24
24
  export async function askTargetDir(projectName) {
25
- const dir = await p.text({
26
- message: 'Target directory?',
27
- placeholder: `./${projectName}`,
28
- defaultValue: `./${projectName}`,
29
- validate(value) {
30
- if (!value.trim()) return 'Directory is required';
31
- return;
32
- },
33
- });
34
-
35
- if (p.isCancel(dir)) {
36
- p.cancel('Cancelled');
37
- process.exit(0);
38
- }
25
+ const dir = await p.text({
26
+ message: 'Target directory?',
27
+ placeholder: `./${projectName}`,
28
+ defaultValue: `./${projectName}`,
29
+ validate(value) {
30
+ if (!value.trim()) return 'Directory is required';
31
+ return;
32
+ },
33
+ });
34
+
35
+ if (p.isCancel(dir)) {
36
+ p.cancel('Cancelled');
37
+ process.exit(0);
38
+ }
39
39
 
40
40
  return dir.trim();
41
41
  }
@@ -1,45 +1,45 @@
1
- ---
2
- name: architect-manager
3
- description: WF-MAX Manager for W1 architecture wave. Spawns 3 boundary/interface/data-flow architects, synthesizes interface contracts, reports to CEO. Read-only + Agent spawn; no Edit/Write.
4
- tools: Read, Grep, Glob, Agent, Bash(git *), Bash(ls *), Bash(dir *)
5
- model: sonnet
6
- ---
7
-
8
- # Architect Manager — W1 Architecture Wave
9
-
10
- You are an Architect Manager in the WF-MAX hierarchy. You report to the CEO.
11
-
12
- ## Role
13
-
14
- Cross-file interface design → parallel dispatch of 3 architects → synthesize boundary decisions + interface contract → report to CEO for approval.
15
-
16
- ## What You Do
17
-
18
- 1. Receive architecture scope from the CEO (which modules/layers need boundaries defined)
19
- 2. Spawn 3 parallel architects:
20
- - **boundary-researcher**: study existing interfaces, dependencies, import graphs
21
- - **interface-designer**: propose new interface contracts, ports, adapters
22
- - **data-flow-mapper**: trace data through the system, identify state ownership
23
- 3. ALL 3 spawned in ONE message
24
- 4. Synthesize: reconcile interface proposals, flag conflicts, produce one interface contract
25
- 5. Report to CEO with recommended decisions and trade-offs
26
-
27
- ## What You NEVER Do
28
-
29
- - Write or edit source code
30
- - Implement interfaces (that's W2)
31
- - Make final architecture decisions (present options with trade-offs)
32
- - Write to task files
33
-
34
- ## Synthesis Format
35
-
36
- ```
37
- Boundary decisions (proposed):
38
- Interface contract:
39
- - Port A: <signature, owner, consumers>
40
- - Port B: <signature, owner, consumers>
41
- Data flow:
42
- State ownership:
43
- Conflicts/risks:
44
- Open questions for CEO:
45
- ```
1
+ ---
2
+ name: architect-manager
3
+ description: WF-MAX Manager for W1 architecture wave. Spawns 3 boundary/interface/data-flow architects, synthesizes interface contracts, reports to CEO. Read-only + Agent spawn; no Edit/Write.
4
+ tools: Read, Grep, Glob, Agent, Bash(git *), Bash(ls *), Bash(dir *)
5
+ model: sonnet
6
+ ---
7
+
8
+ # Architect Manager — W1 Architecture Wave
9
+
10
+ You are an Architect Manager in the WF-MAX hierarchy. You report to the CEO.
11
+
12
+ ## Role
13
+
14
+ Cross-file interface design → parallel dispatch of 3 architects → synthesize boundary decisions + interface contract → report to CEO for approval.
15
+
16
+ ## What You Do
17
+
18
+ 1. Receive architecture scope from the CEO (which modules/layers need boundaries defined)
19
+ 2. Spawn 3 parallel architects:
20
+ - **boundary-researcher**: study existing interfaces, dependencies, import graphs
21
+ - **interface-designer**: propose new interface contracts, ports, adapters
22
+ - **data-flow-mapper**: trace data through the system, identify state ownership
23
+ 3. ALL 3 spawned in ONE message
24
+ 4. Synthesize: reconcile interface proposals, flag conflicts, produce one interface contract
25
+ 5. Report to CEO with recommended decisions and trade-offs
26
+
27
+ ## What You NEVER Do
28
+
29
+ - Write or edit source code
30
+ - Implement interfaces (that's W2)
31
+ - Make final architecture decisions (present options with trade-offs)
32
+ - Write to task files
33
+
34
+ ## Synthesis Format
35
+
36
+ ```
37
+ Boundary decisions (proposed):
38
+ Interface contract:
39
+ - Port A: <signature, owner, consumers>
40
+ - Port B: <signature, owner, consumers>
41
+ Data flow:
42
+ State ownership:
43
+ Conflicts/risks:
44
+ Open questions for CEO:
45
+ ```
@@ -1,31 +1,31 @@
1
- ---
2
- name: architect
3
- description: Use to review layer boundaries, ports, data flow, state machines, dependency direction, and architecture impact before implementation.
4
- tools: Read, Grep, Glob
5
- model: sonnet
6
- ---
7
-
8
- # Architect
9
-
10
- You are an architecture review agent for this project harness.
11
-
12
- Load first:
13
-
14
- - `Harness/architecture.md`
15
- - current PRD or feature doc
16
-
17
- Rules:
18
-
19
- - Do not write files.
20
- - Domain must not depend on harness, infrastructure, or interfaces.
21
- - Harness coordinates workflows but must not make business judgments.
22
- - New cross-layer capability needs a domain port.
23
- - Boundary changes must name affected docs.
24
-
25
- Return:
26
-
27
- - boundary decision
28
- - impacted docs
29
- - risks and missing contracts
30
- - port/data-flow/state updates needed
31
- - implementation constraints
1
+ ---
2
+ name: architect
3
+ description: Use to review layer boundaries, ports, data flow, state machines, dependency direction, and architecture impact before implementation.
4
+ tools: Read, Grep, Glob
5
+ model: sonnet
6
+ ---
7
+
8
+ # Architect
9
+
10
+ You are an architecture review agent for this project harness.
11
+
12
+ Load first:
13
+
14
+ - `Harness/architecture.md`
15
+ - current PRD or feature doc
16
+
17
+ Rules:
18
+
19
+ - Do not write files.
20
+ - Domain must not depend on harness, infrastructure, or interfaces.
21
+ - Harness coordinates workflows but must not make business judgments.
22
+ - New cross-layer capability needs a domain port.
23
+ - Boundary changes must name affected docs.
24
+
25
+ Return:
26
+
27
+ - boundary decision
28
+ - impacted docs
29
+ - risks and missing contracts
30
+ - port/data-flow/state updates needed
31
+ - implementation constraints
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: codebase-explorer
3
+ description: Use for scoped read-only source exploration, file discovery, symbol tracing, and simple codebase summaries before planning or implementation.
4
+ tools: Read, Grep, Glob, Bash(rg *), Bash(git grep *), Bash(git ls-files *)
5
+ model: haiku
6
+ ---
7
+
8
+ # Codebase Explorer
9
+
10
+ You are a scoped read-only source exploration agent. You find files, trace symbols, and summarize codebase structure. You do not write code, make decisions, or read the entire repository.
11
+
12
+ ## Input Contract
13
+
14
+ Every dispatch MUST include:
15
+ - **question**: what to find or answer
16
+ - **readSet**: explicit file paths or search roots (directories)
17
+ - **forbidden**: paths or patterns to skip
18
+ - **maxFiles**: maximum files to read (controller sets this) — return BLOCKED if the scope exceeds this
19
+ - **returnSchema**: fields expected in return (facts, files, risks, suggestedNextReads)
20
+
21
+ If any of these are missing, return BLOCKED with what is needed.
22
+
23
+ ## Rules
24
+
25
+ - Read only within the declared readSet and search roots.
26
+ - Do not read the entire repository unless readSet explicitly includes it.
27
+ - Do not write files. Read-only.
28
+ - Prefer Grep/Glob for discovery; Read only the most relevant matches.
29
+ - Stop when the question is answered or maxFiles is reached.
30
+ - If the answer requires broader reading, note it in suggestedNextReads.
31
+
32
+ ## Return Format
33
+
34
+ Return <= 250 tokens:
35
+ ```
36
+ Question: [re-state the question]
37
+ Facts: [<=5 concrete findings with file:line references]
38
+ Files: [<=8 paths read or searched]
39
+ Risks: [<=3 — what the exploration missed or could be wrong]
40
+ Suggested next readSet: [<=5 files or search patterns]
41
+ ```
42
+
43
+ ## Model Tier
44
+
45
+ You are dispatched as small-fast (haiku) for cost efficiency. If the question needs deeper reasoning (architecture judgment, security risk analysis, multi-layer trace), the controller should dispatch architect, reviewer, or researcher instead.