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.
- package/README-CN.md +163 -105
- package/README.md +179 -244
- package/bin/create-harness-vibe-coding.js +2 -2
- package/docs/images/harness-architecture-light.png +0 -0
- package/docs/images/harness-architecture.drawio +164 -0
- package/docs/images/harness-icon.png +0 -0
- package/package.json +47 -44
- package/src/generator.js +41 -5
- package/src/index.js +86 -13
- package/src/prompts.js +37 -37
- package/templates/common/.claude/agents/architect-manager.md +45 -45
- package/templates/common/.claude/agents/architect.md +31 -31
- package/templates/common/.claude/agents/codebase-explorer.md +45 -0
- package/templates/common/.claude/agents/context-master.md +75 -75
- package/templates/common/.claude/agents/debugger.md +41 -41
- package/templates/common/.claude/agents/docs-researcher.md +41 -41
- package/templates/common/.claude/agents/explore-manager.md +41 -41
- package/templates/common/.claude/agents/implement-manager.md +49 -49
- package/templates/common/.claude/agents/implementer.md +40 -40
- package/templates/common/.claude/agents/memory-master.md +82 -64
- package/templates/common/.claude/agents/planner.md +34 -34
- package/templates/common/.claude/agents/researcher.md +41 -41
- package/templates/common/.claude/agents/review-manager.md +56 -56
- package/templates/common/.claude/agents/reviewer.md +34 -34
- package/templates/common/.claude/agents/task-scribe.md +70 -0
- package/templates/common/.claude/agents/verifier.md +29 -29
- package/templates/common/.claude/commands/wf-help.md +9 -5
- package/templates/common/.claude/commands/wf-update.md +24 -0
- package/templates/common/.claude/rules/ecc/common.md +57 -44
- package/templates/common/.claude/settings.json +13 -0
- package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +8 -4
- package/templates/common/.claude/skills/wf/SKILL.md +15 -8
- package/templates/common/.claude/skills/wf-auto/SKILL.md +10 -7
- package/templates/common/.claude/skills/wf-learn/SKILL.md +9 -2
- package/templates/common/.claude/skills/wf-max/SKILL.md +23 -5
- package/templates/common/.claude/skills/wf-readme/SKILL.md +49 -49
- package/templates/common/.claude/skills/wf-remove/SKILL.md +7 -7
- package/templates/common/.claude/skills/wf-update/SKILL.md +15 -2
- package/templates/common/.codex/hooks.json +17 -0
- package/templates/common/.harness-version +130 -45
- package/templates/common/.opencode/agents/architect-manager.md +52 -0
- package/templates/common/.opencode/agents/architect.md +35 -0
- package/templates/common/.opencode/agents/codebase-explorer.md +45 -0
- package/templates/common/.opencode/agents/context-master.md +81 -0
- package/templates/common/.opencode/agents/debugger.md +43 -0
- package/templates/common/.opencode/agents/docs-researcher.md +42 -0
- package/templates/common/.opencode/agents/explore-manager.md +49 -0
- package/templates/common/.opencode/agents/implement-manager.md +56 -0
- package/templates/common/.opencode/agents/implementer.md +42 -0
- package/templates/common/.opencode/agents/memory-master.md +88 -0
- package/templates/common/.opencode/agents/planner.md +38 -0
- package/templates/common/.opencode/agents/reflector.md +39 -0
- package/templates/common/.opencode/agents/researcher.md +42 -0
- package/templates/common/.opencode/agents/review-manager.md +63 -0
- package/templates/common/.opencode/agents/reviewer.md +37 -0
- package/templates/common/.opencode/agents/task-scribe.md +70 -0
- package/templates/common/.opencode/agents/tdd-guide.md +83 -0
- package/templates/common/.opencode/agents/test-writer.md +54 -0
- package/templates/common/.opencode/agents/verifier.md +37 -0
- package/templates/common/.opencode/commands/wf-auto-spark.md +15 -0
- package/templates/common/.opencode/commands/wf-auto.md +15 -0
- package/templates/common/.opencode/commands/wf-help.md +27 -0
- package/templates/common/.opencode/commands/wf-learn.md +15 -0
- package/templates/common/.opencode/commands/wf-max.md +15 -0
- package/templates/common/.opencode/commands/wf-readme.md +15 -0
- package/templates/common/.opencode/commands/wf-remove.md +15 -0
- package/templates/common/.opencode/commands/wf-review.md +15 -0
- package/templates/common/.opencode/commands/wf-update.md +24 -0
- package/templates/common/.opencode/commands/wf.md +15 -0
- package/templates/common/.opencode/plugins/harness-wf-status.mjs +135 -0
- package/templates/common/AGENTS.md +2 -29
- package/templates/common/CLAUDE.md +114 -88
- package/templates/common/Harness/ACCEPTANCE_PROTOCOL.md +2 -2
- package/templates/common/{MEMORY.md → Harness/MEMORY.md} +17 -4
- package/templates/common/Harness/MEMORY_PROTOCOL.md +80 -30
- package/templates/common/Harness/PROGRESS.md +17 -17
- package/templates/common/Harness/README.md +58 -19
- package/templates/common/{SETUP.md → Harness/SETUP.md} +278 -276
- package/templates/common/Harness/TASK_ARCHIVE.md +56 -0
- package/templates/common/Harness/WF-AUTO-ANGLES.md +170 -0
- package/templates/common/Harness/WF-AUTO-SPARK.md +10 -19
- package/templates/common/Harness/WF-AUTO.md +93 -167
- package/templates/common/Harness/WF-KERNEL.md +189 -0
- package/templates/common/Harness/WF-MAX.md +60 -328
- package/templates/common/Harness/WF-STATE.md +83 -0
- package/templates/common/Harness/WF.md +117 -237
- package/templates/common/Harness/agent-workflow.md +2 -2
- package/templates/common/Harness/architecture.md +124 -124
- package/templates/common/Harness/context-loading.md +111 -111
- package/templates/common/Harness/dispatch.md +43 -35
- package/templates/common/Harness/extension.md +66 -66
- package/templates/common/Harness/lifecycle.md +20 -20
- package/templates/common/Harness/research/PRD.md +56 -56
- package/templates/common/Harness/research/README.md +169 -169
- package/templates/common/Harness/research/research-results.md +66 -66
- package/templates/common/Harness/scripts/archive-tasks.mjs +239 -0
- package/templates/common/{scripts → Harness/scripts}/scan-clean.mjs +443 -416
- package/templates/common/{scripts → Harness/scripts}/validate-harness.mjs +691 -452
- package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +258 -0
- package/templates/common/{scripts → Harness/scripts}/wf-remove.mjs +56 -39
- package/templates/common/{scripts → Harness/scripts}/wf-update-check.mjs +632 -599
- package/templates/common/Harness/subagents.md +215 -214
- package/templates/common/Harness/tasks/_template/ARTIFACTS.md +2 -2
- package/templates/common/Harness/tasks/_template/NOTES.md +2 -2
- package/templates/common/Harness/tasks/_template/PLAN.md +5 -0
- package/templates/common/Harness/tasks/_template/STATE.json +23 -0
- package/templates/common/README.md +37 -37
- package/templates/common/memory/agent-lessons-patterns.md +22 -21
- package/templates/common/memory/routes.md +43 -0
- package/templates/common/memory/startup-hints.md +32 -0
- package/templates/common/memory/tool-usage-reflections.md +22 -21
- package/templates/common/memory/user-corrections-preferences.md +23 -21
- package/templates/common/opencode.json +19 -0
- package/templates/optional/catalog.json +49 -33
- package/templates/optional/skills/browser-e2e/.claude/skills/browser-e2e/SKILL.md +42 -42
- package/templates/optional/skills/browser-e2e/.claude/skills/wf-browser/SKILL.md +193 -193
- package/templates/optional/skills/browser-e2e/.opencode/commands/wf-browser.md +15 -0
- package/templates/optional/skills/browser-e2e/Harness/workflows/browser-e2e.md +48 -48
- package/templates/optional/skills/github-pr-review/.claude/skills/github-pr-review/SKILL.md +40 -40
- package/templates/optional/skills/github-pr-review/Harness/workflows/github-pr-review.md +28 -28
- package/templates/optional/skills/python-backend/.claude/skills/python-backend/SKILL.md +40 -40
- package/templates/optional/skills/python-backend/Harness/workflows/python-backend.md +34 -34
- package/templates/optional/skills/ts-react-frontend/.claude/skills/ts-react-frontend/SKILL.md +43 -43
- package/templates/optional/skills/ts-react-frontend/Harness/workflows/ts-react-frontend.md +34 -34
- package/templates/optional/skills/ui-ux-review/.claude/skills/ui-ux-review/SKILL.md +40 -40
- 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.
|
|
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
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
},
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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.
|