create-harness-vibe-coding 0.8.18 → 0.8.19
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/CHANGELOG.md +13 -0
- package/README.md +1 -0
- package/package.json +5 -2
- package/src/index.js +383 -267
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js +17 -0
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js.map +1 -0
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js +7 -0
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js.map +1 -0
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js +12 -0
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js.map +1 -0
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js +50 -0
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js.map +1 -0
- package/src/ui/dist/assets/TerminalDrawer-6GBZ9nXN.css +32 -0
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js +74 -0
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js.map +1 -0
- package/src/ui/dist/assets/WorkflowRoute-BnuhLJ6X.css +1 -0
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js +39 -0
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js.map +1 -0
- package/src/ui/dist/assets/index-BOVYWntB.js +117 -0
- package/src/ui/dist/assets/index-BOVYWntB.js.map +1 -0
- package/src/ui/dist/assets/index-DaUObq0H.css +1 -0
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js +7 -0
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js.map +1 -0
- package/src/ui/dist/assets/plus-QuTJyoT3.js +7 -0
- package/src/ui/dist/assets/plus-QuTJyoT3.js.map +1 -0
- package/src/ui/dist/assets/proxy-D0vUbds5.js +2 -0
- package/src/ui/dist/assets/proxy-D0vUbds5.js.map +1 -0
- package/src/ui/dist/assets/refresh-cw-JNmExijd.js +7 -0
- package/src/ui/dist/assets/refresh-cw-JNmExijd.js.map +1 -0
- package/src/ui/dist/assets/terminal-BBQvnOAK.js +7 -0
- package/src/ui/dist/assets/terminal-BBQvnOAK.js.map +1 -0
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js +7 -0
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js.map +1 -0
- package/src/ui/dist/assets/x-DoIOGAJz.js +7 -0
- package/src/ui/dist/assets/x-DoIOGAJz.js.map +1 -0
- package/src/ui/dist/index.html +18 -0
- package/src/ui/index.html +17 -0
- package/src/ui/package.json +33 -0
- package/src/ui/pnpm-lock.yaml +1582 -0
- package/src/ui/pnpm-workspace.yaml +2 -0
- package/src/ui/src/App.tsx +86 -0
- package/src/ui/src/api.ts +93 -0
- package/src/ui/src/components/AgentsRoute.tsx +503 -0
- package/src/ui/src/components/Footer.tsx +69 -0
- package/src/ui/src/components/Header.tsx +175 -0
- package/src/ui/src/components/LoadingView.tsx +22 -0
- package/src/ui/src/components/RolesRoute.tsx +169 -0
- package/src/ui/src/components/SettingsRoute.tsx +166 -0
- package/src/ui/src/components/TaskList.tsx +388 -0
- package/src/ui/src/components/TerminalDrawer.tsx +611 -0
- package/src/ui/src/components/WorkflowRoute.tsx +670 -0
- package/src/ui/src/hooks/useReducedMotion.ts +17 -0
- package/src/ui/src/hooks/useServerConnection.ts +114 -0
- package/src/ui/src/index.css +281 -0
- package/src/ui/src/main.tsx +11 -0
- package/src/ui/src/types.ts +108 -0
- package/src/ui/tsconfig.json +21 -0
- package/src/ui/vite.config.ts +19 -0
- package/src/wf-ui-server/__tests__/a2a-store.test.mjs +79 -0
- package/src/wf-ui-server/__tests__/peer-capsule.test.mjs +268 -0
- package/src/wf-ui-server/__tests__/pty-adapter.test.mjs +70 -0
- package/src/wf-ui-server/__tests__/runtime-config.test.mjs +43 -0
- package/src/wf-ui-server/__tests__/runtime-detector.test.mjs +90 -0
- package/src/wf-ui-server/__tests__/security.test.mjs +59 -0
- package/src/wf-ui-server/__tests__/server.integration.test.mjs +150 -0
- package/src/wf-ui-server/__tests__/session-registry.test.mjs +238 -0
- package/src/wf-ui-server/__tests__/settings.test.mjs +135 -0
- package/src/wf-ui-server/__tests__/task-parser.test.mjs +200 -0
- package/src/wf-ui-server/__tests__/terminal-store.test.mjs +138 -0
- package/src/wf-ui-server/__tests__/token.test.mjs +48 -0
- package/src/wf-ui-server/__tests__/ws-events.integration.test.mjs +419 -0
- package/src/wf-ui-server/__tests__/ws-terminal.integration.test.mjs +383 -0
- package/src/wf-ui-server/a2a-store.mjs +296 -0
- package/src/wf-ui-server/peer-capsule.mjs +213 -0
- package/src/wf-ui-server/pty-adapter.mjs +155 -0
- package/src/wf-ui-server/runtime-config.mjs +153 -0
- package/src/wf-ui-server/runtime-detector.mjs +374 -0
- package/src/wf-ui-server/security.mjs +67 -0
- package/src/wf-ui-server/server.mjs +849 -0
- package/src/wf-ui-server/session-registry.mjs +204 -0
- package/src/wf-ui-server/settings.mjs +100 -0
- package/src/wf-ui-server/task-parser.mjs +133 -0
- package/src/wf-ui-server/terminal-store.mjs +225 -0
- package/src/wf-ui-server/token.mjs +41 -0
- package/src/wf-ui-server/ws-events.mjs +354 -0
- package/src/wf-ui-server/ws-terminal.mjs +473 -0
- package/templates/common/.claude/commands/wf-help.md +1 -0
- package/templates/common/.claude/commands/wf-task-list.md +2 -0
- package/templates/common/.claude/commands/wf-ui.md +26 -0
- package/templates/common/.claude/commands/wf-update.md +54 -9
- package/templates/common/.claude/rules/ecc/common.md +1 -1
- package/templates/common/.claude/skills/wf-auto/SKILL.md +3 -3
- package/templates/common/.claude/skills/wf-auto-spark/SKILL.md +2 -2
- package/templates/common/.claude/skills/wf-ui/SKILL.md +78 -0
- package/templates/common/.claude/skills/wf-update/SKILL.md +55 -58
- package/templates/common/.harness-version +63 -39
- package/templates/common/.opencode/commands/wf-help.md +1 -0
- package/templates/common/.opencode/commands/wf-task-list.md +2 -0
- package/templates/common/.opencode/commands/wf-ui.md +26 -0
- package/templates/common/.opencode/commands/wf-update.md +54 -9
- package/templates/common/CLAUDE.md +1 -1
- package/templates/common/Harness/MEMORY.md +2 -0
- package/templates/common/Harness/README.md +6 -4
- package/templates/common/Harness/a2a/role-graph.json +79 -0
- package/templates/common/Harness/a2a/runtime-registry.json +5 -0
- package/templates/common/Harness/a2a/skills/terminal-control.json +15 -0
- package/templates/common/Harness/ownership.manifest.json +57 -2
- package/templates/common/Harness/scripts/README.md +134 -0
- package/templates/common/Harness/scripts/a2a-terminal.mjs +191 -0
- package/templates/common/Harness/scripts/context-budget.mjs +1 -1
- package/templates/common/Harness/scripts/sync-host-global.mjs +278 -0
- package/templates/common/Harness/scripts/task-state.mjs +556 -23
- package/templates/common/Harness/scripts/validate-harness.mjs +229 -19
- package/templates/common/Harness/scripts/wf-remove.mjs +8 -3
- package/templates/common/Harness/scripts/wf-update-check.mjs +37 -9
- package/templates/common/Harness/scripts/wf-update-runner.mjs +325 -0
- package/templates/common/Harness/settings.json +35 -0
- package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +7 -2
- package/templates/common/Harness/specs/runtime/command-surface.json +14 -0
- package/templates/common/Harness/specs/workflows/WF-AUTO-SPARK.md +8 -8
- package/templates/common/Harness/specs/workflows/WF-AUTO.md +12 -12
- package/templates/common/Harness/tasks/_template/NAMING.md +16 -16
- package/templates/common/Harness/tasks/_template/PLAN.md +17 -60
- package/templates/common/Harness/tasks/_template/PROBLEM.md +18 -0
- package/templates/common/Harness/tasks/_template/PROGRESS.md +9 -14
- package/templates/common/Harness/tasks/_template/REFERENCES.md +26 -0
- package/templates/common/Harness/tasks/_template/ARTIFACTS.md +0 -3
- package/templates/common/Harness/tasks/_template/NOTES.md +0 -3
package/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
6
6
|
import pc from 'picocolors';
|
|
7
7
|
import { askConflictPolicy, askInstallScope, askOptionalSelections, askProjectName, askTargetDir } from './prompts.js';
|
|
8
8
|
import { generate, getOptionalCatalog } from './generator.js';
|
|
@@ -10,37 +10,55 @@ import { generate, getOptionalCatalog } from './generator.js';
|
|
|
10
10
|
const UPDATE_SUCCESS_STATUSES = new Set(['up-to-date', 'update-available', 'partial-update']);
|
|
11
11
|
const UPDATE_FAILURE_STATUSES = new Set(['error', 'offline', 'template-remote', 'downgrade-refused']);
|
|
12
12
|
const CANONICAL_UPDATE_SOURCE_BASE = 'https://raw.githubusercontent.com/LiWeny16/create-harness-vibe-coding/main/templates/common/';
|
|
13
|
-
|
|
14
|
-
// ── CLI flags ──────────────────────────────────────────────
|
|
15
|
-
const raw = process.argv.slice(2);
|
|
13
|
+
|
|
14
|
+
// ── CLI flags ──────────────────────────────────────────────
|
|
15
|
+
const raw = process.argv.slice(2);
|
|
16
|
+
function extractFlag(args, flag) {
|
|
17
|
+
const idx = args.indexOf(flag);
|
|
18
|
+
if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
|
|
19
|
+
const withEquals = args.find(arg => arg.startsWith(`${flag}=`));
|
|
20
|
+
if (withEquals) return withEquals.slice(flag.length + 1);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function hasFlag(args, flag) {
|
|
24
|
+
return args.includes(flag);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// wf-ui subcommand: handle before parseArgs so --project/--port/--open flags
|
|
28
|
+
// never fall through to the scaffold installer.
|
|
29
|
+
if (raw[0] === 'wf-ui') {
|
|
30
|
+
await runWfUi(raw.slice(1));
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
const parsed = parseArgs(raw);
|
|
17
35
|
const showHelp = parsed.flags.help || parsed.flags.h;
|
|
18
36
|
const skipPrompts = parsed.flags.yes || parsed.flags.y;
|
|
19
37
|
const conflictPolicyProvided = parsed.flags.onConflict !== undefined;
|
|
20
|
-
|
|
21
|
-
if (parsed.errors.length > 0) {
|
|
22
|
-
for (const err of parsed.errors) {
|
|
23
|
-
console.error(pc.red(`Error: ${err}`));
|
|
24
|
-
}
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (showHelp) {
|
|
29
|
-
console.log('');
|
|
30
|
-
console.log(' create-harness-vibe-coding');
|
|
31
|
-
console.log('');
|
|
32
|
-
console.log(' Usage:');
|
|
33
|
-
console.log(' npx create-harness-vibe-coding@latest [project-name] [target-dir] [flags]');
|
|
34
|
-
console.log('');
|
|
35
|
-
console.log(' Arguments:');
|
|
36
|
-
console.log(' project-name Name for the new project (default: my-vibe-project)');
|
|
37
|
-
console.log(' target-dir Directory to create the project in (default: ./<project-name>)');
|
|
38
|
-
console.log('');
|
|
39
|
-
console.log(' Flags:');
|
|
40
|
-
console.log(' -y, --yes Skip all prompts, use defaults or provided args');
|
|
41
|
-
console.log(' -h, --help Show this help');
|
|
42
|
-
console.log(' --dry-run Print the planned writes without creating files');
|
|
43
|
-
console.log(' --on-conflict <policy> fail, skip, backup, or overwrite (default: fail)');
|
|
38
|
+
|
|
39
|
+
if (parsed.errors.length > 0 && raw[0] !== 'wf-ui') {
|
|
40
|
+
for (const err of parsed.errors) {
|
|
41
|
+
console.error(pc.red(`Error: ${err}`));
|
|
42
|
+
}
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (showHelp) {
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log(' create-harness-vibe-coding');
|
|
49
|
+
console.log('');
|
|
50
|
+
console.log(' Usage:');
|
|
51
|
+
console.log(' npx create-harness-vibe-coding@latest [project-name] [target-dir] [flags]');
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(' Arguments:');
|
|
54
|
+
console.log(' project-name Name for the new project (default: my-vibe-project)');
|
|
55
|
+
console.log(' target-dir Directory to create the project in (default: ./<project-name>)');
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(' Flags:');
|
|
58
|
+
console.log(' -y, --yes Skip all prompts, use defaults or provided args');
|
|
59
|
+
console.log(' -h, --help Show this help');
|
|
60
|
+
console.log(' --dry-run Print the planned writes without creating files');
|
|
61
|
+
console.log(' --on-conflict <policy> fail, skip, backup, or overwrite (default: fail)');
|
|
44
62
|
console.log(' --with <id,id> Add optional local workflow skills');
|
|
45
63
|
console.log(' --without <id,id> Remove optional workflow skills selected by --preset or --with');
|
|
46
64
|
console.log(' --recommend <id,id> Record recommendation-only external capabilities');
|
|
@@ -52,35 +70,35 @@ if (showHelp) {
|
|
|
52
70
|
console.log(' --json Output machine-readable JSON (use with --dry-run for planning)');
|
|
53
71
|
console.log('');
|
|
54
72
|
console.log(' Interactive mode offers checkbox selection for optional workflows and external recommendations.');
|
|
55
|
-
console.log('');
|
|
56
|
-
console.log(' Examples:');
|
|
57
|
-
console.log(' npx create-harness-vibe-coding@latest');
|
|
58
|
-
console.log(' npx create-harness-vibe-coding@latest -y');
|
|
59
|
-
console.log(' npx create-harness-vibe-coding@latest my-project');
|
|
60
|
-
console.log(' npx create-harness-vibe-coding@latest my-project ./dist/my-project -y');
|
|
61
|
-
console.log(' npx create-harness-vibe-coding@latest legacy ./legacy -y --dry-run');
|
|
62
|
-
console.log(' npx create-harness-vibe-coding@latest legacy ./legacy -y --on-conflict skip');
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log(' Examples:');
|
|
75
|
+
console.log(' npx create-harness-vibe-coding@latest');
|
|
76
|
+
console.log(' npx create-harness-vibe-coding@latest -y');
|
|
77
|
+
console.log(' npx create-harness-vibe-coding@latest my-project');
|
|
78
|
+
console.log(' npx create-harness-vibe-coding@latest my-project ./dist/my-project -y');
|
|
79
|
+
console.log(' npx create-harness-vibe-coding@latest legacy ./legacy -y --dry-run');
|
|
80
|
+
console.log(' npx create-harness-vibe-coding@latest legacy ./legacy -y --on-conflict skip');
|
|
63
81
|
console.log(' npx create-harness-vibe-coding@latest web ./web -y --with ts-react-frontend,ui-ux-review');
|
|
64
82
|
console.log(' npx create-harness-vibe-coding@latest web ./web -y --preset web-app');
|
|
65
83
|
console.log(' npx create-harness-vibe-coding@latest api ./api -y --preset fullstack --without github-pr-review');
|
|
66
84
|
console.log(' npx create-harness-vibe-coding@latest app ./app -y --install-scope global');
|
|
67
|
-
console.log('');
|
|
68
|
-
process.exit(0);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (parsed.flags.listOptions) {
|
|
72
|
-
printOptions();
|
|
73
|
-
process.exit(0);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Positional args (non-flag)
|
|
77
|
-
const positional = parsed.positionals;
|
|
78
|
-
const argName = positional[0];
|
|
79
|
-
const argDir = positional[1];
|
|
80
|
-
const generationOptions = {
|
|
81
|
-
dryRun: Boolean(parsed.flags.dryRun),
|
|
82
|
-
onConflict: parsed.flags.onConflict || 'fail',
|
|
83
|
-
withOptions: parsed.flags.with || [],
|
|
85
|
+
console.log('');
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (parsed.flags.listOptions) {
|
|
90
|
+
printOptions();
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Positional args (non-flag)
|
|
95
|
+
const positional = parsed.positionals;
|
|
96
|
+
const argName = positional[0];
|
|
97
|
+
const argDir = positional[1];
|
|
98
|
+
const generationOptions = {
|
|
99
|
+
dryRun: Boolean(parsed.flags.dryRun),
|
|
100
|
+
onConflict: parsed.flags.onConflict || 'fail',
|
|
101
|
+
withOptions: parsed.flags.with || [],
|
|
84
102
|
withoutOptions: parsed.flags.without || [],
|
|
85
103
|
externalOptions: parsed.flags.recommend || [],
|
|
86
104
|
preset: parsed.flags.preset,
|
|
@@ -89,10 +107,10 @@ const generationOptions = {
|
|
|
89
107
|
hostGlobalDir: parsed.flags.hostGlobalDir,
|
|
90
108
|
json: Boolean(parsed.flags.json),
|
|
91
109
|
};
|
|
92
|
-
|
|
93
|
-
const DEFAULT_NAME = 'my-vibe-project';
|
|
94
|
-
|
|
95
|
-
// --json: machine-readable output, no prompts, no decorative output
|
|
110
|
+
|
|
111
|
+
const DEFAULT_NAME = 'my-vibe-project';
|
|
112
|
+
|
|
113
|
+
// --json: machine-readable output, no prompts, no decorative output
|
|
96
114
|
if (generationOptions.json) {
|
|
97
115
|
const projectName = argName || DEFAULT_NAME;
|
|
98
116
|
const targetDir = argDir || `./${projectName}`;
|
|
@@ -116,20 +134,22 @@ if (generationOptions.json) {
|
|
|
116
134
|
scan,
|
|
117
135
|
});
|
|
118
136
|
printJsonResult(result);
|
|
119
|
-
// printJsonResult exits with 1 on failure; we only reach here on success
|
|
120
|
-
process.exit(0);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
console.log('');
|
|
124
|
-
console.log(pc.magenta('╔══════════════════════════════════════════╗'));
|
|
125
|
-
console.log(pc.magenta('║ create-harness-vibe-coding ║'));
|
|
126
|
-
console.log(pc.magenta('║ 0-1 Product Harness Scaffold ║'));
|
|
127
|
-
console.log(pc.magenta('╚══════════════════════════════════════════╝'));
|
|
128
|
-
console.log('');
|
|
129
|
-
|
|
130
|
-
let projectName, targetDir;
|
|
131
|
-
|
|
132
|
-
//
|
|
137
|
+
// printJsonResult exits with 1 on failure; we only reach here on success
|
|
138
|
+
process.exit(0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(pc.magenta('╔══════════════════════════════════════════╗'));
|
|
143
|
+
console.log(pc.magenta('║ create-harness-vibe-coding ║'));
|
|
144
|
+
console.log(pc.magenta('║ 0-1 Product Harness Scaffold ║'));
|
|
145
|
+
console.log(pc.magenta('╚══════════════════════════════════════════╝'));
|
|
146
|
+
console.log('');
|
|
147
|
+
|
|
148
|
+
let projectName, targetDir;
|
|
149
|
+
|
|
150
|
+
// wf-ui: server already running, skip the scaffold/update flow
|
|
151
|
+
if (raw[0] !== 'wf-ui') {
|
|
152
|
+
// Non-interactive: positionals provided OR -y/--yes flag set
|
|
133
153
|
if (argName || skipPrompts) {
|
|
134
154
|
projectName = argName || DEFAULT_NAME;
|
|
135
155
|
targetDir = argDir || `./${projectName}`;
|
|
@@ -157,39 +177,39 @@ if (argName || skipPrompts) {
|
|
|
157
177
|
? 'project bridge/state/settings + global runtime + Claude/Codex/OpenCode host copies'
|
|
158
178
|
: 'CLAUDE.md, README.md, Harness/PROGRESS.md, Harness/, .claude/, .agents/, .opencode/, opencode.json, tests/';
|
|
159
179
|
console.log(` Creates ${pc.cyan(creates)}`);
|
|
160
|
-
if (generationOptions.dryRun) {
|
|
180
|
+
if (generationOptions.dryRun) {
|
|
161
181
|
console.log(` Mode ${pc.yellow('dry-run')}`);
|
|
162
|
-
}
|
|
163
|
-
console.log(` Conflicts ${pc.cyan(generationOptions.onConflict)}`);
|
|
164
|
-
if (generationOptions.withOptions.length > 0) {
|
|
165
|
-
console.log(` Optional ${pc.cyan(generationOptions.withOptions.join(','))}`);
|
|
166
|
-
}
|
|
182
|
+
}
|
|
183
|
+
console.log(` Conflicts ${pc.cyan(generationOptions.onConflict)}`);
|
|
184
|
+
if (generationOptions.withOptions.length > 0) {
|
|
185
|
+
console.log(` Optional ${pc.cyan(generationOptions.withOptions.join(','))}`);
|
|
186
|
+
}
|
|
167
187
|
if (generationOptions.withoutOptions.length > 0) {
|
|
168
188
|
console.log(` Without ${pc.cyan(generationOptions.withoutOptions.join(','))}`);
|
|
169
189
|
}
|
|
170
190
|
if (generationOptions.externalOptions.length > 0) {
|
|
171
191
|
console.log(` Recommend ${pc.cyan(generationOptions.externalOptions.join(','))}`);
|
|
172
192
|
}
|
|
173
|
-
if (generationOptions.preset) {
|
|
174
|
-
console.log(` Preset ${pc.cyan(generationOptions.preset)}`);
|
|
175
|
-
}
|
|
176
|
-
if (skipPrompts) {
|
|
177
|
-
console.log(` Mode ${pc.dim('non-interactive (-y)')}`);
|
|
178
|
-
}
|
|
179
|
-
console.log(pc.dim('────────────────────────────────────────────'));
|
|
180
|
-
console.log('');
|
|
181
|
-
|
|
182
|
-
const result = generate({ projectName, targetDir, ...generationOptions });
|
|
183
|
-
printResult(result, targetDir);
|
|
184
|
-
} else {
|
|
185
|
-
// Interactive mode
|
|
186
|
-
try {
|
|
187
|
-
projectName = await askProjectName();
|
|
188
|
-
} catch {
|
|
189
|
-
projectName = 'my-vibe-project';
|
|
190
|
-
console.log(pc.dim(` Project: ${projectName} (default)`));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
+
if (generationOptions.preset) {
|
|
194
|
+
console.log(` Preset ${pc.cyan(generationOptions.preset)}`);
|
|
195
|
+
}
|
|
196
|
+
if (skipPrompts) {
|
|
197
|
+
console.log(` Mode ${pc.dim('non-interactive (-y)')}`);
|
|
198
|
+
}
|
|
199
|
+
console.log(pc.dim('────────────────────────────────────────────'));
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
const result = generate({ projectName, targetDir, ...generationOptions });
|
|
203
|
+
printResult(result, targetDir);
|
|
204
|
+
} else {
|
|
205
|
+
// Interactive mode
|
|
206
|
+
try {
|
|
207
|
+
projectName = await askProjectName();
|
|
208
|
+
} catch {
|
|
209
|
+
projectName = 'my-vibe-project';
|
|
210
|
+
console.log(pc.dim(` Project: ${projectName} (default)`));
|
|
211
|
+
}
|
|
212
|
+
|
|
193
213
|
try {
|
|
194
214
|
targetDir = await askTargetDir(projectName);
|
|
195
215
|
} catch {
|
|
@@ -240,9 +260,9 @@ if (argName || skipPrompts) {
|
|
|
240
260
|
console.log(pc.dim(' Optional: none (default)'));
|
|
241
261
|
}
|
|
242
262
|
}
|
|
243
|
-
|
|
244
|
-
console.log('');
|
|
245
|
-
console.log(pc.dim('────────────────────────────────────────────'));
|
|
263
|
+
|
|
264
|
+
console.log('');
|
|
265
|
+
console.log(pc.dim('────────────────────────────────────────────'));
|
|
246
266
|
console.log(` Project ${pc.green(projectName)}`);
|
|
247
267
|
console.log(` Directory ${pc.green(targetDir)}`);
|
|
248
268
|
console.log(` Scope ${pc.green(generationOptions.installScope)}`);
|
|
@@ -263,14 +283,14 @@ if (argName || skipPrompts) {
|
|
|
263
283
|
if (generationOptions.externalOptions.length > 0) {
|
|
264
284
|
console.log(` Recommend ${pc.cyan(generationOptions.externalOptions.join(','))}`);
|
|
265
285
|
}
|
|
266
|
-
console.log(pc.dim('────────────────────────────────────────────'));
|
|
267
|
-
console.log('');
|
|
268
|
-
|
|
269
|
-
const preview = generate({ projectName, targetDir, ...generationOptions, dryRun: true });
|
|
270
|
-
if (!preview.success) {
|
|
271
|
-
printResult(preview, targetDir);
|
|
272
|
-
}
|
|
273
|
-
|
|
286
|
+
console.log(pc.dim('────────────────────────────────────────────'));
|
|
287
|
+
console.log('');
|
|
288
|
+
|
|
289
|
+
const preview = generate({ projectName, targetDir, ...generationOptions, dryRun: true });
|
|
290
|
+
if (!preview.success) {
|
|
291
|
+
printResult(preview, targetDir);
|
|
292
|
+
}
|
|
293
|
+
|
|
274
294
|
console.log(pc.yellow('Planned changes: no files have been written yet.'));
|
|
275
295
|
console.log(pc.bold('Project plan:'));
|
|
276
296
|
printSummary(preview.summary);
|
|
@@ -284,34 +304,34 @@ if (argName || skipPrompts) {
|
|
|
284
304
|
printHostPlans(preview.hostPlans, preview.hostSummary);
|
|
285
305
|
printWarnings(preview);
|
|
286
306
|
console.log('');
|
|
287
|
-
|
|
288
|
-
if (generationOptions.dryRun) {
|
|
289
|
-
process.exit(0);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
let proceed = true;
|
|
293
|
-
try {
|
|
294
|
-
proceed = await p.confirm({
|
|
295
|
-
message: 'Confirm generation with this plan?',
|
|
296
|
-
initialValue: true,
|
|
297
|
-
});
|
|
298
|
-
if (p.isCancel(proceed)) proceed = false;
|
|
299
|
-
} catch {
|
|
300
|
-
proceed = true;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (!proceed) {
|
|
304
|
-
p.cancel('Cancelled');
|
|
305
|
-
process.exit(0);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
console.log('');
|
|
309
|
-
const result = generate({ projectName, targetDir, ...generationOptions });
|
|
310
|
-
printResult(result, targetDir);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function printResult(result, targetDir) {
|
|
314
|
-
if (result.success) {
|
|
307
|
+
|
|
308
|
+
if (generationOptions.dryRun) {
|
|
309
|
+
process.exit(0);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let proceed = true;
|
|
313
|
+
try {
|
|
314
|
+
proceed = await p.confirm({
|
|
315
|
+
message: 'Confirm generation with this plan?',
|
|
316
|
+
initialValue: true,
|
|
317
|
+
});
|
|
318
|
+
if (p.isCancel(proceed)) proceed = false;
|
|
319
|
+
} catch {
|
|
320
|
+
proceed = true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!proceed) {
|
|
324
|
+
p.cancel('Cancelled');
|
|
325
|
+
process.exit(0);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
console.log('');
|
|
329
|
+
const result = generate({ projectName, targetDir, ...generationOptions });
|
|
330
|
+
printResult(result, targetDir);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function printResult(result, targetDir) {
|
|
334
|
+
if (result.success) {
|
|
315
335
|
if (result.dryRun) {
|
|
316
336
|
console.log(pc.yellow('\nDry run: no files or directories were written.'));
|
|
317
337
|
console.log(pc.bold('Project plan:'));
|
|
@@ -325,14 +345,14 @@ function printResult(result, targetDir) {
|
|
|
325
345
|
}
|
|
326
346
|
printHostPlans(result.hostPlans, result.hostSummary);
|
|
327
347
|
if (result.warnings.length > 0) {
|
|
328
|
-
console.log(pc.yellow('\nWarning(s):'));
|
|
329
|
-
for (const warning of result.warnings) {
|
|
330
|
-
console.log(pc.yellow(` - ${warning}`));
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
console.log('');
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
348
|
+
console.log(pc.yellow('\nWarning(s):'));
|
|
349
|
+
for (const warning of result.warnings) {
|
|
350
|
+
console.log(pc.yellow(` - ${warning}`));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
console.log('');
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
336
356
|
|
|
337
357
|
console.log(pc.green('\nGeneration complete.\n'));
|
|
338
358
|
console.log(pc.bold('Project install:'));
|
|
@@ -345,86 +365,182 @@ function printResult(result, targetDir) {
|
|
|
345
365
|
printHostPlans(result.hostPlans, result.hostSummary, { summaryOnly: true });
|
|
346
366
|
|
|
347
367
|
printWarnings(result);
|
|
348
|
-
|
|
368
|
+
|
|
349
369
|
console.log(pc.bold('Next steps:'));
|
|
350
370
|
console.log(` ${pc.cyan(`cd ${targetDir}`)}`);
|
|
351
|
-
console.log(` ${pc.cyan('claude')} # Start Claude Code`);
|
|
352
|
-
console.log(` ${pc.cyan('codex')} # Or start Codex`);
|
|
353
|
-
console.log(` ${pc.cyan('opencode')} # Or start OpenCode`);
|
|
371
|
+
console.log(` ${pc.cyan('claude')} # Start Claude Code`);
|
|
372
|
+
console.log(` ${pc.cyan('codex')} # Or start Codex`);
|
|
373
|
+
console.log(` ${pc.cyan('opencode')} # Or start OpenCode`);
|
|
354
374
|
console.log(` Tell your agent: "${pc.yellow('Read Harness/specs/guides/SETUP.md. Bootstrap this project from idea to first vertical slice.')}"`);
|
|
355
|
-
console.log('');
|
|
375
|
+
console.log('');
|
|
356
376
|
console.log(pc.dim(' Keep Harness/specs/guides/SETUP.md as a setup reference; normal sessions start at CLAUDE.md, with Harness/README.md as the routed workflow router.'));
|
|
357
|
-
console.log('');
|
|
358
|
-
|
|
359
|
-
} else {
|
|
360
|
-
console.log(pc.red('\nGeneration failed:'));
|
|
361
|
-
for (const err of result.errors) {
|
|
362
|
-
console.log(pc.red(` - ${err}`));
|
|
363
|
-
}
|
|
364
|
-
process.exit(1);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
377
|
+
console.log('');
|
|
378
|
+
|
|
379
|
+
} else {
|
|
380
|
+
console.log(pc.red('\nGeneration failed:'));
|
|
381
|
+
for (const err of result.errors) {
|
|
382
|
+
console.log(pc.red(` - ${err}`));
|
|
383
|
+
}
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
} // wf-ui skip block end — functions below are module-scoped
|
|
388
|
+
|
|
389
|
+
async function runWfUi(args) {
|
|
390
|
+
const projectRoot = extractFlag(args, '--project') || process.cwd();
|
|
391
|
+
const host = extractFlag(args, '--host') || '127.0.0.1';
|
|
392
|
+
const rawPort = extractFlag(args, '--port') || '0';
|
|
393
|
+
const port = Number.parseInt(rawPort, 10);
|
|
394
|
+
const openBrowser = !hasFlag(args, '--no-open');
|
|
395
|
+
|
|
396
|
+
if (host !== '127.0.0.1') {
|
|
397
|
+
console.error('[wf-ui] host must be 127.0.0.1');
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
401
|
+
console.error('[wf-ui] port must be an integer from 0 to 65535');
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const serverModule = await import('./wf-ui-server/server.mjs');
|
|
407
|
+
const { SessionRegistry } = await import('./wf-ui-server/session-registry.mjs');
|
|
408
|
+
const { attachEventsWs } = await import('./wf-ui-server/ws-events.mjs');
|
|
409
|
+
const { attachTerminalWs } = await import('./wf-ui-server/ws-terminal.mjs');
|
|
410
|
+
const { warmRuntimeCache } = await import('./wf-ui-server/runtime-detector.mjs');
|
|
411
|
+
const { appendSessionEvent, appendTerminalData, persistSession, recordInputRequest } =
|
|
412
|
+
await import('./wf-ui-server/terminal-store.mjs');
|
|
413
|
+
const registry = new SessionRegistry();
|
|
414
|
+
const terminalHub = {};
|
|
415
|
+
const started = await serverModule.startServer({
|
|
416
|
+
projectRoot,
|
|
417
|
+
host,
|
|
418
|
+
port,
|
|
419
|
+
sessionRegistry: registry,
|
|
420
|
+
terminalHub,
|
|
421
|
+
});
|
|
422
|
+
attachEventsWs(started.server, started.token, projectRoot);
|
|
423
|
+
Object.assign(terminalHub, attachTerminalWs(started.server, started.token, registry, {
|
|
424
|
+
onTerminalInput(session, data) {
|
|
425
|
+
try {
|
|
426
|
+
recordInputRequest(projectRoot, session.sessionId, data);
|
|
427
|
+
} catch {
|
|
428
|
+
// The session may not have been persisted yet; stdout/stderr capture still remains authoritative.
|
|
429
|
+
}
|
|
430
|
+
appendTerminalData(projectRoot, session, data, 'stdin');
|
|
431
|
+
},
|
|
432
|
+
onAttachModeChange(session, attachMode) {
|
|
433
|
+
persistSession(projectRoot, session);
|
|
434
|
+
appendSessionEvent(projectRoot, session, { type: 'session.attach-mode', attachMode });
|
|
435
|
+
},
|
|
436
|
+
onSessionState(session, state) {
|
|
437
|
+
persistSession(projectRoot, session);
|
|
438
|
+
appendSessionEvent(projectRoot, session, { type: 'session.state', state });
|
|
439
|
+
},
|
|
440
|
+
}));
|
|
441
|
+
warmRuntimeCache();
|
|
442
|
+
console.log(`[wf-ui] ${started.url}`);
|
|
443
|
+
if (openBrowser) openLocalUrl(started.url);
|
|
444
|
+
await waitForWfUiShutdown(started.server);
|
|
445
|
+
} catch (err) {
|
|
446
|
+
console.error(`[wf-ui] ${err.message}`);
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function waitForWfUiShutdown(server) {
|
|
452
|
+
return new Promise((resolve) => {
|
|
453
|
+
let closing = false;
|
|
454
|
+
const shutdown = () => {
|
|
455
|
+
if (closing) return;
|
|
456
|
+
closing = true;
|
|
457
|
+
server.close(() => resolve());
|
|
458
|
+
};
|
|
459
|
+
process.once('SIGINT', shutdown);
|
|
460
|
+
process.once('SIGTERM', shutdown);
|
|
461
|
+
server.once('close', resolve);
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function openLocalUrl(url) {
|
|
466
|
+
const command = process.platform === 'win32'
|
|
467
|
+
? { file: 'cmd', args: ['/c', 'start', '', url] }
|
|
468
|
+
: process.platform === 'darwin'
|
|
469
|
+
? { file: 'open', args: [url] }
|
|
470
|
+
: { file: 'xdg-open', args: [url] };
|
|
471
|
+
|
|
472
|
+
try {
|
|
473
|
+
const child = spawn(command.file, command.args, {
|
|
474
|
+
detached: true,
|
|
475
|
+
stdio: 'ignore',
|
|
476
|
+
windowsHide: true,
|
|
477
|
+
});
|
|
478
|
+
child.unref();
|
|
479
|
+
} catch (err) {
|
|
480
|
+
console.error(`[wf-ui] could not open browser automatically: ${err.message}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function parseArgs(args) {
|
|
485
|
+
const flags = {
|
|
486
|
+
with: [],
|
|
487
|
+
without: [],
|
|
488
|
+
};
|
|
489
|
+
const positionals = [];
|
|
490
|
+
const errors = [];
|
|
491
|
+
|
|
492
|
+
function readValue(flagName, index) {
|
|
493
|
+
const value = args[index + 1];
|
|
494
|
+
if (!value || value.startsWith('-')) {
|
|
495
|
+
errors.push(`${flagName} requires a value`);
|
|
496
|
+
return { value: undefined, nextIndex: index };
|
|
497
|
+
}
|
|
498
|
+
return { value, nextIndex: index + 1 };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function readEqualsValue(flagName, value) {
|
|
502
|
+
if (!value || value.startsWith('-')) {
|
|
503
|
+
errors.push(`${flagName} requires a value`);
|
|
504
|
+
return undefined;
|
|
505
|
+
}
|
|
506
|
+
return value;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
510
|
+
const arg = args[i];
|
|
511
|
+
|
|
512
|
+
if (arg === '-h') {
|
|
513
|
+
flags.h = true;
|
|
514
|
+
} else if (arg === '--help') {
|
|
515
|
+
flags.help = true;
|
|
516
|
+
} else if (arg === '-y') {
|
|
517
|
+
flags.y = true;
|
|
518
|
+
} else if (arg === '--yes') {
|
|
519
|
+
flags.yes = true;
|
|
520
|
+
} else if (arg === '--dry-run') {
|
|
521
|
+
flags.dryRun = true;
|
|
406
522
|
} else if (arg === '--list-options') {
|
|
407
523
|
flags.listOptions = true;
|
|
408
524
|
} else if (arg === '--json') {
|
|
409
525
|
flags.json = true;
|
|
410
|
-
} else if (arg === '--on-conflict') {
|
|
411
|
-
const parsedValue = readValue('--on-conflict', i);
|
|
412
|
-
flags.onConflict = parsedValue.value;
|
|
413
|
-
i = parsedValue.nextIndex;
|
|
414
|
-
} else if (arg.startsWith('--on-conflict=')) {
|
|
415
|
-
flags.onConflict = readEqualsValue('--on-conflict', arg.slice('--on-conflict='.length));
|
|
416
|
-
} else if (arg === '--with') {
|
|
417
|
-
const parsedValue = readValue('--with', i);
|
|
418
|
-
if (parsedValue.value !== undefined) flags.with.push(parsedValue.value);
|
|
419
|
-
i = parsedValue.nextIndex;
|
|
420
|
-
} else if (arg.startsWith('--with=')) {
|
|
421
|
-
const value = readEqualsValue('--with', arg.slice('--with='.length));
|
|
422
|
-
if (value !== undefined) flags.with.push(value);
|
|
423
|
-
} else if (arg === '--without') {
|
|
424
|
-
const parsedValue = readValue('--without', i);
|
|
425
|
-
if (parsedValue.value !== undefined) flags.without.push(parsedValue.value);
|
|
426
|
-
i = parsedValue.nextIndex;
|
|
427
|
-
} else if (arg.startsWith('--without=')) {
|
|
526
|
+
} else if (arg === '--on-conflict') {
|
|
527
|
+
const parsedValue = readValue('--on-conflict', i);
|
|
528
|
+
flags.onConflict = parsedValue.value;
|
|
529
|
+
i = parsedValue.nextIndex;
|
|
530
|
+
} else if (arg.startsWith('--on-conflict=')) {
|
|
531
|
+
flags.onConflict = readEqualsValue('--on-conflict', arg.slice('--on-conflict='.length));
|
|
532
|
+
} else if (arg === '--with') {
|
|
533
|
+
const parsedValue = readValue('--with', i);
|
|
534
|
+
if (parsedValue.value !== undefined) flags.with.push(parsedValue.value);
|
|
535
|
+
i = parsedValue.nextIndex;
|
|
536
|
+
} else if (arg.startsWith('--with=')) {
|
|
537
|
+
const value = readEqualsValue('--with', arg.slice('--with='.length));
|
|
538
|
+
if (value !== undefined) flags.with.push(value);
|
|
539
|
+
} else if (arg === '--without') {
|
|
540
|
+
const parsedValue = readValue('--without', i);
|
|
541
|
+
if (parsedValue.value !== undefined) flags.without.push(parsedValue.value);
|
|
542
|
+
i = parsedValue.nextIndex;
|
|
543
|
+
} else if (arg.startsWith('--without=')) {
|
|
428
544
|
const value = readEqualsValue('--without', arg.slice('--without='.length));
|
|
429
545
|
if (value !== undefined) flags.without.push(value);
|
|
430
546
|
} else if (arg === '--recommend') {
|
|
@@ -466,23 +582,23 @@ function parseArgs(args) {
|
|
|
466
582
|
flags.hostGlobalDir = readEqualsValue('--host-global-dir', arg.slice('--host-global-dir='.length));
|
|
467
583
|
} else if (arg.startsWith('-')) {
|
|
468
584
|
errors.push(`Unknown flag "${arg}"`);
|
|
469
|
-
} else {
|
|
470
|
-
positionals.push(arg);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return { flags, positionals, errors };
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function printOptions() {
|
|
478
|
-
const catalog = getOptionalCatalog();
|
|
479
|
-
|
|
480
|
-
console.log('');
|
|
481
|
-
console.log(pc.bold('Optional workflow skills:'));
|
|
482
|
-
for (const skill of catalog.skills) {
|
|
483
|
-
console.log(` ${pc.cyan(skill.id)} - ${skill.description}`);
|
|
484
|
-
}
|
|
485
|
-
|
|
585
|
+
} else {
|
|
586
|
+
positionals.push(arg);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return { flags, positionals, errors };
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function printOptions() {
|
|
594
|
+
const catalog = getOptionalCatalog();
|
|
595
|
+
|
|
596
|
+
console.log('');
|
|
597
|
+
console.log(pc.bold('Optional workflow skills:'));
|
|
598
|
+
for (const skill of catalog.skills) {
|
|
599
|
+
console.log(` ${pc.cyan(skill.id)} - ${skill.description}`);
|
|
600
|
+
}
|
|
601
|
+
|
|
486
602
|
console.log('');
|
|
487
603
|
console.log(pc.bold('Presets:'));
|
|
488
604
|
for (const [name, skills] of Object.entries(catalog.presets)) {
|
|
@@ -498,24 +614,24 @@ function printOptions() {
|
|
|
498
614
|
}
|
|
499
615
|
console.log('');
|
|
500
616
|
}
|
|
501
|
-
|
|
502
|
-
function printSummary(summary) {
|
|
503
|
-
console.log(` created ${pc.green(summary.created)}`);
|
|
504
|
-
console.log(` skipped ${pc.yellow(summary.skipped)}`);
|
|
505
|
-
console.log(` backed up ${pc.cyan(summary.backedUp)}`);
|
|
506
|
-
console.log(` overwritten ${pc.cyan(summary.overwritten)}`);
|
|
507
|
-
console.log(` conflicts ${summary.conflicts > 0 ? pc.red(summary.conflicts) : pc.dim(summary.conflicts)}`);
|
|
508
|
-
console.log(` directories ${pc.dim(summary.mkdir)}`);
|
|
509
|
-
console.log('');
|
|
510
|
-
}
|
|
511
|
-
|
|
617
|
+
|
|
618
|
+
function printSummary(summary) {
|
|
619
|
+
console.log(` created ${pc.green(summary.created)}`);
|
|
620
|
+
console.log(` skipped ${pc.yellow(summary.skipped)}`);
|
|
621
|
+
console.log(` backed up ${pc.cyan(summary.backedUp)}`);
|
|
622
|
+
console.log(` overwritten ${pc.cyan(summary.overwritten)}`);
|
|
623
|
+
console.log(` conflicts ${summary.conflicts > 0 ? pc.red(summary.conflicts) : pc.dim(summary.conflicts)}`);
|
|
624
|
+
console.log(` directories ${pc.dim(summary.mkdir)}`);
|
|
625
|
+
console.log('');
|
|
626
|
+
}
|
|
627
|
+
|
|
512
628
|
function printPlan(plan) {
|
|
513
629
|
for (const [label, files] of Object.entries(plan)) {
|
|
514
630
|
if (!files.length) continue;
|
|
515
|
-
console.log(` ${label}:`);
|
|
516
|
-
for (const file of files) {
|
|
517
|
-
console.log(` - ${file}`);
|
|
518
|
-
}
|
|
631
|
+
console.log(` ${label}:`);
|
|
632
|
+
for (const file of files) {
|
|
633
|
+
console.log(` - ${file}`);
|
|
634
|
+
}
|
|
519
635
|
}
|
|
520
636
|
}
|
|
521
637
|
|
|
@@ -533,20 +649,20 @@ function printHostPlans(hostPlans = [], hostSummary = [], { summaryOnly = false
|
|
|
533
649
|
}
|
|
534
650
|
|
|
535
651
|
function printWarnings(result) {
|
|
536
|
-
if (!result.warnings.length) return;
|
|
537
|
-
|
|
538
|
-
console.log(pc.yellow('\nWarning(s):'));
|
|
539
|
-
for (const warning of result.warnings) {
|
|
540
|
-
console.log(pc.yellow(` - ${warning}`));
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
652
|
+
if (!result.warnings.length) return;
|
|
653
|
+
|
|
654
|
+
console.log(pc.yellow('\nWarning(s):'));
|
|
655
|
+
for (const warning of result.warnings) {
|
|
656
|
+
console.log(pc.yellow(` - ${warning}`));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
544
660
|
function printJsonResult(result) {
|
|
545
661
|
// Remove `created` array from output — it is already in the plan, avoid duplication
|
|
546
662
|
const { created, globalCreated, hostCreated, ...rest } = result;
|
|
547
|
-
console.log(JSON.stringify(rest, null, 2));
|
|
548
|
-
if (!result.success) {
|
|
549
|
-
process.exit(1);
|
|
663
|
+
console.log(JSON.stringify(rest, null, 2));
|
|
664
|
+
if (!result.success) {
|
|
665
|
+
process.exit(1);
|
|
550
666
|
}
|
|
551
667
|
}
|
|
552
668
|
|
|
@@ -729,8 +845,8 @@ function scanTarget(targetDir) {
|
|
|
729
845
|
const hasClaude = isDirectory && fs.existsSync(path.join(resolvedDir, 'CLAUDE.md'));
|
|
730
846
|
const hasAgents = isDirectory && fs.existsSync(path.join(resolvedDir, 'AGENTS.md'));
|
|
731
847
|
const hasAgentSkills = isDirectory && fs.existsSync(path.join(resolvedDir, '.agents'));
|
|
732
|
-
const hasCodex = isDirectory && fs.existsSync(path.join(resolvedDir, '.codex'));
|
|
733
|
-
const hasOpencode = isDirectory && (fs.existsSync(path.join(resolvedDir, '.opencode')) || fs.existsSync(path.join(resolvedDir, 'opencode.json')));
|
|
848
|
+
const hasCodex = isDirectory && fs.existsSync(path.join(resolvedDir, '.codex'));
|
|
849
|
+
const hasOpencode = isDirectory && (fs.existsSync(path.join(resolvedDir, '.opencode')) || fs.existsSync(path.join(resolvedDir, 'opencode.json')));
|
|
734
850
|
const hasDocs = isDirectory && fs.existsSync(path.join(resolvedDir, 'docs'));
|
|
735
851
|
const hasReadme = isDirectory && fs.existsSync(path.join(resolvedDir, 'README.md'));
|
|
736
852
|
const hasPackageJson = isDirectory && fs.existsSync(path.join(resolvedDir, 'package.json'));
|
|
@@ -748,8 +864,8 @@ function scanTarget(targetDir) {
|
|
|
748
864
|
hasClaude,
|
|
749
865
|
hasAgents,
|
|
750
866
|
hasAgentSkills,
|
|
751
|
-
hasCodex,
|
|
752
|
-
hasOpencode,
|
|
867
|
+
hasCodex,
|
|
868
|
+
hasOpencode,
|
|
753
869
|
hasDocs,
|
|
754
870
|
hasReadme,
|
|
755
871
|
hasPackageJson,
|
|
@@ -777,8 +893,8 @@ function createJsonScan(scan) {
|
|
|
777
893
|
hasClaude: scan.hasClaude,
|
|
778
894
|
hasAgents: scan.hasAgents,
|
|
779
895
|
hasAgentSkills: scan.hasAgentSkills,
|
|
780
|
-
hasCodex: scan.hasCodex,
|
|
781
|
-
hasOpencode: scan.hasOpencode,
|
|
896
|
+
hasCodex: scan.hasCodex,
|
|
897
|
+
hasOpencode: scan.hasOpencode,
|
|
782
898
|
hasDocs: scan.hasDocs,
|
|
783
899
|
hasReadme: scan.hasReadme,
|
|
784
900
|
hasPackageJson: scan.hasPackageJson,
|
|
@@ -995,7 +1111,7 @@ function printScan(scan) {
|
|
|
995
1111
|
if (scan.hasClaude) console.log(` CLAUDE.md ${pc.yellow('exists')}`);
|
|
996
1112
|
if (scan.hasAgents) console.log(` AGENTS.md ${pc.yellow('exists')}`);
|
|
997
1113
|
if (scan.hasAgentSkills) console.log(` .agents/ ${pc.yellow('exists')}`);
|
|
998
|
-
if (scan.hasCodex) console.log(` .codex/ ${pc.yellow('exists')}`);
|
|
999
|
-
if (scan.hasOpencode) console.log(` opencode ${pc.yellow('exists')} ${pc.dim('(.opencode/ or opencode.json)')}`);
|
|
1114
|
+
if (scan.hasCodex) console.log(` .codex/ ${pc.yellow('exists')}`);
|
|
1115
|
+
if (scan.hasOpencode) console.log(` opencode ${pc.yellow('exists')} ${pc.dim('(.opencode/ or opencode.json)')}`);
|
|
1000
1116
|
if (scan.hasDocs) console.log(` docs/ ${pc.dim('project-owned; Harness stays in Harness/')}`);
|
|
1001
1117
|
}
|