coding-agent-harness 1.0.2 → 1.0.5
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 +32 -0
- package/CONTRIBUTING.md +98 -0
- package/LICENSE +661 -21
- package/LICENSE-EXCEPTION.md +37 -0
- package/README.md +244 -87
- package/README.zh-CN.md +77 -35
- package/SKILL.md +32 -24
- package/docs-release/README.md +9 -5
- package/docs-release/architecture/overview.md +17 -5
- package/docs-release/architecture/overview.zh-CN.md +9 -5
- package/docs-release/architecture/system-explainer/01-system-overview.md +217 -0
- package/docs-release/architecture/system-explainer/02-module-dependency.md +257 -0
- package/docs-release/architecture/system-explainer/03-task-lifecycle.md +304 -0
- package/docs-release/architecture/system-explainer/04-check-and-governance.md +239 -0
- package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
- package/docs-release/architecture/system-explainer/06-preset-and-migration.md +303 -0
- package/docs-release/architecture/system-explainer/README.md +67 -0
- package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +226 -0
- package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +263 -0
- package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +319 -0
- package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +250 -0
- package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +290 -0
- package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +323 -0
- package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
- package/docs-release/assets/dashboard-overview.png +0 -0
- package/docs-release/guides/agent-installation.en-US.md +39 -15
- package/docs-release/guides/agent-installation.md +43 -16
- package/docs-release/guides/contributing.md +100 -0
- package/docs-release/guides/contributing.zh-CN.md +99 -0
- package/docs-release/guides/document-audience-and-surfaces.en-US.md +3 -2
- package/docs-release/guides/document-audience-and-surfaces.md +3 -2
- package/docs-release/guides/full-legacy-migration-subagent-strategy.md +2 -2
- package/docs-release/guides/full-legacy-migration-subagent-strategy.zh-CN.md +2 -2
- package/docs-release/guides/legacy-migration-agent-prompt.md +0 -11
- package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +0 -11
- package/docs-release/guides/migration-playbook.en-US.md +14 -15
- package/docs-release/guides/migration-playbook.md +14 -15
- package/docs-release/guides/parent-control-repository-pattern.en-US.md +7 -5
- package/docs-release/guides/parent-control-repository-pattern.md +7 -5
- package/docs-release/guides/preset-development.md +238 -0
- package/docs-release/guides/repository-operating-models.en-US.md +5 -4
- package/docs-release/guides/repository-operating-models.md +5 -4
- package/docs-release/guides/task-state-machine.en-US.md +224 -0
- package/docs-release/guides/task-state-machine.md +231 -0
- package/docs-release/intl/en-US.md +1 -1
- package/docs-release/intl/zh-CN.md +1 -1
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/INDEX.md +60 -0
- package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/findings.md +7 -0
- package/package.json +10 -4
- package/presets/legacy-migration/checks/preset-check.mjs +3 -0
- package/presets/legacy-migration/preset.yaml +134 -0
- package/presets/legacy-migration/scripts/plan-work-queue.mjs +4 -0
- package/presets/legacy-migration/scripts/scaffold-task-contracts.mjs +4 -0
- package/presets/legacy-migration/templates/execution_strategy.append.md +18 -0
- package/presets/legacy-migration/templates/findings.seed.md +17 -0
- package/presets/legacy-migration/templates/review.seed.md +12 -0
- package/presets/legacy-migration/templates/task_plan.append.md +9 -0
- package/presets/legacy-migration/templates/visual_map.append.md +12 -0
- package/presets/legacy-migration/workbench/dashboard-panels.yaml +2 -0
- package/presets/legacy-migration/workbench/migration-queue.schema.json +23 -0
- package/presets/lesson-sedimentation/preset.yaml +23 -0
- package/presets/lesson-sedimentation/templates/prompt.md +23 -0
- package/presets/module/preset.yaml +25 -0
- package/presets/module/templates/execution_strategy.append.md +8 -0
- package/presets/module/templates/task_plan.append.md +17 -0
- package/presets/standard-task/preset.yaml +31 -0
- package/presets/standard-task/templates/task_plan.append.md +7 -0
- package/references/adversarial-review-standard.md +2 -2
- package/references/agents-md-pattern.md +2 -2
- package/references/delivery-operating-model-standard.md +3 -3
- package/references/docs-directory-standard.md +6 -7
- package/references/harness-ledger.md +53 -96
- package/references/lessons-governance.md +88 -93
- package/references/module-parallel-standard.md +14 -14
- package/references/planning-loop.md +12 -6
- package/references/pull-request-standard.md +118 -0
- package/references/repo-governance-standard.md +11 -2
- package/references/review-routing-standard.md +7 -1
- package/references/ssot-governance.md +67 -59
- package/references/taskr-gap-analysis.md +600 -0
- package/references/walkthrough-closeout.md +7 -7
- package/scripts/check-harness.mjs +40 -301
- package/scripts/commands/dashboard-command.mjs +67 -0
- package/scripts/commands/migration-command.mjs +126 -0
- package/scripts/commands/preset-command.mjs +73 -0
- package/scripts/commands/task-command.mjs +328 -0
- package/scripts/harness.mjs +59 -260
- package/scripts/lib/capability-registry.mjs +82 -28
- package/scripts/lib/check-module-parallel.mjs +230 -0
- package/scripts/lib/check-profiles.mjs +90 -228
- package/scripts/lib/check-task-contracts.mjs +55 -0
- package/scripts/lib/core-shared.mjs +65 -2
- package/scripts/lib/dashboard-data.mjs +155 -24
- package/scripts/lib/dashboard-workbench.mjs +131 -12
- package/scripts/lib/dashboard-writer.mjs +20 -4
- package/scripts/lib/git-status-summary.mjs +46 -0
- package/scripts/lib/governance-index-generator.mjs +174 -0
- package/scripts/lib/governance-sync.mjs +611 -0
- package/scripts/lib/governance-table-boundary.mjs +175 -0
- package/scripts/lib/harness-core.mjs +6 -0
- package/scripts/lib/lesson-maintenance.mjs +36 -29
- package/scripts/lib/markdown-utils.mjs +33 -0
- package/scripts/lib/migration-planner.mjs +4 -6
- package/scripts/lib/migration-support.mjs +1 -1
- package/scripts/lib/phase-kind.mjs +50 -0
- package/scripts/lib/preset-audit-contracts.mjs +37 -0
- package/scripts/lib/preset-engine.mjs +494 -0
- package/scripts/lib/preset-registry.mjs +776 -0
- package/scripts/lib/preset-resource-contracts.mjs +83 -0
- package/scripts/lib/review-confirm-git-gate.mjs +248 -0
- package/scripts/lib/status-builder.mjs +88 -0
- package/scripts/lib/status-dashboard-renderer.mjs +105 -0
- package/scripts/lib/subagent-authorization-audit.mjs +196 -0
- package/scripts/lib/task-audit-metadata.mjs +385 -0
- package/scripts/lib/task-audit-migration.mjs +350 -0
- package/scripts/lib/task-completion-consistency.mjs +26 -0
- package/scripts/lib/task-index.mjs +93 -0
- package/scripts/lib/task-lesson-candidates.mjs +242 -0
- package/scripts/lib/task-lesson-sedimentation.mjs +326 -0
- package/scripts/lib/task-lifecycle/create-task-helpers.mjs +67 -0
- package/scripts/lib/task-lifecycle/phase-sync.mjs +88 -0
- package/scripts/lib/task-lifecycle/review-confirm.mjs +112 -0
- package/scripts/lib/task-lifecycle/review-gates.mjs +73 -0
- package/scripts/lib/task-lifecycle/review-submission.mjs +63 -0
- package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +49 -0
- package/scripts/lib/task-lifecycle/template-files.mjs +53 -0
- package/scripts/lib/task-lifecycle/text-utils.mjs +24 -0
- package/scripts/lib/task-lifecycle.mjs +338 -477
- package/scripts/lib/task-metadata.mjs +118 -0
- package/scripts/lib/task-review-model.mjs +455 -0
- package/scripts/lib/task-scanner.mjs +193 -372
- package/scripts/lib/task-tombstone-commands.mjs +140 -0
- package/scripts/postinstall.mjs +14 -0
- package/skills/preset-creator/SKILL.md +179 -0
- package/skills/preset-creator/references/complex-task-skeleton/README.md +31 -0
- package/skills/preset-creator/references/complex-task-skeleton/artifacts/INDEX.md +12 -0
- package/skills/preset-creator/references/complex-task-skeleton/brief.md +43 -0
- package/skills/preset-creator/references/complex-task-skeleton/execution_strategy.md +71 -0
- package/skills/preset-creator/references/complex-task-skeleton/findings.md +24 -0
- package/skills/preset-creator/references/complex-task-skeleton/lesson_candidates.md +70 -0
- package/skills/preset-creator/references/complex-task-skeleton/long-running-task-contract.md +76 -0
- package/skills/preset-creator/references/complex-task-skeleton/progress.md +33 -0
- package/skills/preset-creator/references/complex-task-skeleton/references/INDEX.md +13 -0
- package/skills/preset-creator/references/complex-task-skeleton/review.md +107 -0
- package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +111 -0
- package/skills/preset-creator/references/complex-task-skeleton/visual_map.md +50 -0
- package/skills/preset-creator/references/preset-package-skeleton.md +296 -0
- package/templates/AGENTS.md.template +24 -18
- package/templates/dashboard/assets/app-src/00-state.js +13 -0
- package/templates/dashboard/assets/app-src/10-router.js +5 -1
- package/templates/dashboard/assets/app-src/20-overview.js +18 -8
- package/templates/dashboard/assets/app-src/30-tasks.js +92 -246
- package/templates/dashboard/assets/app-src/35-task-detail.js +286 -0
- package/templates/dashboard/assets/app-src/45-review.js +241 -22
- package/templates/dashboard/assets/app-src/50-migration.js +24 -10
- package/templates/dashboard/assets/app-src/55-presets.js +375 -0
- package/templates/dashboard/assets/app-src/60-shared.js +3 -1
- package/templates/dashboard/assets/app-src/90-bindings.js +302 -29
- package/templates/dashboard/assets/app.css +1501 -376
- package/templates/dashboard/assets/app.css.manifest.json +10 -0
- package/templates/dashboard/assets/app.js +1240 -101
- package/templates/dashboard/assets/app.manifest.json +2 -0
- package/templates/dashboard/assets/css-src/00-foundation.css +346 -0
- package/templates/dashboard/assets/css-src/10-panels-flow.css +236 -0
- package/templates/dashboard/assets/css-src/20-briefs-controls.css +398 -0
- package/templates/dashboard/assets/css-src/30-task-index.css +739 -0
- package/templates/dashboard/assets/css-src/35-review-workspace.css +507 -0
- package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +489 -0
- package/templates/dashboard/assets/css-src/45-presets.css +516 -0
- package/templates/dashboard/assets/css-src/50-responsive-overrides.css +551 -0
- package/templates/dashboard/assets/i18n.js +263 -23
- package/templates/ledger/Harness-Ledger.md +13 -25
- package/templates/lessons/lesson-arch-process-change.md +1 -1
- package/templates/lessons/lesson-new-doc.md +1 -1
- package/templates/lessons/lesson-ref-change.md +1 -1
- package/templates/planning/INDEX.md +87 -0
- package/templates/planning/brief.md +1 -1
- package/templates/planning/execution_strategy.md +31 -0
- package/templates/planning/lesson_candidates.md +18 -6
- package/templates/planning/module_session_prompt.md +1 -0
- package/templates/planning/optional/artifacts/INDEX.md +3 -3
- package/templates/planning/optional/references/INDEX.md +3 -3
- package/templates/planning/review.md +41 -0
- package/templates/planning/task_plan.md +5 -21
- package/templates/planning/visual_map.md +13 -9
- package/templates/planning/visual_map.simple.md +52 -0
- package/templates/reference/execution-workflow-standard.md +31 -3
- package/templates/reference/pull-request-standard.md +80 -0
- package/templates/reference/repo-governance-standard.md +7 -6
- package/templates/reference/review-routing-standard.md +6 -0
- package/templates/reference/walkthrough-standard.md +2 -1
- package/templates/verifier/verifier-output.md +1 -1
- package/templates-zh-CN/AGENTS.md.template +25 -19
- package/templates-zh-CN/ledger/Harness-Ledger.md +17 -40
- package/templates-zh-CN/planning/INDEX.md +87 -0
- package/templates-zh-CN/planning/brief.md +1 -1
- package/templates-zh-CN/planning/execution_strategy.md +30 -0
- package/templates-zh-CN/planning/lesson_candidates.md +18 -6
- package/templates-zh-CN/planning/module_session_prompt.md +1 -0
- package/templates-zh-CN/planning/review.md +41 -1
- package/templates-zh-CN/planning/task_plan.md +4 -44
- package/templates-zh-CN/planning/visual_map.md +14 -7
- package/templates-zh-CN/planning/visual_map.simple.md +48 -0
- package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
- package/templates-zh-CN/reference/docs-library-standard.md +1 -1
- package/templates-zh-CN/reference/execution-workflow-standard.md +33 -7
- package/templates-zh-CN/reference/harness-ledger-standard.md +2 -2
- package/templates-zh-CN/reference/pull-request-standard.md +106 -0
- package/templates-zh-CN/reference/repo-governance-standard.md +4 -3
- package/templates-zh-CN/reference/review-routing-standard.md +8 -1
- package/templates-zh-CN/reference/walkthrough-standard.md +3 -2
- package/templates-zh-CN/walkthrough/Closeout-SSoT.md +1 -1
- package/docs-release/assets/dashboard-overview-en.png +0 -0
- package/scripts/smoke-dashboard.mjs +0 -92
- package/scripts/test-harness.mjs +0 -1395
- package/templates/ssot/Feature-SSoT.md +0 -43
- package/templates/ssot/Lessons-SSoT.md +0 -44
- package/templates-zh-CN/ssot/Feature-SSoT.md +0 -49
- package/templates-zh-CN/ssot/Lessons-SSoT.md +0 -49
package/scripts/harness.mjs
CHANGED
|
@@ -6,26 +6,19 @@ import path from "node:path";
|
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import {
|
|
8
8
|
addCapability,
|
|
9
|
-
buildMigrationPlan,
|
|
10
9
|
buildStatus,
|
|
11
|
-
confirmTaskReview,
|
|
12
|
-
createTask,
|
|
13
10
|
doctorUserSkill,
|
|
14
11
|
installUserSkill,
|
|
15
|
-
listLifecycleTasks,
|
|
16
12
|
normalizeLocale,
|
|
17
|
-
|
|
18
|
-
runMigration,
|
|
13
|
+
rebuildGovernanceIndexes,
|
|
19
14
|
serveDashboardWorkbench,
|
|
20
15
|
validateSourcePackageBoundary,
|
|
21
|
-
updateModuleStep,
|
|
22
|
-
updateTaskPhase,
|
|
23
|
-
updateTaskLifecycle,
|
|
24
|
-
verifyMigrationSession,
|
|
25
|
-
writeDashboardFolder,
|
|
26
|
-
writeDashboardSingleFile,
|
|
27
16
|
writeInitFiles,
|
|
28
17
|
} from "./lib/harness-core.mjs";
|
|
18
|
+
import { runDashboardCommand } from "./commands/dashboard-command.mjs";
|
|
19
|
+
import { runMigrationCommand } from "./commands/migration-command.mjs";
|
|
20
|
+
import { runPresetCommand } from "./commands/preset-command.mjs";
|
|
21
|
+
import { runTaskCommand } from "./commands/task-command.mjs";
|
|
29
22
|
|
|
30
23
|
const args = process.argv.slice(2);
|
|
31
24
|
const command = args.shift() || "help";
|
|
@@ -94,24 +87,53 @@ Usage:
|
|
|
94
87
|
harness init [--dry-run] [--locale zh-CN|en-US] [--capabilities core,dashboard] [--add-npm-scripts] [target]
|
|
95
88
|
harness add-capability <name> [--dry-run] [--locale zh-CN|en-US] [target]
|
|
96
89
|
harness migrate-plan [--json] [--limit n] [target]
|
|
90
|
+
harness migrate-task-audit-index [--plan] [--apply] [--json] [target]
|
|
97
91
|
harness migrate-run [--locale zh-CN|en-US] [--assume-locale] [--allow-dirty] [--plan-only] [--out-dir folder] [--session-dir folder] [target]
|
|
98
92
|
harness migrate-verify [--json] [--full-cutover] <session.json>
|
|
99
|
-
harness
|
|
93
|
+
harness governance rebuild [--dry-run] [--archive] [--apply] [target]
|
|
94
|
+
harness preset list [--json] [target]
|
|
95
|
+
harness preset inspect <id> [--json] [target]
|
|
96
|
+
harness preset check <id> [--json] [target]
|
|
97
|
+
harness preset install <folder|zip|builtin-id> [--project] [--force] [--json] [target]
|
|
98
|
+
harness preset seed [--project] [--force] [--dry-run] [--json] [target]
|
|
99
|
+
harness preset uninstall <id> [--project] [--json] [target]
|
|
100
|
+
harness new-task [task-id] [--module key] [--budget simple|standard|complex] [--preset id] [--from-session session.json] [--long-running] [--title title] [--locale zh-CN|en-US] [--dry-run] [target]
|
|
100
101
|
harness task-start <task-id> [--message text] [target]
|
|
101
102
|
harness task-phase <task-id> <phase-id> [--state done] [--completion 100] [--evidence present] [target]
|
|
102
103
|
harness task-log <task-id> --message text [--evidence type:PATH:summary] [target]
|
|
103
104
|
harness task-block <task-id> [--message text] [target]
|
|
104
105
|
harness task-review <task-id> [--message text] [target]
|
|
105
106
|
harness review-confirm <task-id> --confirm task-id [--reviewer name] [--message text] [target]
|
|
106
|
-
harness lesson-promote <task-id> <candidate-id> [--dry-run] [target]
|
|
107
|
+
harness lesson-promote <task-id> <candidate-id> [--dry-run|--apply] [target]
|
|
108
|
+
harness lesson-sediment <task-id> <candidate-id> [--dry-run] [--title title] [target]
|
|
107
109
|
harness task-complete <task-id> [--message text] [target]
|
|
108
|
-
harness task-list [--json] [--state state] [--module key] [target]
|
|
110
|
+
harness task-list [--json] [--state state] [--module key] [--queue queue] [--preset id] [--review status] [--lesson status] [--missing-materials] [--search text] [target]
|
|
111
|
+
harness task-index [--json] [target]
|
|
112
|
+
harness task-supersede <old-task-id> --by <new-task-id> [--reason text] [target]
|
|
113
|
+
harness task-delete <task-id> --soft [--reason text] [target]
|
|
114
|
+
harness task-archive <task-id> [--reason text] [target]
|
|
115
|
+
harness task-reopen <task-id> [--reason text] [target]
|
|
109
116
|
harness module-step <module-key> <step-id> [--state done|in-progress|blocked] [target]
|
|
110
|
-
harness install-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir] [--dry-run] [--force] [--yes]
|
|
117
|
+
harness install-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir] [--dry-run] [--force] [--skip-presets] [--yes]
|
|
111
118
|
harness doctor-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir]
|
|
112
119
|
|
|
113
120
|
If init runs in an interactive terminal and --locale is omitted, it asks for a
|
|
114
121
|
language. Non-interactive init defaults to en-US.
|
|
122
|
+
|
|
123
|
+
Preset discovery:
|
|
124
|
+
Project presets live in <target>/.coding-agent-harness/presets/<preset-id>/.
|
|
125
|
+
User presets live in ~/.coding-agent-harness/presets/<preset-id>/.
|
|
126
|
+
Harness discovers project presets first when a target is supplied, then user
|
|
127
|
+
presets, then bundled package presets under presets/<preset-id>/.
|
|
128
|
+
"harness init" seeds bundled presets into the target project. "harness
|
|
129
|
+
install-user" and npm postinstall seed bundled presets into the user root.
|
|
130
|
+
Use "harness preset seed" to repair or re-run preset seeding.
|
|
131
|
+
Use "harness preset install" with a local preset folder, .zip archive, or
|
|
132
|
+
bundled preset id. Preset archives must contain preset.yaml at the archive
|
|
133
|
+
root or inside one top-level folder.
|
|
134
|
+
Use "harness preset list --json" to see available presets, their source,
|
|
135
|
+
purpose, compatible budgets, and manifest path. Use "harness preset inspect
|
|
136
|
+
<id> --json" for the full preset manifest summary.
|
|
115
137
|
`);
|
|
116
138
|
}
|
|
117
139
|
|
|
@@ -123,6 +145,8 @@ function exitWithReport(report) {
|
|
|
123
145
|
|
|
124
146
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
125
147
|
printHelp();
|
|
148
|
+
} else if (args[0] === "help" || args.includes("--help") || args.includes("-h")) {
|
|
149
|
+
printHelp();
|
|
126
150
|
} else if (command === "check") {
|
|
127
151
|
const profile = takeOption("--profile", "target-project");
|
|
128
152
|
const strict = takeFlag("--strict");
|
|
@@ -166,8 +190,9 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
166
190
|
const port = takeOption("--port", "0");
|
|
167
191
|
const localeOverride = takeOption("--locale", "");
|
|
168
192
|
const target = targetArg();
|
|
193
|
+
const usesDefaultOutDir = !outDir;
|
|
169
194
|
const dashboardOutDir = outDir || defaultDevOutDir(target);
|
|
170
|
-
const opts = localeOverride ? { localeOverride } : {};
|
|
195
|
+
const opts = { ...(localeOverride ? { localeOverride } : {}), recoverGeneratedDashboard: usesDefaultOutDir };
|
|
171
196
|
try {
|
|
172
197
|
await serveDashboardWorkbench(dashboardOutDir, target, { ...opts, host, port, autoRefresh: true, open, label: "harness dev" });
|
|
173
198
|
} catch (error) {
|
|
@@ -175,63 +200,7 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
175
200
|
process.exit(1);
|
|
176
201
|
}
|
|
177
202
|
} else if (command === "dashboard") {
|
|
178
|
-
|
|
179
|
-
const workbench = takeFlag("--workbench");
|
|
180
|
-
const out = takeOption("--out", "harness-dashboard.html");
|
|
181
|
-
const outDir = takeOption("--out-dir", "");
|
|
182
|
-
const host = takeOption("--host", "127.0.0.1");
|
|
183
|
-
const port = takeOption("--port", "0");
|
|
184
|
-
const localeOverride = takeOption("--locale", "");
|
|
185
|
-
const opts = localeOverride ? { localeOverride } : {};
|
|
186
|
-
if (workbench) {
|
|
187
|
-
if (!outDir) {
|
|
188
|
-
console.error("dashboard --workbench requires --out-dir so regenerated data has a stable folder");
|
|
189
|
-
process.exit(2);
|
|
190
|
-
}
|
|
191
|
-
try {
|
|
192
|
-
await serveDashboardWorkbench(outDir, targetArg(), { ...opts, host, port });
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error(error.message);
|
|
195
|
-
process.exit(1);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
if (watch) {
|
|
199
|
-
if (!outDir) {
|
|
200
|
-
console.error("dashboard --watch requires --out-dir so updates are written to a stable folder");
|
|
201
|
-
process.exit(2);
|
|
202
|
-
}
|
|
203
|
-
const target = targetArg();
|
|
204
|
-
const docsRoot = path.basename(path.resolve(target)) === "docs" ? path.resolve(target) : path.join(path.resolve(target), "docs");
|
|
205
|
-
const regenerate = () => {
|
|
206
|
-
try {
|
|
207
|
-
console.log(writeDashboardFolder(outDir, target, opts));
|
|
208
|
-
console.log(`dashboard regenerated: ${new Date().toISOString()}`);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
console.error(`dashboard regeneration failed: ${error.message}`);
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
regenerate();
|
|
214
|
-
let timer = null;
|
|
215
|
-
const watcher = fs.watch(docsRoot, { recursive: true }, () => {
|
|
216
|
-
clearTimeout(timer);
|
|
217
|
-
timer = setTimeout(regenerate, 300);
|
|
218
|
-
});
|
|
219
|
-
const close = () => {
|
|
220
|
-
watcher.close();
|
|
221
|
-
clearTimeout(timer);
|
|
222
|
-
process.exit(0);
|
|
223
|
-
};
|
|
224
|
-
process.on("SIGINT", close);
|
|
225
|
-
process.on("SIGTERM", close);
|
|
226
|
-
console.log(`watching ${docsRoot}`);
|
|
227
|
-
await new Promise(() => {});
|
|
228
|
-
}
|
|
229
|
-
if (outDir) {
|
|
230
|
-
console.log(writeDashboardFolder(outDir, targetArg(), opts));
|
|
231
|
-
} else {
|
|
232
|
-
console.log(writeDashboardSingleFile(out, targetArg(), opts));
|
|
233
|
-
}
|
|
234
|
-
process.exit(0);
|
|
203
|
+
await runDashboardCommand({ takeFlag, takeOption, targetArg });
|
|
235
204
|
} else if (command === "init") {
|
|
236
205
|
const dryRun = takeFlag("--dry-run");
|
|
237
206
|
const addNpmScripts = takeFlag("--add-npm-scripts");
|
|
@@ -239,7 +208,7 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
239
208
|
const capabilities = takeOption("--capabilities", "core").split(",").map((item) => item.trim()).filter(Boolean);
|
|
240
209
|
try {
|
|
241
210
|
const result = writeInitFiles(targetArg(), capabilities, { dryRun, locale, addNpmScripts });
|
|
242
|
-
console.log(JSON.stringify({ dryRun, locale: result.locale, capabilities: result.capabilities, changes: result.changes, nextCommands: result.nextCommands, report: result.report }, null, 2));
|
|
211
|
+
console.log(JSON.stringify({ dryRun, locale: result.locale, capabilities: result.capabilities, changes: result.changes, presetSeed: result.presetSeed, nextCommands: result.nextCommands, report: result.report }, null, 2));
|
|
243
212
|
} catch (error) {
|
|
244
213
|
console.error(error.message);
|
|
245
214
|
process.exit(1);
|
|
@@ -259,202 +228,32 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
259
228
|
console.error(error.message);
|
|
260
229
|
process.exit(1);
|
|
261
230
|
}
|
|
262
|
-
} else if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
console.log(JSON.stringify(plan, null, 2));
|
|
269
|
-
} else {
|
|
270
|
-
console.log(`Migration Plan: ${plan.target}`);
|
|
271
|
-
console.log(`mode: ${plan.mode}`);
|
|
272
|
-
console.log(`warnings: ${plan.summary.warnings}`);
|
|
273
|
-
console.log(`task actions: ${plan.summary.taskActions}`);
|
|
274
|
-
console.log(`visual map actions: ${plan.summary.visualMapActions}`);
|
|
275
|
-
console.log(`legacy visual-only tasks: ${plan.summary.legacyVisualOnly}`);
|
|
276
|
-
console.log(`weak briefs: ${plan.summary.weakBrief}`);
|
|
277
|
-
console.log(`unknown classifications: ${plan.summary.unknownClassification}`);
|
|
278
|
-
console.log(`full cutover eligible: ${plan.summary.fullCutoverEligible ? "yes" : "no"}`);
|
|
279
|
-
console.log(`review actions: ${plan.summary.reviewSchemaGaps}`);
|
|
280
|
-
console.log(`legacy actions: ${plan.summary.legacyReferenceGaps}`);
|
|
281
|
-
console.log(`legacy residuals: ${plan.summary.legacyResiduals}`);
|
|
282
|
-
console.log(`recommended capabilities: ${plan.summary.recommendedCapabilities.join(", ") || "none"}`);
|
|
283
|
-
console.log("\nPhases:");
|
|
284
|
-
for (const phase of plan.phases) console.log(`- ${phase.id}: ${phase.title}`);
|
|
285
|
-
console.log("\nTop task actions:");
|
|
286
|
-
for (const action of plan.taskActions) console.log(`- ${action.taskId}: add ${action.files.join(", ")}`);
|
|
287
|
-
console.log("\nTop review actions:");
|
|
288
|
-
for (const action of plan.reviewActions) console.log(`- ${action.path}: add ${action.missing.join(", ")}`);
|
|
289
|
-
console.log("\nTop legacy residuals:");
|
|
290
|
-
for (const action of plan.legacyResiduals) console.log(`- ${action.taskId}: ${action.missing} (${action.reason})`);
|
|
291
|
-
console.log("\nNext commands:");
|
|
292
|
-
for (const next of plan.nextCommands) console.log(`- ${next}`);
|
|
293
|
-
}
|
|
294
|
-
} catch (error) {
|
|
295
|
-
console.error(error.message);
|
|
296
|
-
process.exit(1);
|
|
297
|
-
}
|
|
298
|
-
} else if (command === "migrate-run") {
|
|
299
|
-
const locale = takeOption("--locale", "");
|
|
300
|
-
const assumeLocale = takeFlag("--assume-locale");
|
|
301
|
-
const allowDirty = takeFlag("--allow-dirty");
|
|
302
|
-
const planOnly = takeFlag("--plan-only");
|
|
303
|
-
const outDir = takeOption("--out-dir", "");
|
|
304
|
-
const sessionDir = takeOption("--session-dir", "");
|
|
305
|
-
try {
|
|
306
|
-
console.log(
|
|
307
|
-
JSON.stringify(
|
|
308
|
-
runMigration(targetArg(), {
|
|
309
|
-
locale,
|
|
310
|
-
assumeLocale,
|
|
311
|
-
allowDirty,
|
|
312
|
-
planOnly,
|
|
313
|
-
outDir,
|
|
314
|
-
sessionDir,
|
|
315
|
-
}),
|
|
316
|
-
null,
|
|
317
|
-
2,
|
|
318
|
-
),
|
|
319
|
-
);
|
|
320
|
-
} catch (error) {
|
|
321
|
-
console.error(error.message);
|
|
322
|
-
process.exit(1);
|
|
323
|
-
}
|
|
324
|
-
} else if (command === "migrate-verify") {
|
|
325
|
-
const json = takeFlag("--json");
|
|
326
|
-
const fullCutover = takeFlag("--full-cutover");
|
|
327
|
-
const sessionPath = args.shift();
|
|
328
|
-
if (!sessionPath) {
|
|
329
|
-
console.error("Missing session.json path");
|
|
231
|
+
} else if (["migrate-plan", "migrate-run", "migrate-verify", "migrate-task-audit-index"].includes(command)) {
|
|
232
|
+
runMigrationCommand(command, { args, takeFlag, takeOption, targetArg });
|
|
233
|
+
} else if (command === "governance") {
|
|
234
|
+
const subcommand = args.shift() || "";
|
|
235
|
+
if (subcommand !== "rebuild") {
|
|
236
|
+
console.error(`Unknown governance subcommand: ${subcommand || "(missing)"}`);
|
|
330
237
|
process.exit(2);
|
|
331
238
|
}
|
|
332
|
-
const result = verifyMigrationSession(sessionPath, { fullCutover });
|
|
333
|
-
if (json) {
|
|
334
|
-
console.log(JSON.stringify(result, null, 2));
|
|
335
|
-
} else {
|
|
336
|
-
for (const failure of result.failures) console.error(`Failure: ${failure}`);
|
|
337
|
-
for (const warning of result.warnings) console.log(`Warning: ${warning}`);
|
|
338
|
-
console.log(`Migration verify ${result.status}: ${result.sessionPath}`);
|
|
339
|
-
}
|
|
340
|
-
process.exit(result.status === "pass" ? 0 : 1);
|
|
341
|
-
} else if (command === "new-task") {
|
|
342
239
|
const dryRun = takeFlag("--dry-run");
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
const moduleKey = takeOption("--module", "");
|
|
346
|
-
const budget = takeOption("--budget", "standard");
|
|
347
|
-
const preset = takeOption("--preset", "");
|
|
348
|
-
const fromSession = takeOption("--from-session", "");
|
|
349
|
-
const longRunning = takeFlag("--long-running");
|
|
350
|
-
const taskId = args.shift() || (fromSession ? "harness-v1-migration" : "");
|
|
351
|
-
if (!taskId) {
|
|
352
|
-
console.error("Missing task id");
|
|
353
|
-
process.exit(2);
|
|
354
|
-
}
|
|
355
|
-
try {
|
|
356
|
-
console.log(JSON.stringify(createTask(targetArg(), taskId, { title, locale, dryRun, moduleKey, budget, longRunning, preset, fromSession }), null, 2));
|
|
357
|
-
} catch (error) {
|
|
358
|
-
console.error(error.message);
|
|
359
|
-
process.exit(1);
|
|
360
|
-
}
|
|
361
|
-
} else if (command === "task-phase") {
|
|
362
|
-
const state = takeOption("--state", "");
|
|
363
|
-
const completion = takeOption("--completion", "");
|
|
364
|
-
const evidenceStatus = takeOption("--evidence", "");
|
|
365
|
-
const taskId = args.shift();
|
|
366
|
-
const phaseId = args.shift();
|
|
367
|
-
if (!taskId || !phaseId) {
|
|
368
|
-
console.error("Missing task id or phase id");
|
|
369
|
-
process.exit(2);
|
|
370
|
-
}
|
|
371
|
-
try {
|
|
372
|
-
console.log(JSON.stringify(updateTaskPhase(targetArg(), taskId, phaseId, { state, completion, evidenceStatus }), null, 2));
|
|
373
|
-
} catch (error) {
|
|
374
|
-
console.error(error.message);
|
|
375
|
-
process.exit(1);
|
|
376
|
-
}
|
|
377
|
-
} else if (["task-start", "task-log", "task-block", "task-review", "task-complete"].includes(command)) {
|
|
378
|
-
const message = takeOption("--message", "");
|
|
379
|
-
const evidence = takeOption("--evidence", "");
|
|
380
|
-
const taskId = args.shift();
|
|
381
|
-
if (!taskId) {
|
|
382
|
-
console.error("Missing task id");
|
|
383
|
-
process.exit(2);
|
|
384
|
-
}
|
|
385
|
-
const lifecycle = {
|
|
386
|
-
"task-start": { event: "task-start", state: "in_progress" },
|
|
387
|
-
"task-log": { event: "task-log", state: "" },
|
|
388
|
-
"task-block": { event: "task-block", state: "blocked" },
|
|
389
|
-
"task-review": { event: "task-review", state: "review" },
|
|
390
|
-
"task-complete": { event: "task-complete", state: "done" },
|
|
391
|
-
}[command];
|
|
392
|
-
try {
|
|
393
|
-
console.log(JSON.stringify(updateTaskLifecycle(targetArg(), taskId, { ...lifecycle, message, evidence }), null, 2));
|
|
394
|
-
} catch (error) {
|
|
395
|
-
console.error(error.message);
|
|
396
|
-
process.exit(1);
|
|
397
|
-
}
|
|
398
|
-
} else if (command === "review-confirm") {
|
|
399
|
-
const reviewer = takeOption("--reviewer", "Human Reviewer");
|
|
400
|
-
const message = takeOption("--message", "");
|
|
401
|
-
const evidence = takeOption("--evidence", "");
|
|
402
|
-
const confirmText = takeOption("--confirm", "");
|
|
403
|
-
const taskId = args.shift();
|
|
404
|
-
if (!taskId) {
|
|
405
|
-
console.error("Missing task id");
|
|
406
|
-
process.exit(2);
|
|
407
|
-
}
|
|
408
|
-
try {
|
|
409
|
-
console.log(JSON.stringify(confirmTaskReview(targetArg(), taskId, { reviewer, message, evidence, confirmText }), null, 2));
|
|
410
|
-
} catch (error) {
|
|
411
|
-
console.error(error.message);
|
|
412
|
-
process.exit(1);
|
|
413
|
-
}
|
|
414
|
-
} else if (command === "lesson-promote") {
|
|
415
|
-
const dryRun = takeFlag("--dry-run");
|
|
416
|
-
const taskId = args.shift();
|
|
417
|
-
const candidateId = args.shift();
|
|
418
|
-
if (!taskId || !candidateId) {
|
|
419
|
-
console.error("Missing task id or candidate id");
|
|
420
|
-
process.exit(2);
|
|
421
|
-
}
|
|
422
|
-
try {
|
|
423
|
-
console.log(JSON.stringify(promoteLessonCandidate(targetArg(), taskId, candidateId, { dryRun }), null, 2));
|
|
424
|
-
} catch (error) {
|
|
425
|
-
console.error(error.message);
|
|
426
|
-
process.exit(1);
|
|
427
|
-
}
|
|
428
|
-
} else if (command === "task-list") {
|
|
429
|
-
const json = takeFlag("--json");
|
|
430
|
-
const state = takeOption("--state", "");
|
|
431
|
-
const moduleKey = takeOption("--module", "");
|
|
432
|
-
const result = listLifecycleTasks(targetArg(), { state, moduleKey });
|
|
433
|
-
if (json) {
|
|
434
|
-
console.log(JSON.stringify(result, null, 2));
|
|
435
|
-
} else {
|
|
436
|
-
for (const task of result.tasks) {
|
|
437
|
-
console.log(`${task.id}\t${task.state}\t${task.completion}%\t${task.title}`);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
} else if (command === "module-step") {
|
|
441
|
-
const state = takeOption("--state", "done");
|
|
442
|
-
const moduleKey = args.shift();
|
|
443
|
-
const stepId = args.shift();
|
|
444
|
-
if (!moduleKey || !stepId) {
|
|
445
|
-
console.error("Missing module key or step id");
|
|
446
|
-
process.exit(2);
|
|
447
|
-
}
|
|
240
|
+
const archive = takeFlag("--archive");
|
|
241
|
+
const apply = takeFlag("--apply");
|
|
448
242
|
try {
|
|
449
|
-
console.log(JSON.stringify(
|
|
243
|
+
console.log(JSON.stringify(rebuildGovernanceIndexes(targetArg(), { dryRun, archive, apply }), null, 2));
|
|
450
244
|
} catch (error) {
|
|
451
245
|
console.error(error.message);
|
|
452
246
|
process.exit(1);
|
|
453
247
|
}
|
|
248
|
+
} else if (command === "preset") {
|
|
249
|
+
runPresetCommand({ args, takeFlag, targetArg });
|
|
250
|
+
} else if (["new-task", "task-phase", "task-start", "task-log", "task-block", "task-review", "task-complete", "review-confirm", "lesson-promote", "lesson-sediment", "task-list", "task-index", "task-supersede", "task-delete", "task-archive", "task-reopen", "module-step"].includes(command)) {
|
|
251
|
+
runTaskCommand(command, { args, takeFlag, takeOption, targetArg });
|
|
454
252
|
} else if (command === "install-user") {
|
|
455
253
|
const dryRun = takeFlag("--dry-run");
|
|
456
254
|
const force = takeFlag("--force");
|
|
457
255
|
const yes = takeFlag("--yes") || takeFlag("-y");
|
|
256
|
+
const skipPresets = takeFlag("--skip-presets");
|
|
458
257
|
takeFlag("--global");
|
|
459
258
|
const agent = takeOption("--agent", "codex");
|
|
460
259
|
const home = takeOption("--home", "");
|
|
@@ -463,7 +262,7 @@ if (command === "help" || command === "--help" || command === "-h") {
|
|
|
463
262
|
process.exit(2);
|
|
464
263
|
}
|
|
465
264
|
try {
|
|
466
|
-
console.log(JSON.stringify(installUserSkill({ agent, home, dryRun, force }), null, 2));
|
|
265
|
+
console.log(JSON.stringify(installUserSkill({ agent, home, dryRun, force, seedPresets: !skipPresets }), null, 2));
|
|
467
266
|
} catch (error) {
|
|
468
267
|
console.error(error.message);
|
|
469
268
|
process.exit(1);
|
|
@@ -10,11 +10,14 @@ import {
|
|
|
10
10
|
exists,
|
|
11
11
|
existsInDocs,
|
|
12
12
|
readFileSafe,
|
|
13
|
+
readJsonSafe,
|
|
13
14
|
readBundledTemplate,
|
|
14
15
|
walkFiles,
|
|
15
16
|
normalizeLocale,
|
|
16
17
|
localizedTemplateSource,
|
|
18
|
+
userPresetRootForHome,
|
|
17
19
|
} from "./core-shared.mjs";
|
|
20
|
+
import { listBundledPresetIds, seedBundledPresets } from "./preset-registry.mjs";
|
|
18
21
|
|
|
19
22
|
export const capabilityDefinitions = {
|
|
20
23
|
core: {
|
|
@@ -94,8 +97,9 @@ export function readCapabilityRegistry(target) {
|
|
|
94
97
|
};
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
let readError = null;
|
|
101
|
+
const raw = readJsonSafe(registryPath, null, { onError: (error) => { readError = error; } });
|
|
102
|
+
if (raw) {
|
|
99
103
|
const locale = normalizeLocale(raw.locale);
|
|
100
104
|
const capabilities = Array.isArray(raw.capabilities)
|
|
101
105
|
? raw.capabilities.map((entry) =>
|
|
@@ -105,9 +109,8 @@ export function readCapabilityRegistry(target) {
|
|
|
105
109
|
)
|
|
106
110
|
: [];
|
|
107
111
|
return { mode: "declared-capability", path: registryPath, capabilities, raw, locale, errors: [] };
|
|
108
|
-
} catch (error) {
|
|
109
|
-
return { mode: "declared-capability", path: registryPath, capabilities: [], raw: null, errors: [error.message] };
|
|
110
112
|
}
|
|
113
|
+
return { mode: "declared-capability", path: registryPath, capabilities: [], raw: null, errors: [readError?.message || "invalid .harness-capabilities.json"] };
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
export function normalizeCapabilityName(name) {
|
|
@@ -124,12 +127,53 @@ export function validateSourcePackageBoundary(targetInput = ".") {
|
|
|
124
127
|
.split("\0")
|
|
125
128
|
.filter(Boolean)
|
|
126
129
|
.filter((file) => file === "AGENTS.md" || file === "CLAUDE.md" || file === "docs" || file.startsWith("docs/") || file === ".harness-private" || file.startsWith(".harness-private/"));
|
|
130
|
+
const tracked = spawnSync("git", ["-C", root, "ls-files", "-z", "--", "harness-dashboard.html"], { encoding: "utf8" });
|
|
131
|
+
const generatedRootDashboard = tracked.status === 0
|
|
132
|
+
? tracked.stdout.split("\0").filter(Boolean)
|
|
133
|
+
.filter((file) => fs.existsSync(path.join(root, file)))
|
|
134
|
+
: [];
|
|
135
|
+
const internalScripts = ["scripts/test-harness.mjs", "scripts/smoke-dashboard.mjs"]
|
|
136
|
+
.filter((file) => fs.existsSync(path.join(root, file)));
|
|
137
|
+
const dashboardAppDrift = validateDashboardAppAssembly(root);
|
|
138
|
+
const dashboardCssDrift = validateDashboardAssetAssembly(root, "app.css.manifest.json", "app.css", "dashboard assets/app.css does not match css-src manifest assembly");
|
|
127
139
|
return {
|
|
128
|
-
failures:
|
|
129
|
-
|
|
140
|
+
failures: [
|
|
141
|
+
...localOnly.map((file) => `private local-only file staged: ${file}`),
|
|
142
|
+
...generatedRootDashboard.map((file) => `generated dashboard file tracked in source root: ${file}`),
|
|
143
|
+
...internalScripts.map((file) => `internal test/smoke file in publishable scripts directory: ${file}`),
|
|
144
|
+
...dashboardAppDrift,
|
|
145
|
+
...dashboardCssDrift,
|
|
146
|
+
],
|
|
147
|
+
warnings: tracked.status === 0 ? [] : [`could not inspect tracked generated dashboard files: ${tracked.stderr.trim() || tracked.status}`],
|
|
130
148
|
};
|
|
131
149
|
}
|
|
132
150
|
|
|
151
|
+
function validateDashboardAppAssembly(root) {
|
|
152
|
+
return validateDashboardAssetAssembly(root, "app.manifest.json", "app.js", "dashboard assets/app.js does not match app-src manifest assembly");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function validateDashboardAssetAssembly(root, manifestName, assetName, driftMessage) {
|
|
156
|
+
const assetsDir = path.join(root, "templates/dashboard/assets");
|
|
157
|
+
const manifestPath = path.join(assetsDir, manifestName);
|
|
158
|
+
const assetPath = path.join(assetsDir, assetName);
|
|
159
|
+
if (!fs.existsSync(manifestPath) || !fs.existsSync(assetPath)) return [];
|
|
160
|
+
try {
|
|
161
|
+
const manifest = readJsonSafe(manifestPath, null);
|
|
162
|
+
if (!Array.isArray(manifest) || manifest.length === 0) {
|
|
163
|
+
return [`dashboard asset manifest must list source files: ${manifestName}`];
|
|
164
|
+
}
|
|
165
|
+
const assembled = `${manifest.map((relativePath) => {
|
|
166
|
+
const source = path.join(assetsDir, relativePath);
|
|
167
|
+
if (!fs.existsSync(source)) throw new Error(`missing ${relativePath}`);
|
|
168
|
+
return fs.readFileSync(source, "utf8").trimEnd();
|
|
169
|
+
}).join("\n\n")}\n`;
|
|
170
|
+
const trackedAsset = fs.readFileSync(assetPath, "utf8");
|
|
171
|
+
return trackedAsset === assembled ? [] : [driftMessage];
|
|
172
|
+
} catch (error) {
|
|
173
|
+
return [`could not validate dashboard asset assembly (${assetName}): ${error.message}`];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
133
177
|
export function detectCapabilities(target) {
|
|
134
178
|
const detected = new Set(["core"]);
|
|
135
179
|
if (existsInDocs(target, "09-PLANNING/Module-Registry.md")) detected.add("module-parallel");
|
|
@@ -164,6 +208,7 @@ export function buildInstallReport({ target, locale, capabilities, changes, dryR
|
|
|
164
208
|
agentInstructions: [
|
|
165
209
|
"Agents must choose locale during Decide and pass --locale zh-CN|en-US explicitly in non-interactive installs.",
|
|
166
210
|
"Use core for every install; add optional capabilities only when their selectWhen rule is true.",
|
|
211
|
+
"Bundled presets are seeded during init; use harness preset list --json before choosing task presets.",
|
|
167
212
|
"After scaffold, run Configure before marking capabilities configured or verified.",
|
|
168
213
|
"Run harness check/status/dashboard and record residuals before delivery.",
|
|
169
214
|
],
|
|
@@ -177,7 +222,7 @@ export function buildInstallReport({ target, locale, capabilities, changes, dryR
|
|
|
177
222
|
|
|
178
223
|
function packageVersion() {
|
|
179
224
|
try {
|
|
180
|
-
const pkg =
|
|
225
|
+
const pkg = readJsonSafe(path.join(repoRoot, "package.json"), {});
|
|
181
226
|
return pkg.version || "";
|
|
182
227
|
} catch {
|
|
183
228
|
return "";
|
|
@@ -209,6 +254,7 @@ function skillPackageEntries() {
|
|
|
209
254
|
"references",
|
|
210
255
|
"templates",
|
|
211
256
|
"templates-zh-CN",
|
|
257
|
+
"presets",
|
|
212
258
|
"scripts",
|
|
213
259
|
"docs-release",
|
|
214
260
|
"examples",
|
|
@@ -216,19 +262,14 @@ function skillPackageEntries() {
|
|
|
216
262
|
}
|
|
217
263
|
|
|
218
264
|
function listPackageFiles() {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
if (stat.isFile()) files.push(toPosix(relativePath));
|
|
229
|
-
}
|
|
230
|
-
for (const entry of skillPackageEntries()) walk(entry);
|
|
231
|
-
return files.sort();
|
|
265
|
+
return skillPackageEntries()
|
|
266
|
+
.flatMap((entry) => {
|
|
267
|
+
const fullPath = path.join(repoRoot, entry);
|
|
268
|
+
if (!fs.existsSync(fullPath)) return [];
|
|
269
|
+
if (fs.statSync(fullPath).isFile()) return [toPosix(path.relative(repoRoot, fullPath))];
|
|
270
|
+
return walkFiles(fullPath).map((file) => toPosix(path.relative(repoRoot, file)));
|
|
271
|
+
})
|
|
272
|
+
.sort();
|
|
232
273
|
}
|
|
233
274
|
|
|
234
275
|
function copySkillPackage(targetRoot, { dryRun = false, force = false } = {}) {
|
|
@@ -246,7 +287,7 @@ function copySkillPackage(targetRoot, { dryRun = false, force = false } = {}) {
|
|
|
246
287
|
return changes;
|
|
247
288
|
}
|
|
248
289
|
|
|
249
|
-
export function installUserSkill({ agent = "codex", home = "", dryRun = false, force = false } = {}) {
|
|
290
|
+
export function installUserSkill({ agent = "codex", home = "", dryRun = false, force = false, seedPresets = true } = {}) {
|
|
250
291
|
const agents = normalizeUserAgent(agent);
|
|
251
292
|
const targets = agents.map((targetAgent) => {
|
|
252
293
|
const target = targetForUserAgent(targetAgent, home);
|
|
@@ -260,8 +301,11 @@ export function installUserSkill({ agent = "codex", home = "", dryRun = false, f
|
|
|
260
301
|
skipped: changes.filter((change) => change.action === "skip-existing").length,
|
|
261
302
|
};
|
|
262
303
|
});
|
|
263
|
-
const
|
|
264
|
-
const
|
|
304
|
+
const presetSeed = seedPresets ? seedBundledPresets({ scope: "user", home, dryRun, force }) : null;
|
|
305
|
+
const changed = targets.some((target) => target.created > 0 || target.overwritten > 0) || (presetSeed && (presetSeed.created > 0 || presetSeed.overwritten > 0));
|
|
306
|
+
const onlySkipped =
|
|
307
|
+
targets.every((target) => target.created === 0 && target.overwritten === 0 && target.skipped > 0) &&
|
|
308
|
+
(!presetSeed || presetSeed.presets.every((preset) => preset.action === "skip-existing"));
|
|
265
309
|
return {
|
|
266
310
|
operation: "install-user",
|
|
267
311
|
status: dryRun ? "dry-run" : changed ? "installed" : onlySkipped ? "already-present" : "no-op",
|
|
@@ -269,13 +313,14 @@ export function installUserSkill({ agent = "codex", home = "", dryRun = false, f
|
|
|
269
313
|
force,
|
|
270
314
|
version: packageVersion(),
|
|
271
315
|
source: repoRoot,
|
|
316
|
+
presets: presetSeed,
|
|
272
317
|
targets,
|
|
273
318
|
};
|
|
274
319
|
}
|
|
275
320
|
|
|
276
321
|
function readInstalledVersion(targetRoot) {
|
|
277
322
|
try {
|
|
278
|
-
const pkg =
|
|
323
|
+
const pkg = readJsonSafe(path.join(targetRoot, "package.json"), {});
|
|
279
324
|
return pkg.version || "";
|
|
280
325
|
} catch {
|
|
281
326
|
return "";
|
|
@@ -301,6 +346,7 @@ export function doctorUserSkill({ agent = "codex", home = "" } = {}) {
|
|
|
301
346
|
"references",
|
|
302
347
|
"templates",
|
|
303
348
|
"templates-zh-CN",
|
|
349
|
+
"presets",
|
|
304
350
|
"scripts/harness.mjs",
|
|
305
351
|
"docs-release/guides/agent-installation.md",
|
|
306
352
|
];
|
|
@@ -315,12 +361,20 @@ export function doctorUserSkill({ agent = "codex", home = "" } = {}) {
|
|
|
315
361
|
missing,
|
|
316
362
|
};
|
|
317
363
|
});
|
|
364
|
+
const presetRoot = userPresetRootForHome(home);
|
|
365
|
+
const missingPresets = listBundledPresetIds().filter((id) => !fs.existsSync(path.join(presetRoot, id, "preset.yaml")));
|
|
366
|
+
const presets = {
|
|
367
|
+
root: presetRoot,
|
|
368
|
+
status: missingPresets.length === 0 ? "pass" : "fail",
|
|
369
|
+
missing: missingPresets,
|
|
370
|
+
};
|
|
318
371
|
const harnessCommand = commandOnPath("harness");
|
|
319
372
|
return {
|
|
320
373
|
operation: "doctor-user",
|
|
321
|
-
status: targets.every((target) => target.status === "pass") ? "pass" : "fail",
|
|
374
|
+
status: targets.every((target) => target.status === "pass") && presets.status === "pass" ? "pass" : "fail",
|
|
322
375
|
version: packageVersion(),
|
|
323
376
|
harnessCommand: harnessCommand || null,
|
|
377
|
+
presets,
|
|
324
378
|
targets,
|
|
325
379
|
};
|
|
326
380
|
}
|
|
@@ -399,7 +453,6 @@ export function plannedInitFiles(capabilities = ["core"], { locale = "en-US" } =
|
|
|
399
453
|
["docs/09-PLANNING/TASKS/_task-template/review.md", "templates/planning/review.md"],
|
|
400
454
|
["docs/05-TEST-QA/Regression-SSoT.md", "templates/ssot/Regression-SSoT.md"],
|
|
401
455
|
["docs/05-TEST-QA/Cadence-Ledger.md", "templates/regression/Cadence-Ledger.md"],
|
|
402
|
-
["docs/01-GOVERNANCE/Lessons-SSoT.md", "templates/ssot/Lessons-SSoT.md"],
|
|
403
456
|
["docs/10-WALKTHROUGH/_walkthrough-template.md", "templates/walkthrough/walkthrough-template.md"],
|
|
404
457
|
["docs/10-WALKTHROUGH/Closeout-SSoT.md", "templates/walkthrough/Closeout-SSoT.md"],
|
|
405
458
|
["docs/11-REFERENCE/external-source-intake-standard.md", "templates/reference/external-source-intake-standard.md"],
|
|
@@ -464,8 +517,9 @@ export function writeInitFiles(targetInput, capabilities, { dryRun = true, local
|
|
|
464
517
|
const registryPath = path.join(target.projectRoot, ".harness-capabilities.json");
|
|
465
518
|
if (!fs.existsSync(registryPath)) fs.writeFileSync(registryPath, `${JSON.stringify(registry, null, 2)}\n`);
|
|
466
519
|
}
|
|
520
|
+
const presetSeed = seedBundledPresets({ scope: "project", targetInput: target.projectRoot, dryRun });
|
|
467
521
|
const report = buildInstallReport({ target, locale: normalizedLocale, capabilities: normalizedCapabilities, changes, dryRun, operation: "init" });
|
|
468
|
-
return { target, capabilities: normalizedCapabilities, locale: normalizedLocale, changes, nextCommands: initNextCommands(), report };
|
|
522
|
+
return { target, capabilities: normalizedCapabilities, locale: normalizedLocale, changes, presetSeed, nextCommands: initNextCommands(), report };
|
|
469
523
|
}
|
|
470
524
|
|
|
471
525
|
function initNextCommands() {
|
|
@@ -478,7 +532,7 @@ function initNextCommands() {
|
|
|
478
532
|
function writeNpmScripts(target, { dryRun = true } = {}) {
|
|
479
533
|
const packagePath = path.join(target.projectRoot, "package.json");
|
|
480
534
|
if (!fs.existsSync(packagePath)) throw new Error("init --add-npm-scripts requires an existing package.json");
|
|
481
|
-
const pkg =
|
|
535
|
+
const pkg = readJsonSafe(packagePath, {});
|
|
482
536
|
const scripts = { ...(pkg.scripts || {}) };
|
|
483
537
|
const additions = {
|
|
484
538
|
"harness:dev": "coding-agent-harness dev .",
|