create-harness-vibe-coding 0.8.6 → 0.8.8
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 +157 -105
- package/README.md +160 -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/package.json +46 -44
- package/src/generator.js +30 -11
- package/src/index.js +143 -14
- package/src/prompts.js +37 -37
- package/templates/common/.claude/agents/architect-manager.md +45 -45
- package/templates/common/.claude/agents/context-master.md +75 -75
- package/templates/common/.claude/agents/debugger.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 +64 -64
- package/templates/common/.claude/agents/planner.md +34 -34
- package/templates/common/.claude/agents/reflector.md +35 -0
- package/templates/common/.claude/agents/researcher.md +41 -41
- package/templates/common/.claude/agents/review-manager.md +56 -56
- package/templates/common/.claude/agents/verifier.md +34 -32
- package/templates/common/.claude/commands/wf-help.md +2 -3
- package/templates/common/.claude/rules/ecc/common.md +44 -44
- package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +10 -4
- package/templates/common/.claude/skills/wf/SKILL.md +7 -3
- package/templates/common/.claude/skills/wf-auto/SKILL.md +61 -107
- package/templates/common/.claude/skills/wf-auto-spark/SKILL.md +19 -17
- package/templates/common/.claude/skills/wf-max/SKILL.md +40 -21
- 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 +9 -4
- package/templates/common/.codex/config.toml +5 -0
- package/templates/common/.harness-version +78 -36
- package/templates/common/.opencode/agents/architect-manager.md +52 -0
- package/templates/common/.opencode/agents/architect.md +35 -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 +70 -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/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-help.md +23 -0
- package/templates/common/AGENTS.md +26 -25
- package/templates/common/CLAUDE.md +86 -88
- package/templates/common/Harness/ACCEPTANCE_PROTOCOL.md +12 -4
- package/templates/common/Harness/PROGRESS.md +17 -17
- package/templates/common/Harness/README.md +26 -16
- package/templates/common/Harness/WF-AUTO-ANGLES.md +170 -0
- package/templates/common/Harness/WF-AUTO-SPARK.md +23 -6
- package/templates/common/Harness/WF-AUTO.md +508 -493
- package/templates/common/Harness/WF-MAX.md +284 -232
- package/templates/common/Harness/WF.md +47 -29
- package/templates/common/Harness/agent-workflow.md +108 -76
- package/templates/common/Harness/architecture.md +124 -124
- package/templates/common/Harness/context-loading.md +111 -111
- package/templates/common/Harness/dispatch.md +96 -95
- package/templates/common/Harness/extension.md +67 -67
- 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/subagents.md +192 -170
- package/templates/common/Harness/tasks/_template/ARTIFACTS.md +3 -3
- package/templates/common/Harness/tasks/_template/NOTES.md +3 -3
- package/templates/common/Harness/tasks/_template/PLAN.md +53 -60
- package/templates/common/Harness/tasks/_template/PROGRESS.md +26 -29
- package/templates/common/MEMORY.md +27 -30
- package/templates/common/README.md +36 -36
- package/templates/common/SETUP.md +1 -1
- package/templates/common/memory/agent-lessons-patterns.md +21 -21
- package/templates/common/memory/tool-usage-reflections.md +21 -21
- package/templates/common/memory/user-corrections-preferences.md +21 -21
- package/templates/common/opencode.json +19 -0
- package/templates/common/scripts/scan-clean.mjs +487 -448
- package/templates/common/scripts/validate-harness.mjs +245 -146
- package/templates/common/scripts/wf-remove.mjs +311 -293
- package/templates/common/scripts/wf-update-check.mjs +511 -311
- package/templates/optional/catalog.json +41 -33
- package/templates/optional/skills/browser-e2e/.claude/skills/wf-browser/SKILL.md +194 -194
- package/templates/optional/skills/browser-e2e/Harness/workflows/browser-e2e.md +105 -69
- package/templates/optional/skills/github-pr-review/Harness/workflows/github-pr-review.md +28 -28
- package/templates/optional/skills/python-backend/Harness/workflows/python-backend.md +34 -34
- package/templates/optional/skills/ts-react-frontend/Harness/workflows/ts-react-frontend.md +34 -34
- package/templates/optional/skills/ui-ux-review/Harness/workflows/ui-ux-review.md +26 -26
package/package.json
CHANGED
|
@@ -1,44 +1,46 @@
|
|
|
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.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
|
+
"docs/images/harness-architecture-light.png",
|
|
14
|
+
"docs/images/harness-architecture.drawio",
|
|
15
|
+
"README-CN.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "node src/index.js",
|
|
19
|
+
"build:version": "node scripts/build-version.mjs",
|
|
20
|
+
"prepush": "node scripts/pre-push-check.mjs",
|
|
21
|
+
"test": "node --test tests/*.test.js",
|
|
22
|
+
"test:smoke": "node --test tests/cli-smoke.test.js",
|
|
23
|
+
"pack:smoke": "node --test tests/pack-smoke.test.js"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@clack/prompts": "^0.7.0",
|
|
27
|
+
"picocolors": "^1.1.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/zingspark/create-harness-vibe-coding.git"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"claude-code",
|
|
38
|
+
"harness",
|
|
39
|
+
"scaffold",
|
|
40
|
+
"vibe-coding",
|
|
41
|
+
"agentic",
|
|
42
|
+
"ecc",
|
|
43
|
+
"superpowers"
|
|
44
|
+
],
|
|
45
|
+
"license": "MIT"
|
|
46
|
+
}
|
package/src/generator.js
CHANGED
|
@@ -350,8 +350,9 @@ function nextBackupPath(destPath) {
|
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
function registerOptionalContent(file, content, selectedSkills) {
|
|
353
|
-
if (!selectedSkills.length) return content;
|
|
354
|
-
|
|
353
|
+
if (!selectedSkills.length) return content;
|
|
354
|
+
const hasBrowserE2e = selectedSkills.some(skill => skill.id === 'browser-e2e');
|
|
355
|
+
|
|
355
356
|
if (file === 'Harness/MEMORY.md') {
|
|
356
357
|
const lines = selectedSkills.map(skill => (
|
|
357
358
|
`- [${skill.id}](../.claude/skills/${skill.id}/SKILL.md) - ${skill.description} Codex mirror: [${skill.id}](../.agents/skills/${skill.id}/SKILL.md). Workflow: [workflows/${skill.id}.md](workflows/${skill.id}.md)`
|
|
@@ -360,15 +361,33 @@ function registerOptionalContent(file, content, selectedSkills) {
|
|
|
360
361
|
'Stack-specific skills can be added after the product shape is known.',
|
|
361
362
|
`Installed optional skills:\n\n${lines.join('\n')}\n\nStack-specific skills can be added after the product shape is known.`,
|
|
362
363
|
);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (file === 'Harness/README.md') {
|
|
366
|
-
const lines = selectedSkills.map(skill => (
|
|
367
|
-
`- [${skill.title}](workflows/${skill.id}.md) - ${skill.description}`
|
|
368
|
-
));
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (file === 'Harness/README.md') {
|
|
367
|
+
const lines = selectedSkills.map(skill => (
|
|
368
|
+
`- [${skill.title}](workflows/${skill.id}.md) - ${skill.description}`
|
|
369
|
+
));
|
|
370
|
+
let next = content;
|
|
371
|
+
if (hasBrowserE2e) {
|
|
372
|
+
next = next.replace(
|
|
373
|
+
'| Optional workflow installed |',
|
|
374
|
+
'| Browser E2E testing or automation | /wf-browser, $wf-browser, browser, e2e, web automation, screenshot verify, page test, browser test, Playwright, CDP | [workflows/browser-e2e.md](workflows/browser-e2e.md), [HARNESS_BRIDGE.md](HARNESS_BRIDGE.md) | UI/API contract, CLI commands, screenshots, traces, validation matrix |\n| Optional workflow installed |',
|
|
375
|
+
);
|
|
376
|
+
next = next.replace(
|
|
377
|
+
'| `/wf-readme [task]` |',
|
|
378
|
+
'| `/wf-browser [task]` | `$wf-browser [task]` | Optional browser automation/E2E workflow when `browser-e2e` is installed |\n| `/wf-readme [task]` |',
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return `${next.trimEnd()}\n\n## Installed Optional Workflows\n\n${lines.join('\n')}\n`;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if ((file === '.claude/commands/wf-help.md' || file === '.opencode/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
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
372
391
|
return content;
|
|
373
392
|
}
|
|
374
393
|
|
package/src/index.js
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
5
6
|
import pc from 'picocolors';
|
|
6
7
|
import { askConflictPolicy, askOptionalSelections, askProjectName, askTargetDir } from './prompts.js';
|
|
7
8
|
import { generate, getOptionalCatalog } from './generator.js';
|
|
9
|
+
|
|
10
|
+
const UPDATE_SUCCESS_STATUSES = new Set(['up-to-date', 'update-available', 'partial-update']);
|
|
11
|
+
const UPDATE_FAILURE_STATUSES = new Set(['error', 'offline', 'template-remote', 'downgrade-refused']);
|
|
8
12
|
|
|
9
13
|
// ── CLI flags ──────────────────────────────────────────────
|
|
10
14
|
const raw = process.argv.slice(2);
|
|
@@ -85,6 +89,10 @@ if (generationOptions.json) {
|
|
|
85
89
|
const projectName = argName || DEFAULT_NAME;
|
|
86
90
|
const targetDir = argDir || `./${projectName}`;
|
|
87
91
|
const scan = scanTarget(targetDir);
|
|
92
|
+
if (scan.hasHarness) {
|
|
93
|
+
printJsonResult(createUpdateSwitchResult(scan, { json: true }));
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
88
96
|
const result = generate({ projectName, targetDir, ...generationOptions });
|
|
89
97
|
result.scan = createJsonScan(scan);
|
|
90
98
|
result.agent = createAgentGuidance(result, {
|
|
@@ -108,16 +116,21 @@ console.log('');
|
|
|
108
116
|
let projectName, targetDir;
|
|
109
117
|
|
|
110
118
|
// Non-interactive: positionals provided OR -y/--yes flag set
|
|
111
|
-
if (argName || skipPrompts) {
|
|
112
|
-
projectName = argName || DEFAULT_NAME;
|
|
113
|
-
targetDir = argDir || `./${projectName}`;
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
if (argName || skipPrompts) {
|
|
120
|
+
projectName = argName || DEFAULT_NAME;
|
|
121
|
+
targetDir = argDir || `./${projectName}`;
|
|
122
|
+
const scan = scanTarget(targetDir);
|
|
123
|
+
|
|
124
|
+
if (scan.hasHarness) {
|
|
125
|
+
process.exit(runUpdateSwitch(scan, { json: false }));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(pc.dim('────────────────────────────────────────────'));
|
|
116
129
|
console.log(` Project ${pc.green(projectName)}`);
|
|
117
130
|
console.log(` Directory ${pc.green(targetDir)}`);
|
|
118
|
-
console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, tests/')}`);
|
|
131
|
+
console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, .opencode/, opencode.json, tests/')}`);
|
|
119
132
|
if (generationOptions.dryRun) {
|
|
120
|
-
console.log(` Mode ${pc.yellow('dry-run')}`);
|
|
133
|
+
console.log(` Mode ${pc.yellow('dry-run')}`);
|
|
121
134
|
}
|
|
122
135
|
console.log(` Conflicts ${pc.cyan(generationOptions.onConflict)}`);
|
|
123
136
|
if (generationOptions.withOptions.length > 0) {
|
|
@@ -159,6 +172,10 @@ if (argName || skipPrompts) {
|
|
|
159
172
|
const scan = scanTarget(targetDir);
|
|
160
173
|
printScan(scan);
|
|
161
174
|
|
|
175
|
+
if (scan.hasHarness) {
|
|
176
|
+
process.exit(runUpdateSwitch(scan, { json: false }));
|
|
177
|
+
}
|
|
178
|
+
|
|
162
179
|
if (!generationOptions.dryRun && !conflictPolicyProvided && scan.needsConflictPolicy) {
|
|
163
180
|
try {
|
|
164
181
|
generationOptions.onConflict = await askConflictPolicy(scan);
|
|
@@ -189,7 +206,7 @@ if (argName || skipPrompts) {
|
|
|
189
206
|
console.log(pc.dim('────────────────────────────────────────────'));
|
|
190
207
|
console.log(` Project ${pc.green(projectName)}`);
|
|
191
208
|
console.log(` Directory ${pc.green(targetDir)}`);
|
|
192
|
-
console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, tests/')}`);
|
|
209
|
+
console.log(` Creates ${pc.cyan('CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, .opencode/, opencode.json, tests/')}`);
|
|
193
210
|
console.log(` Conflicts ${pc.cyan(generationOptions.onConflict)}`);
|
|
194
211
|
if (generationOptions.withOptions.length > 0) {
|
|
195
212
|
console.log(` Optional ${pc.cyan(generationOptions.withOptions.join(','))}`);
|
|
@@ -259,8 +276,9 @@ function printResult(result, targetDir) {
|
|
|
259
276
|
|
|
260
277
|
console.log(pc.bold('Next steps:'));
|
|
261
278
|
console.log(` ${pc.cyan(`cd ${targetDir}`)}`);
|
|
262
|
-
console.log(` ${pc.cyan('claude')} # Start Claude Code`);
|
|
263
|
-
console.log(` ${pc.cyan('codex')} # Or start Codex`);
|
|
279
|
+
console.log(` ${pc.cyan('claude')} # Start Claude Code`);
|
|
280
|
+
console.log(` ${pc.cyan('codex')} # Or start Codex`);
|
|
281
|
+
console.log(` ${pc.cyan('opencode')} # Or start OpenCode`);
|
|
264
282
|
console.log(` Tell your agent: "${pc.yellow('Read Harness/SETUP.md. Bootstrap this project from idea to first vertical slice.')}"`);
|
|
265
283
|
console.log('');
|
|
266
284
|
console.log(pc.dim(' Harness/SETUP.md is temporary. Delete it after initialization.'));
|
|
@@ -429,6 +447,113 @@ function printJsonResult(result) {
|
|
|
429
447
|
}
|
|
430
448
|
}
|
|
431
449
|
|
|
450
|
+
function runUpdateSwitch(scan, { json }) {
|
|
451
|
+
const updateResult = createUpdateSwitchResult(scan, { json });
|
|
452
|
+
if (json) {
|
|
453
|
+
printJsonResult(updateResult);
|
|
454
|
+
return updateResult.success ? 0 : 1;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
console.log('');
|
|
458
|
+
console.log(pc.yellow('Existing Harness detected. Switching to wf-update check.'));
|
|
459
|
+
console.log(pc.dim(`Directory ${scan.resolvedDir}`));
|
|
460
|
+
console.log(pc.dim('Command node Harness/scripts/wf-update-check.mjs'));
|
|
461
|
+
console.log('');
|
|
462
|
+
|
|
463
|
+
if (!updateResult.success && updateResult.error) {
|
|
464
|
+
console.error(pc.red(updateResult.error));
|
|
465
|
+
return 1;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (updateResult.stdout) process.stdout.write(updateResult.stdout);
|
|
469
|
+
if (updateResult.stderr) process.stderr.write(updateResult.stderr);
|
|
470
|
+
return updateResult.exitCode ?? 0;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function getUpdateStatusError(update) {
|
|
474
|
+
if (!update || typeof update !== 'object' || typeof update.status !== 'string') {
|
|
475
|
+
return 'Update checker did not return a machine-readable status.';
|
|
476
|
+
}
|
|
477
|
+
if (UPDATE_FAILURE_STATUSES.has(update.status)) {
|
|
478
|
+
return `Update checker reported ${update.status}${update.message ? `: ${update.message}` : ''}`;
|
|
479
|
+
}
|
|
480
|
+
if (!UPDATE_SUCCESS_STATUSES.has(update.status)) {
|
|
481
|
+
return `Update checker returned unrecognized status: ${update.status}`;
|
|
482
|
+
}
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function createUpdateSwitchResult(scan, { json }) {
|
|
487
|
+
const args = ['Harness/scripts/wf-update-check.mjs'];
|
|
488
|
+
if (json) args.push('--json');
|
|
489
|
+
|
|
490
|
+
const scriptPath = path.join(scan.resolvedDir, 'Harness', 'scripts', 'wf-update-check.mjs');
|
|
491
|
+
const command = `node ${args.join(' ')}`;
|
|
492
|
+
const base = {
|
|
493
|
+
success: false,
|
|
494
|
+
mode: 'update',
|
|
495
|
+
scan: createJsonScan(scan),
|
|
496
|
+
agent: {
|
|
497
|
+
sourceOfTruth: 'Existing Harness detected; install automatically switched to the target update checker. Do not continue install writes.',
|
|
498
|
+
updateCommand: json
|
|
499
|
+
? 'node Harness/scripts/wf-update-check.mjs --json'
|
|
500
|
+
: 'node Harness/scripts/wf-update-check.mjs',
|
|
501
|
+
next: [
|
|
502
|
+
{
|
|
503
|
+
action: 'update',
|
|
504
|
+
command: json
|
|
505
|
+
? 'node Harness/scripts/wf-update-check.mjs --json'
|
|
506
|
+
: 'node Harness/scripts/wf-update-check.mjs',
|
|
507
|
+
reason: 'Harness already exists, so updates must use the installed Harness update flow.',
|
|
508
|
+
},
|
|
509
|
+
],
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
if (!fs.existsSync(scriptPath)) {
|
|
514
|
+
return {
|
|
515
|
+
...base,
|
|
516
|
+
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.',
|
|
517
|
+
errors: ['Harness/scripts/wf-update-check.mjs not found'],
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const result = spawnSync(process.execPath, args, {
|
|
522
|
+
cwd: scan.resolvedDir,
|
|
523
|
+
encoding: 'utf8',
|
|
524
|
+
});
|
|
525
|
+
const stdout = result.stdout || '';
|
|
526
|
+
const stderr = result.stderr || '';
|
|
527
|
+
const status = result.status ?? 1;
|
|
528
|
+
|
|
529
|
+
let update = undefined;
|
|
530
|
+
if (json && stdout.trim()) {
|
|
531
|
+
try {
|
|
532
|
+
update = JSON.parse(stdout.trim());
|
|
533
|
+
} catch {
|
|
534
|
+
update = { rawOutput: stdout };
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const errors = [];
|
|
539
|
+
if (status !== 0) errors.push(`Update checker exited with status ${status}`);
|
|
540
|
+
if (json) {
|
|
541
|
+
const statusError = getUpdateStatusError(update);
|
|
542
|
+
if (statusError) errors.push(statusError);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
...base,
|
|
547
|
+
success: errors.length === 0,
|
|
548
|
+
exitCode: status,
|
|
549
|
+
command,
|
|
550
|
+
stdout,
|
|
551
|
+
stderr,
|
|
552
|
+
...(json ? { update } : {}),
|
|
553
|
+
...(errors.length === 0 ? {} : { error: errors[0], errors }),
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
432
557
|
function scanTarget(targetDir) {
|
|
433
558
|
const resolvedDir = path.resolve(process.cwd(), targetDir);
|
|
434
559
|
const exists = fs.existsSync(resolvedDir);
|
|
@@ -440,7 +565,8 @@ function scanTarget(targetDir) {
|
|
|
440
565
|
const hasClaude = isDirectory && fs.existsSync(path.join(resolvedDir, 'CLAUDE.md'));
|
|
441
566
|
const hasAgents = isDirectory && fs.existsSync(path.join(resolvedDir, 'AGENTS.md'));
|
|
442
567
|
const hasAgentSkills = isDirectory && fs.existsSync(path.join(resolvedDir, '.agents'));
|
|
443
|
-
const hasCodex = isDirectory && fs.existsSync(path.join(resolvedDir, '.codex'));
|
|
568
|
+
const hasCodex = isDirectory && fs.existsSync(path.join(resolvedDir, '.codex'));
|
|
569
|
+
const hasOpencode = isDirectory && (fs.existsSync(path.join(resolvedDir, '.opencode')) || fs.existsSync(path.join(resolvedDir, 'opencode.json')));
|
|
444
570
|
const hasDocs = isDirectory && fs.existsSync(path.join(resolvedDir, 'docs'));
|
|
445
571
|
const hasReadme = isDirectory && fs.existsSync(path.join(resolvedDir, 'README.md'));
|
|
446
572
|
const hasPackageJson = isDirectory && fs.existsSync(path.join(resolvedDir, 'package.json'));
|
|
@@ -458,7 +584,8 @@ function scanTarget(targetDir) {
|
|
|
458
584
|
hasClaude,
|
|
459
585
|
hasAgents,
|
|
460
586
|
hasAgentSkills,
|
|
461
|
-
hasCodex,
|
|
587
|
+
hasCodex,
|
|
588
|
+
hasOpencode,
|
|
462
589
|
hasDocs,
|
|
463
590
|
hasReadme,
|
|
464
591
|
hasPackageJson,
|
|
@@ -486,7 +613,8 @@ function createJsonScan(scan) {
|
|
|
486
613
|
hasClaude: scan.hasClaude,
|
|
487
614
|
hasAgents: scan.hasAgents,
|
|
488
615
|
hasAgentSkills: scan.hasAgentSkills,
|
|
489
|
-
hasCodex: scan.hasCodex,
|
|
616
|
+
hasCodex: scan.hasCodex,
|
|
617
|
+
hasOpencode: scan.hasOpencode,
|
|
490
618
|
hasDocs: scan.hasDocs,
|
|
491
619
|
hasReadme: scan.hasReadme,
|
|
492
620
|
hasPackageJson: scan.hasPackageJson,
|
|
@@ -660,6 +788,7 @@ function printScan(scan) {
|
|
|
660
788
|
if (scan.hasClaude) console.log(` CLAUDE.md ${pc.yellow('exists')}`);
|
|
661
789
|
if (scan.hasAgents) console.log(` AGENTS.md ${pc.yellow('exists')}`);
|
|
662
790
|
if (scan.hasAgentSkills) console.log(` .agents/ ${pc.yellow('exists')}`);
|
|
663
|
-
if (scan.hasCodex) console.log(` .codex/ ${pc.yellow('exists')}`);
|
|
791
|
+
if (scan.hasCodex) console.log(` .codex/ ${pc.yellow('exists')}`);
|
|
792
|
+
if (scan.hasOpencode) console.log(` opencode ${pc.yellow('exists')} ${pc.dim('(.opencode/ or opencode.json)')}`);
|
|
664
793
|
if (scan.hasDocs) console.log(` docs/ ${pc.dim('project-owned; Harness stays in Harness/')}`);
|
|
665
794
|
}
|
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
|
+
```
|