create-harness-vibe-coding 0.8.17 → 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 +22 -0
- package/README-CN.md +2 -0
- package/README.md +3 -0
- package/package.json +5 -2
- package/src/generator.js +613 -97
- package/src/index.js +556 -309
- package/src/prompts.js +18 -0
- 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-command-create.md +58 -0
- package/templates/common/.claude/commands/wf-help.md +5 -0
- package/templates/common/.claude/commands/wf-task-archive.md +26 -0
- package/templates/common/.claude/commands/wf-task-list.md +26 -0
- package/templates/common/.claude/commands/wf-task-record.md +24 -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-agents-docs/SKILL.md +15 -30
- 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-command-create/SKILL.md +37 -0
- package/templates/common/.claude/skills/wf-max/SKILL.md +1 -1
- package/templates/common/.claude/skills/wf-review/SKILL.md +29 -2
- package/templates/common/.claude/skills/wf-task-archive/SKILL.md +28 -0
- package/templates/common/.claude/skills/wf-task-list/SKILL.md +28 -0
- package/templates/common/.claude/skills/wf-task-record/SKILL.md +28 -0
- 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 +105 -47
- package/templates/common/.opencode/commands/wf-command-create.md +61 -0
- package/templates/common/.opencode/commands/wf-help.md +5 -0
- package/templates/common/.opencode/commands/wf-task-archive.md +29 -0
- package/templates/common/.opencode/commands/wf-task-list.md +29 -0
- package/templates/common/.opencode/commands/wf-task-record.md +27 -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 +8 -6
- package/templates/common/Harness/MEMORY.md +11 -0
- package/templates/common/Harness/README.md +21 -38
- 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 +142 -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 +949 -26
- package/templates/common/Harness/scripts/validate-harness.mjs +637 -62
- package/templates/common/Harness/scripts/wf-remove.mjs +42 -5
- 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/guides/SETUP.md +8 -0
- package/templates/common/Harness/specs/protocols/MEMORY_PROTOCOL.md +15 -0
- package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +16 -5
- package/templates/common/Harness/specs/runtime/command-surface.json +229 -0
- package/templates/common/Harness/specs/runtime/subagents.md +6 -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/specs/workflows/WF-MAX.md +5 -0
- package/templates/common/Harness/specs/workflows/WF-STATE.md +66 -0
- 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/STATE.json +6 -0
- package/templates/common/Harness/tasks/_template/ARTIFACTS.md +0 -3
- package/templates/common/Harness/tasks/_template/NOTES.md +0 -3
|
@@ -83,6 +83,7 @@ const PURGEABLE_HARNESS_DATA_PATTERNS = [
|
|
|
83
83
|
const FRAMEWORK_TASK_PATTERNS = [
|
|
84
84
|
/^Harness\/tasks\/_template\//,
|
|
85
85
|
/^Harness\/tasks\/auto\//,
|
|
86
|
+
/^Harness\/tasks\/continuous\//,
|
|
86
87
|
];
|
|
87
88
|
|
|
88
89
|
function isUserData(file) {
|
|
@@ -141,12 +142,36 @@ const BUILT_IN_SKILL_NAMES = [
|
|
|
141
142
|
'wf-auto',
|
|
142
143
|
'wf-auto-spark',
|
|
143
144
|
'wf-browser',
|
|
145
|
+
'wf-command-create',
|
|
144
146
|
'wf-help',
|
|
145
147
|
'wf-learn',
|
|
146
148
|
'wf-max',
|
|
147
149
|
'wf-readme',
|
|
148
150
|
'wf-remove',
|
|
149
151
|
'wf-review',
|
|
152
|
+
'wf-task-archive',
|
|
153
|
+
'wf-task-list',
|
|
154
|
+
'wf-task-record',
|
|
155
|
+
'wf-ui',
|
|
156
|
+
'wf-update',
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const BUILT_IN_COMMAND_NAMES = [
|
|
160
|
+
'wf',
|
|
161
|
+
'wf-auto',
|
|
162
|
+
'wf-auto-spark',
|
|
163
|
+
'wf-browser',
|
|
164
|
+
'wf-command-create',
|
|
165
|
+
'wf-help',
|
|
166
|
+
'wf-learn',
|
|
167
|
+
'wf-max',
|
|
168
|
+
'wf-readme',
|
|
169
|
+
'wf-remove',
|
|
170
|
+
'wf-review',
|
|
171
|
+
'wf-task-archive',
|
|
172
|
+
'wf-task-list',
|
|
173
|
+
'wf-task-record',
|
|
174
|
+
'wf-ui',
|
|
150
175
|
'wf-update',
|
|
151
176
|
];
|
|
152
177
|
|
|
@@ -159,8 +184,10 @@ const KNOWN_FRAMEWORK_FILES = new Set([
|
|
|
159
184
|
`.claude/skills/${name}/SKILL.md`,
|
|
160
185
|
`.agents/skills/${name}/SKILL.md`,
|
|
161
186
|
]),
|
|
162
|
-
|
|
163
|
-
|
|
187
|
+
...BUILT_IN_COMMAND_NAMES.flatMap(name => [
|
|
188
|
+
`.claude/commands/${name}.md`,
|
|
189
|
+
`.opencode/commands/${name}.md`,
|
|
190
|
+
]),
|
|
164
191
|
'.claude/rules/ecc/common.md',
|
|
165
192
|
'opencode.json',
|
|
166
193
|
]);
|
|
@@ -177,6 +204,7 @@ const CLEANUP_DIRS = [
|
|
|
177
204
|
'.claude/skills/wf-auto',
|
|
178
205
|
'.claude/skills/wf-auto-spark',
|
|
179
206
|
'.claude/skills/wf-browser',
|
|
207
|
+
'.claude/skills/wf-command-create',
|
|
180
208
|
'.claude/skills/wf-help',
|
|
181
209
|
'.claude/skills/tdd',
|
|
182
210
|
'.claude/skills/ts-react-frontend',
|
|
@@ -187,7 +215,11 @@ const CLEANUP_DIRS = [
|
|
|
187
215
|
'.claude/skills/wf-max',
|
|
188
216
|
'.claude/skills/wf-readme',
|
|
189
217
|
'.claude/skills/wf-review',
|
|
218
|
+
'.claude/skills/wf-task-archive',
|
|
219
|
+
'.claude/skills/wf-task-list',
|
|
220
|
+
'.claude/skills/wf-task-record',
|
|
190
221
|
'.claude/skills/wf-update',
|
|
222
|
+
'.claude/skills/wf-ui',
|
|
191
223
|
'.claude/skills/wf-remove',
|
|
192
224
|
'.claude/skills/subagent-orchestrator',
|
|
193
225
|
'.claude/skills',
|
|
@@ -196,6 +228,7 @@ const CLEANUP_DIRS = [
|
|
|
196
228
|
'.agents/skills/wf-auto',
|
|
197
229
|
'.agents/skills/wf-auto-spark',
|
|
198
230
|
'.agents/skills/wf-browser',
|
|
231
|
+
'.agents/skills/wf-command-create',
|
|
199
232
|
'.agents/skills/wf-help',
|
|
200
233
|
'.agents/skills/tdd',
|
|
201
234
|
'.agents/skills/ts-react-frontend',
|
|
@@ -206,7 +239,11 @@ const CLEANUP_DIRS = [
|
|
|
206
239
|
'.agents/skills/wf-max',
|
|
207
240
|
'.agents/skills/wf-readme',
|
|
208
241
|
'.agents/skills/wf-review',
|
|
242
|
+
'.agents/skills/wf-task-archive',
|
|
243
|
+
'.agents/skills/wf-task-list',
|
|
244
|
+
'.agents/skills/wf-task-record',
|
|
209
245
|
'.agents/skills/wf-update',
|
|
246
|
+
'.agents/skills/wf-ui',
|
|
210
247
|
'.agents/skills/wf-remove',
|
|
211
248
|
'.agents/skills/subagent-orchestrator',
|
|
212
249
|
'.agents/skills',
|
|
@@ -231,7 +268,7 @@ const CLEANUP_DIRS = [
|
|
|
231
268
|
'Harness/features',
|
|
232
269
|
'Harness/memory',
|
|
233
270
|
'Harness/tasks/_template',
|
|
234
|
-
'Harness/tasks/
|
|
271
|
+
'Harness/tasks/continuous',
|
|
235
272
|
'Harness/tasks',
|
|
236
273
|
'Harness',
|
|
237
274
|
];
|
|
@@ -402,8 +439,8 @@ async function main() {
|
|
|
402
439
|
'.claude/skills/wf-auto/SKILL.md',
|
|
403
440
|
'.agents/skills/wf-auto/SKILL.md',
|
|
404
441
|
'Harness/specs/workflows/WF-AUTO.md',
|
|
405
|
-
'Harness/tasks/
|
|
406
|
-
'Harness/tasks/
|
|
442
|
+
'Harness/tasks/continuous/PROGRESS.md',
|
|
443
|
+
'Harness/tasks/continuous/PLAN.md',
|
|
407
444
|
'Harness/tasks/_template/ARTIFACTS.md',
|
|
408
445
|
'Harness/tasks/_template/NOTES.md',
|
|
409
446
|
'Harness/tasks/_template/PLAN.md',
|
|
@@ -541,6 +541,10 @@ async function main() {
|
|
|
541
541
|
const acceptMerged = readRepeatedFlagValues(args, '--accept-merged');
|
|
542
542
|
const acceptTemplate = readRepeatedFlagValues(args, '--accept-template');
|
|
543
543
|
const ignoreVersion = args.includes('--ignore-version') || args.includes('--force-check');
|
|
544
|
+
const repairMode = args.includes('--repair');
|
|
545
|
+
const effectiveIgnoreVersion = ignoreVersion || repairMode;
|
|
546
|
+
const allowDowngrade = args.includes('--allow-downgrade');
|
|
547
|
+
const allowPrerelease = args.includes('--include-prerelease');
|
|
544
548
|
const explicitSource = readFlagValue(args, '--source-base') || process.env.WF_SOURCE_BASE;
|
|
545
549
|
let source;
|
|
546
550
|
let sourceBase;
|
|
@@ -632,7 +636,7 @@ async function main() {
|
|
|
632
636
|
const localGen = localVersion.generator || '0.0.0';
|
|
633
637
|
const remoteGen = remoteVersion.generator || '0.0.0';
|
|
634
638
|
|
|
635
|
-
if (!
|
|
639
|
+
if (!allowPrerelease && isPrerelease(remoteGen)) {
|
|
636
640
|
if (jsonOut) {
|
|
637
641
|
console.log(JSON.stringify({ status: 'up-to-date', version: localGen, remote: remoteGen, sourceBase }));
|
|
638
642
|
} else {
|
|
@@ -642,27 +646,41 @@ async function main() {
|
|
|
642
646
|
}
|
|
643
647
|
|
|
644
648
|
const versionCmp = cmpSemver(remoteGen, localGen);
|
|
649
|
+
const reportDowngrade = !sourceStable;
|
|
645
650
|
|
|
646
|
-
if (
|
|
647
|
-
const reportDowngrade = !sourceStable;
|
|
651
|
+
if (versionCmp < 0 && !allowDowngrade) {
|
|
648
652
|
if (jsonOut) {
|
|
649
653
|
console.log(JSON.stringify({
|
|
650
|
-
status:
|
|
654
|
+
status: reportDowngrade ? 'downgrade-refused' : 'up-to-date',
|
|
651
655
|
version: localGen,
|
|
652
656
|
remote: remoteGen,
|
|
653
657
|
sourceBase,
|
|
654
658
|
}));
|
|
655
|
-
} else if (
|
|
659
|
+
} else if (reportDowngrade) {
|
|
656
660
|
console.log(`WARN: Remote (v${remoteGen}) is OLDER than local (v${localGen}). Downgrade refused.`);
|
|
661
|
+
} else {
|
|
662
|
+
console.log(`Already up to date (v${localGen}). Remote ${remoteGen} is older and ignored.`);
|
|
663
|
+
}
|
|
664
|
+
if (reportDowngrade) process.exitCode = 1;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
if (!effectiveIgnoreVersion && versionCmp <= 0) {
|
|
669
|
+
if (jsonOut) {
|
|
670
|
+
console.log(JSON.stringify({
|
|
671
|
+
status: 'up-to-date',
|
|
672
|
+
version: localGen,
|
|
673
|
+
remote: remoteGen,
|
|
674
|
+
sourceBase,
|
|
675
|
+
}));
|
|
657
676
|
} else {
|
|
658
677
|
console.log(`Already up to date (v${localGen})`);
|
|
659
678
|
}
|
|
660
|
-
if (versionCmp < 0 && reportDowngrade) process.exitCode = 1;
|
|
661
679
|
return;
|
|
662
680
|
}
|
|
663
681
|
|
|
664
|
-
if (
|
|
665
|
-
if (!jsonOut) console.log(
|
|
682
|
+
if (effectiveIgnoreVersion) {
|
|
683
|
+
if (!jsonOut) console.log(`Version check bypassed (${repairMode ? '--repair' : '--ignore-version'}). Comparing files anyway.`);
|
|
666
684
|
}
|
|
667
685
|
|
|
668
686
|
const remoteChecksums = remoteVersion.checksums || {};
|
|
@@ -760,6 +778,9 @@ async function main() {
|
|
|
760
778
|
: 0;
|
|
761
779
|
return {
|
|
762
780
|
mode: 'script-first-ai-conflicts',
|
|
781
|
+
multiScopeDryRunCommand: 'node Harness/scripts/wf-update-runner.mjs --json',
|
|
782
|
+
multiScopeSafeApplyCommand: 'node Harness/scripts/wf-update-runner.mjs --apply-safe --json',
|
|
783
|
+
multiScopeFinalizeCommand: 'node Harness/scripts/wf-update-runner.mjs --finalize --json',
|
|
763
784
|
dryRunJsonCommand: 'node Harness/scripts/wf-update-check.mjs --json',
|
|
764
785
|
safeApplyCommand: 'node Harness/scripts/wf-update-check.mjs --apply-safe',
|
|
765
786
|
strictApplyCommand: 'node Harness/scripts/wf-update-check.mjs --apply',
|
|
@@ -776,7 +797,10 @@ async function main() {
|
|
|
776
797
|
aiMergeRequiredTruncated: truncated > 0 ? truncated : undefined,
|
|
777
798
|
conflictPolicy: 'Use the script for SAFE/NEW/adopted files. For each CONFLICT file, compare local content with templateHint/remoteUrl, then record the decision with --accept-local, --accept-merged, or --accept-template. Do not hand-edit Harness/.harness-version.',
|
|
778
799
|
postUpdateCommands: [
|
|
800
|
+
'node Harness/scripts/sync-host-global.mjs --json',
|
|
801
|
+
'node Harness/scripts/sync-host-global.mjs --apply --json',
|
|
779
802
|
'node Harness/scripts/validate-harness.mjs',
|
|
803
|
+
'node Harness/scripts/validate-harness.mjs --manifest-audit',
|
|
780
804
|
'node Harness/scripts/scan-clean.mjs --json',
|
|
781
805
|
],
|
|
782
806
|
bootstrapDebtCommand: 'node Harness/scripts/validate-harness.mjs --strict',
|
|
@@ -1010,8 +1034,11 @@ async function main() {
|
|
|
1010
1034
|
if (jsonOut) {
|
|
1011
1035
|
const verbose = args.includes('--verbose') || args.includes('--full-plan');
|
|
1012
1036
|
const jsonPlan = buildJsonPlan();
|
|
1037
|
+
const effectiveStatus = repairMode && versionCmp <= 0 && !localVersion.partialUpdate
|
|
1038
|
+
? 'repair-check'
|
|
1039
|
+
: (localVersion.partialUpdate ? 'partial-update' : 'update-available');
|
|
1013
1040
|
console.log(JSON.stringify({
|
|
1014
|
-
status:
|
|
1041
|
+
status: effectiveStatus,
|
|
1015
1042
|
from: localGen,
|
|
1016
1043
|
to: remoteGen,
|
|
1017
1044
|
sourceBase,
|
|
@@ -1026,6 +1053,7 @@ async function main() {
|
|
|
1026
1053
|
skipped: plan.skipped.length,
|
|
1027
1054
|
// Token-safe by default: attach the full plan only when --verbose / --full-plan is passed.
|
|
1028
1055
|
...(verbose ? { plan: jsonPlan } : {}),
|
|
1056
|
+
repair: repairMode || undefined,
|
|
1029
1057
|
agent: buildAgentHints(jsonPlan),
|
|
1030
1058
|
}, null, 2));
|
|
1031
1059
|
return;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const SCRIPT_ROOT = path.resolve(__dirname, '..', '..');
|
|
10
|
+
const rawArgs = process.argv.slice(2);
|
|
11
|
+
const JSON_OUT = rawArgs.includes('--json');
|
|
12
|
+
|
|
13
|
+
function readFlagValue(args, flagName) {
|
|
14
|
+
const idx = args.indexOf(flagName);
|
|
15
|
+
if (idx === -1) return null;
|
|
16
|
+
const value = args[idx + 1];
|
|
17
|
+
return value && !value.startsWith('--') ? value : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function stripFlagWithValue(args, flagName) {
|
|
21
|
+
const next = [];
|
|
22
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
23
|
+
if (args[i] === flagName) {
|
|
24
|
+
i += 1;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (args[i].startsWith(`${flagName}=`)) continue;
|
|
28
|
+
next.push(args[i]);
|
|
29
|
+
}
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function normalizePath(value) {
|
|
34
|
+
return String(value || '').replace(/\\/g, '/');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function comparablePath(value) {
|
|
38
|
+
const resolved = path.resolve(value);
|
|
39
|
+
return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function samePath(a, b) {
|
|
43
|
+
if (!a || !b) return false;
|
|
44
|
+
return comparablePath(a) === comparablePath(b);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseSemver(value) {
|
|
48
|
+
if (!value || typeof value !== 'string') return [0, 0, 0];
|
|
49
|
+
return value.replace(/^[^0-9]*/, '').split('.').slice(0, 3).map(part => Number(part) || 0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function cmpSemver(a, b) {
|
|
53
|
+
const left = parseSemver(a);
|
|
54
|
+
const right = parseSemver(b);
|
|
55
|
+
for (let i = 0; i < 3; i += 1) {
|
|
56
|
+
if ((left[i] || 0) > (right[i] || 0)) return 1;
|
|
57
|
+
if ((left[i] || 0) < (right[i] || 0)) return -1;
|
|
58
|
+
}
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function shouldAutoRepair(firstJson, targetVersion) {
|
|
63
|
+
if (firstJson?.status !== 'up-to-date') return false;
|
|
64
|
+
const remote = firstJson.remote || firstJson.to || null;
|
|
65
|
+
if (remote && targetVersion && cmpSemver(remote, targetVersion) < 0) return false;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function defaultGlobalDir() {
|
|
70
|
+
return process.env.HARNESS_GLOBAL_HOME
|
|
71
|
+
|| path.join(os.homedir(), '.harness', 'create-harness-vibe-coding');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function versionPath(root) {
|
|
75
|
+
return path.join(root, 'Harness', '.harness-version');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function updateScriptPath(root) {
|
|
79
|
+
return path.join(root, 'Harness', 'scripts', 'wf-update-check.mjs');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function syncScriptPath(root) {
|
|
83
|
+
return path.join(root, 'Harness', 'scripts', 'sync-host-global.mjs');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function validateScriptPath(root) {
|
|
87
|
+
return path.join(root, 'Harness', 'scripts', 'validate-harness.mjs');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function scanCleanScriptPath(root) {
|
|
91
|
+
return path.join(root, 'Harness', 'scripts', 'scan-clean.mjs');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readVersion(root) {
|
|
95
|
+
try {
|
|
96
|
+
const file = versionPath(root);
|
|
97
|
+
if (!fs.existsSync(file)) return null;
|
|
98
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function hasRunnableUpdater(root) {
|
|
105
|
+
return fs.existsSync(versionPath(root)) && fs.existsSync(updateScriptPath(root));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function discoverGlobalRuntime(projectVersion) {
|
|
109
|
+
const candidates = [];
|
|
110
|
+
if (projectVersion?.globalDir) candidates.push(projectVersion.globalDir);
|
|
111
|
+
if (process.env.HARNESS_GLOBAL_HOME) candidates.push(process.env.HARNESS_GLOBAL_HOME);
|
|
112
|
+
candidates.push(defaultGlobalDir());
|
|
113
|
+
const scriptVersion = readVersion(SCRIPT_ROOT);
|
|
114
|
+
if (scriptVersion?.installScope === 'global') candidates.push(SCRIPT_ROOT);
|
|
115
|
+
|
|
116
|
+
for (const candidate of candidates) {
|
|
117
|
+
if (!candidate) continue;
|
|
118
|
+
const resolved = path.resolve(candidate);
|
|
119
|
+
if (hasRunnableUpdater(resolved)) return resolved;
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function dedupeTargets(targets) {
|
|
125
|
+
const seen = new Set();
|
|
126
|
+
const result = [];
|
|
127
|
+
for (const target of targets) {
|
|
128
|
+
const key = comparablePath(target.root);
|
|
129
|
+
if (seen.has(key)) continue;
|
|
130
|
+
seen.add(key);
|
|
131
|
+
result.push(target);
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function discoverTargets(projectRoot) {
|
|
137
|
+
const targets = [];
|
|
138
|
+
const projectVersion = readVersion(projectRoot);
|
|
139
|
+
const projectHasUpdater = hasRunnableUpdater(projectRoot);
|
|
140
|
+
const globalRoot = discoverGlobalRuntime(projectVersion);
|
|
141
|
+
|
|
142
|
+
if (projectHasUpdater && !samePath(projectRoot, globalRoot)) {
|
|
143
|
+
targets.push({
|
|
144
|
+
scope: projectVersion?.installScope === 'global' && projectVersion?.globalDir ? 'project-bridge' : 'project',
|
|
145
|
+
root: projectRoot,
|
|
146
|
+
version: projectVersion?.generator || null,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (globalRoot) {
|
|
151
|
+
const globalVersion = readVersion(globalRoot);
|
|
152
|
+
targets.push({
|
|
153
|
+
scope: 'global',
|
|
154
|
+
root: globalRoot,
|
|
155
|
+
version: globalVersion?.generator || null,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return dedupeTargets(targets);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function runNode(root, scriptPath, args, { parseJson = false } = {}) {
|
|
163
|
+
const result = spawnSync(process.execPath, [scriptPath, ...args], {
|
|
164
|
+
cwd: root,
|
|
165
|
+
encoding: 'utf8',
|
|
166
|
+
env: process.env,
|
|
167
|
+
});
|
|
168
|
+
const stdout = result.stdout || '';
|
|
169
|
+
const stderr = result.stderr || '';
|
|
170
|
+
let json = null;
|
|
171
|
+
if (parseJson && stdout.trim()) {
|
|
172
|
+
try {
|
|
173
|
+
json = JSON.parse(stdout.trim());
|
|
174
|
+
} catch {
|
|
175
|
+
json = { rawOutput: stdout };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
status: result.status ?? 1,
|
|
180
|
+
stdout,
|
|
181
|
+
stderr,
|
|
182
|
+
...(parseJson ? { json } : {}),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function wantsApply(args) {
|
|
187
|
+
return args.includes('--apply') || args.includes('--apply-safe') || args.includes('--safe-only') || args.includes('--finalize');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function wantsRepair(args) {
|
|
191
|
+
return args.includes('--repair') || args.includes('--ignore-version') || args.includes('--force-check');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function ensureJsonArg(args) {
|
|
195
|
+
return args.includes('--json') ? args : [...args, '--json'];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function withRepair(args) {
|
|
199
|
+
return wantsRepair(args) ? args : [...args, '--repair'];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function runUpdateForTarget(target, forwardArgs) {
|
|
203
|
+
const parseJson = forwardArgs.includes('--json');
|
|
204
|
+
const script = updateScriptPath(target.root);
|
|
205
|
+
const first = runNode(target.root, script, forwardArgs, { parseJson });
|
|
206
|
+
const runs = [{ command: `node Harness/scripts/wf-update-check.mjs ${forwardArgs.join(' ')}`.trim(), ...first }];
|
|
207
|
+
let effective = first;
|
|
208
|
+
|
|
209
|
+
if (
|
|
210
|
+
parseJson
|
|
211
|
+
&& first.status === 0
|
|
212
|
+
&& shouldAutoRepair(first.json, target.version)
|
|
213
|
+
&& !wantsRepair(forwardArgs)
|
|
214
|
+
) {
|
|
215
|
+
const repairArgs = withRepair(forwardArgs);
|
|
216
|
+
const repair = runNode(target.root, script, repairArgs, { parseJson });
|
|
217
|
+
runs.push({ command: `node Harness/scripts/wf-update-check.mjs ${repairArgs.join(' ')}`.trim(), ...repair });
|
|
218
|
+
effective = repair;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return { ...target, update: effective, runs };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function runPostUpdate(target, forwardArgs) {
|
|
225
|
+
const post = [];
|
|
226
|
+
const apply = wantsApply(forwardArgs);
|
|
227
|
+
const syncScript = syncScriptPath(target.root);
|
|
228
|
+
if (target.scope === 'global' && fs.existsSync(syncScript)) {
|
|
229
|
+
const syncArgs = apply ? ['--apply', '--json'] : ['--json'];
|
|
230
|
+
post.push({
|
|
231
|
+
step: 'sync-host-global',
|
|
232
|
+
...runNode(target.root, syncScript, syncArgs, { parseJson: true }),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (apply && fs.existsSync(validateScriptPath(target.root))) {
|
|
236
|
+
post.push({
|
|
237
|
+
step: 'validate',
|
|
238
|
+
...runNode(target.root, validateScriptPath(target.root), [], { parseJson: false }),
|
|
239
|
+
});
|
|
240
|
+
post.push({
|
|
241
|
+
step: 'manifest-audit',
|
|
242
|
+
...runNode(target.root, validateScriptPath(target.root), ['--manifest-audit'], { parseJson: false }),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (apply && target.scope !== 'global' && fs.existsSync(scanCleanScriptPath(target.root))) {
|
|
246
|
+
post.push({
|
|
247
|
+
step: 'scan-clean',
|
|
248
|
+
...runNode(target.root, scanCleanScriptPath(target.root), ['--json'], { parseJson: true }),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return post;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function printText(result) {
|
|
255
|
+
if (!result.targets.length) {
|
|
256
|
+
console.error(result.message);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
for (const target of result.targets) {
|
|
260
|
+
console.log(`\n[${target.scope}] ${target.root}`);
|
|
261
|
+
for (const run of target.runs || []) {
|
|
262
|
+
if (run.stdout) process.stdout.write(run.stdout);
|
|
263
|
+
if (run.stderr) process.stderr.write(run.stderr);
|
|
264
|
+
}
|
|
265
|
+
for (const post of target.post || []) {
|
|
266
|
+
console.log(`[post:${post.step}] exit ${post.status}`);
|
|
267
|
+
if (post.stdout) process.stdout.write(post.stdout);
|
|
268
|
+
if (post.stderr) process.stderr.write(post.stderr);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function main() {
|
|
274
|
+
const projectRoot = path.resolve(readFlagValue(rawArgs, '--project') || process.cwd());
|
|
275
|
+
const forwardArgs = stripFlagWithValue(rawArgs, '--project');
|
|
276
|
+
const effectiveArgs = JSON_OUT ? ensureJsonArg(forwardArgs) : forwardArgs;
|
|
277
|
+
const targets = discoverTargets(projectRoot);
|
|
278
|
+
|
|
279
|
+
if (targets.length === 0) {
|
|
280
|
+
const result = {
|
|
281
|
+
success: false,
|
|
282
|
+
status: 'no-harness-target',
|
|
283
|
+
projectRoot: normalizePath(projectRoot),
|
|
284
|
+
message: 'No project Harness updater or global Harness runtime was found. No project files were modified.',
|
|
285
|
+
targets: [],
|
|
286
|
+
};
|
|
287
|
+
if (JSON_OUT) console.log(JSON.stringify(result, null, 2));
|
|
288
|
+
else printText(result);
|
|
289
|
+
process.exitCode = 1;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const updatedTargets = targets.map(target => {
|
|
294
|
+
const withUpdate = runUpdateForTarget(target, effectiveArgs);
|
|
295
|
+
return {
|
|
296
|
+
...withUpdate,
|
|
297
|
+
post: runPostUpdate(withUpdate, effectiveArgs),
|
|
298
|
+
};
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const failures = [];
|
|
302
|
+
for (const target of updatedTargets) {
|
|
303
|
+
if (target.update.status !== 0) failures.push(`${target.scope}: update exit ${target.update.status}`);
|
|
304
|
+
for (const post of target.post || []) {
|
|
305
|
+
if (post.status !== 0) failures.push(`${target.scope}: ${post.step} exit ${post.status}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const result = {
|
|
310
|
+
success: failures.length === 0,
|
|
311
|
+
status: failures.length === 0 ? 'ok' : 'failed',
|
|
312
|
+
projectRoot: normalizePath(projectRoot),
|
|
313
|
+
targets: updatedTargets.map(target => ({
|
|
314
|
+
...target,
|
|
315
|
+
root: normalizePath(target.root),
|
|
316
|
+
})),
|
|
317
|
+
...(failures.length ? { failures } : {}),
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
if (JSON_OUT) console.log(JSON.stringify(result, null, 2));
|
|
321
|
+
else printText(result);
|
|
322
|
+
if (failures.length) process.exitCode = 1;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
main();
|
|
@@ -39,5 +39,40 @@
|
|
|
39
39
|
"claimMinReadRatioPercent": 95,
|
|
40
40
|
"claimMinUpliftPoints": 2
|
|
41
41
|
}
|
|
42
|
+
},
|
|
43
|
+
"server": {
|
|
44
|
+
"host": "127.0.0.1",
|
|
45
|
+
"port": 0
|
|
46
|
+
},
|
|
47
|
+
"terminal": {
|
|
48
|
+
"enabled": false,
|
|
49
|
+
"attachMode": false,
|
|
50
|
+
"defaultRuntime": "codex"
|
|
51
|
+
},
|
|
52
|
+
"peers": {
|
|
53
|
+
"allowlist": [
|
|
54
|
+
"claude",
|
|
55
|
+
"cc",
|
|
56
|
+
"codex",
|
|
57
|
+
"opencode",
|
|
58
|
+
"gemini",
|
|
59
|
+
"qwen",
|
|
60
|
+
"deepseek",
|
|
61
|
+
"pi",
|
|
62
|
+
"aider",
|
|
63
|
+
"goose",
|
|
64
|
+
"amp",
|
|
65
|
+
"crush",
|
|
66
|
+
"cline",
|
|
67
|
+
"plandex",
|
|
68
|
+
"openclaw",
|
|
69
|
+
"chatgpt",
|
|
70
|
+
"openai"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"ui": {
|
|
74
|
+
"theme": "auto",
|
|
75
|
+
"language": "zh",
|
|
76
|
+
"reducedMotion": false
|
|
42
77
|
}
|
|
43
78
|
}
|
|
@@ -55,6 +55,14 @@ Claude or Codex must follow this order during bootstrap. This sequence is broade
|
|
|
55
55
|
|
|
56
56
|
Before writing, identify the project state:
|
|
57
57
|
|
|
58
|
+
Choose the install scope before scaffold writes:
|
|
59
|
+
|
|
60
|
+
- `--install-scope project` (default): full Harness scaffold is project-local.
|
|
61
|
+
- `--install-scope global`: write shared runtime assets to the selected global Harness directory and copy Claude Code, Codex, and OpenCode command/skill surfaces to host-global directories while keeping `Harness/tasks/`, `Harness/PROGRESS.md`, project memory, research, architecture, and project settings under the target project.
|
|
62
|
+
- Use `--global-dir <dir>` when the global runtime location must be explicit.
|
|
63
|
+
- Use `--host-global-dir <dir>` only when the host-global copy base must be explicit; the installer creates `claude/`, `codex/`, and `opencode/` subdirectories under it.
|
|
64
|
+
- Project settings in `Harness/settings.json` override global settings defaults. Existing user-authored files at config, command, skill, or agent paths are preserved or surfaced for review unless they carry a Harness ownership marker.
|
|
65
|
+
|
|
58
66
|
| Project state | Required action |
|
|
59
67
|
| --- | --- |
|
|
60
68
|
| Empty or new project | Run the scaffold, then follow this file for 0-1 bootstrap |
|
|
@@ -10,6 +10,21 @@ Memory is downstream of evidence. It records reusable patterns, not raw transcri
|
|
|
10
10
|
- **L2 = Startup Digest**: `Harness/memory/startup-hints.md`. Lightweight hints loaded at session start. Not a replacement for full router.
|
|
11
11
|
- **L3 = Detailed Durable Memory**: `Harness/memory/*.md` files. Loaded only when scenario matches via `routes.md`.
|
|
12
12
|
|
|
13
|
+
## Project/Global Memory Boundary
|
|
14
|
+
|
|
15
|
+
- `Harness/tasks/` and `Harness/PROGRESS.md` are always project-local, even when the Harness runtime is installed globally.
|
|
16
|
+
- Project memory lives in `Harness/memory/` and records repo-specific lessons, preferences, and tool patterns.
|
|
17
|
+
- Global memory may live under the selected global Harness directory for cross-project lessons and preferences.
|
|
18
|
+
- Global memory must never store project task state, secrets, raw transcripts, or private project facts.
|
|
19
|
+
|
|
20
|
+
## Project/Global Settings Boundary
|
|
21
|
+
|
|
22
|
+
- Project settings live in `Harness/settings.json` and override global settings.
|
|
23
|
+
- Global settings defaults live in the selected global Harness runtime under `Harness/settings.json`.
|
|
24
|
+
- Host-global settings are copied, not symlinked, into Claude Code, Codex, and OpenCode user config directories.
|
|
25
|
+
- Settings and memory must never store secrets; use external secret stores or environment variables.
|
|
26
|
+
- Existing user-authored config, command, skill, or agent files are user-owned unless they carry a Harness ownership marker.
|
|
27
|
+
|
|
13
28
|
## When To Write Memory
|
|
14
29
|
|
|
15
30
|
Write memory when any condition applies:
|
|
@@ -11,7 +11,7 @@ Archived tasks move to `Harness/tasks/_archive/YYYY/<task-id>/`.
|
|
|
11
11
|
## What Is Never Archived
|
|
12
12
|
|
|
13
13
|
- `Harness/tasks/_template/` - scaffold template, never moved.
|
|
14
|
-
- `Harness/tasks/
|
|
14
|
+
- `Harness/tasks/continuous/` - WF-AUTO/WF-AUTO-SPARK permanent state capsule, never moved unless
|
|
15
15
|
explicitly allowed by WF-AUTO docs.
|
|
16
16
|
- `Harness/tasks/_archive/` - the archive directory itself.
|
|
17
17
|
- Active, blocked, in-progress, running, pending, or needs-user-decision tasks.
|
|
@@ -46,21 +46,32 @@ machine state.
|
|
|
46
46
|
- Do NOT delete historical evidence.
|
|
47
47
|
- `Harness/PROGRESS.md` keeps the last 5 non-archived task entries in the Task
|
|
48
48
|
Index.
|
|
49
|
-
- When outer task capsules exceed 5 completed/abandoned/obsolete tasks,
|
|
50
|
-
the
|
|
49
|
+
- When outer task capsules exceed 5 completed/abandoned/obsolete tasks, remind
|
|
50
|
+
the user to run the task archive command. Do not auto-archive during unrelated
|
|
51
|
+
validation or closeout unless the user explicitly invokes archive apply mode.
|
|
51
52
|
- The validator (`Harness/scripts/validate-harness.mjs`) warns when
|
|
52
53
|
`Harness/tasks/` holds more than 5 outer task capsules (excluding `_archive`,
|
|
53
54
|
`_template`, `auto`) and fails in `--strict` mode.
|
|
54
55
|
|
|
55
56
|
## Script
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
User-facing command:
|
|
58
59
|
|
|
59
|
-
-
|
|
60
|
+
- Codex: `$wf-task-archive` or `/skills wf-task-archive`.
|
|
61
|
+
- Claude Code/OpenCode: `/wf-task-archive`.
|
|
62
|
+
|
|
63
|
+
Underlying implementation: `Harness/scripts/task-state.mjs archive`.
|
|
64
|
+
|
|
65
|
+
- Default: dry-run (respects `--keep` cap, archives oldest-eligible up to cap).
|
|
60
66
|
- `--apply` to execute.
|
|
67
|
+
- Explicit `--apply` with no `--task` filter: archives **ALL** eligible tasks, bypassing `--keep`.
|
|
61
68
|
- `--keep 5` to set the non-archived task threshold.
|
|
62
69
|
- `--task <task-id>` to archive a specific task.
|
|
63
70
|
- `--json` for machine-readable output.
|
|
71
|
+
- After archiving, `Harness/tasks/_archive/INDEX.md` is regenerated with a full
|
|
72
|
+
task graph: roots, dependency chains, blocks relationships, and per-task details
|
|
73
|
+
(status, phase, goal, depends-on, blocks). The graph covers **all** archived tasks
|
|
74
|
+
across all years, so it stays accurate as tasks accumulate.
|
|
64
75
|
- Run `node Harness/scripts/task-state.mjs archive --dry-run --json` before
|
|
65
76
|
applying a risky cleanup.
|
|
66
77
|
|