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.
Files changed (219) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/CONTRIBUTING.md +98 -0
  3. package/LICENSE +661 -21
  4. package/LICENSE-EXCEPTION.md +37 -0
  5. package/README.md +244 -87
  6. package/README.zh-CN.md +77 -35
  7. package/SKILL.md +32 -24
  8. package/docs-release/README.md +9 -5
  9. package/docs-release/architecture/overview.md +17 -5
  10. package/docs-release/architecture/overview.zh-CN.md +9 -5
  11. package/docs-release/architecture/system-explainer/01-system-overview.md +217 -0
  12. package/docs-release/architecture/system-explainer/02-module-dependency.md +257 -0
  13. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +304 -0
  14. package/docs-release/architecture/system-explainer/04-check-and-governance.md +239 -0
  15. package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
  16. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +303 -0
  17. package/docs-release/architecture/system-explainer/README.md +67 -0
  18. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +226 -0
  19. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +263 -0
  20. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +319 -0
  21. package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +250 -0
  22. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +290 -0
  23. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +323 -0
  24. package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
  25. package/docs-release/assets/dashboard-overview.png +0 -0
  26. package/docs-release/guides/agent-installation.en-US.md +39 -15
  27. package/docs-release/guides/agent-installation.md +43 -16
  28. package/docs-release/guides/contributing.md +100 -0
  29. package/docs-release/guides/contributing.zh-CN.md +99 -0
  30. package/docs-release/guides/document-audience-and-surfaces.en-US.md +3 -2
  31. package/docs-release/guides/document-audience-and-surfaces.md +3 -2
  32. package/docs-release/guides/full-legacy-migration-subagent-strategy.md +2 -2
  33. package/docs-release/guides/full-legacy-migration-subagent-strategy.zh-CN.md +2 -2
  34. package/docs-release/guides/legacy-migration-agent-prompt.md +0 -11
  35. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +0 -11
  36. package/docs-release/guides/migration-playbook.en-US.md +14 -15
  37. package/docs-release/guides/migration-playbook.md +14 -15
  38. package/docs-release/guides/parent-control-repository-pattern.en-US.md +7 -5
  39. package/docs-release/guides/parent-control-repository-pattern.md +7 -5
  40. package/docs-release/guides/preset-development.md +238 -0
  41. package/docs-release/guides/repository-operating-models.en-US.md +5 -4
  42. package/docs-release/guides/repository-operating-models.md +5 -4
  43. package/docs-release/guides/task-state-machine.en-US.md +224 -0
  44. package/docs-release/guides/task-state-machine.md +231 -0
  45. package/docs-release/intl/en-US.md +1 -1
  46. package/docs-release/intl/zh-CN.md +1 -1
  47. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/INDEX.md +60 -0
  48. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/findings.md +7 -0
  49. package/package.json +10 -4
  50. package/presets/legacy-migration/checks/preset-check.mjs +3 -0
  51. package/presets/legacy-migration/preset.yaml +134 -0
  52. package/presets/legacy-migration/scripts/plan-work-queue.mjs +4 -0
  53. package/presets/legacy-migration/scripts/scaffold-task-contracts.mjs +4 -0
  54. package/presets/legacy-migration/templates/execution_strategy.append.md +18 -0
  55. package/presets/legacy-migration/templates/findings.seed.md +17 -0
  56. package/presets/legacy-migration/templates/review.seed.md +12 -0
  57. package/presets/legacy-migration/templates/task_plan.append.md +9 -0
  58. package/presets/legacy-migration/templates/visual_map.append.md +12 -0
  59. package/presets/legacy-migration/workbench/dashboard-panels.yaml +2 -0
  60. package/presets/legacy-migration/workbench/migration-queue.schema.json +23 -0
  61. package/presets/lesson-sedimentation/preset.yaml +23 -0
  62. package/presets/lesson-sedimentation/templates/prompt.md +23 -0
  63. package/presets/module/preset.yaml +25 -0
  64. package/presets/module/templates/execution_strategy.append.md +8 -0
  65. package/presets/module/templates/task_plan.append.md +17 -0
  66. package/presets/standard-task/preset.yaml +31 -0
  67. package/presets/standard-task/templates/task_plan.append.md +7 -0
  68. package/references/adversarial-review-standard.md +2 -2
  69. package/references/agents-md-pattern.md +2 -2
  70. package/references/delivery-operating-model-standard.md +3 -3
  71. package/references/docs-directory-standard.md +6 -7
  72. package/references/harness-ledger.md +53 -96
  73. package/references/lessons-governance.md +88 -93
  74. package/references/module-parallel-standard.md +14 -14
  75. package/references/planning-loop.md +12 -6
  76. package/references/pull-request-standard.md +118 -0
  77. package/references/repo-governance-standard.md +11 -2
  78. package/references/review-routing-standard.md +7 -1
  79. package/references/ssot-governance.md +67 -59
  80. package/references/taskr-gap-analysis.md +600 -0
  81. package/references/walkthrough-closeout.md +7 -7
  82. package/scripts/check-harness.mjs +40 -301
  83. package/scripts/commands/dashboard-command.mjs +67 -0
  84. package/scripts/commands/migration-command.mjs +126 -0
  85. package/scripts/commands/preset-command.mjs +73 -0
  86. package/scripts/commands/task-command.mjs +328 -0
  87. package/scripts/harness.mjs +59 -260
  88. package/scripts/lib/capability-registry.mjs +82 -28
  89. package/scripts/lib/check-module-parallel.mjs +230 -0
  90. package/scripts/lib/check-profiles.mjs +90 -228
  91. package/scripts/lib/check-task-contracts.mjs +55 -0
  92. package/scripts/lib/core-shared.mjs +65 -2
  93. package/scripts/lib/dashboard-data.mjs +155 -24
  94. package/scripts/lib/dashboard-workbench.mjs +131 -12
  95. package/scripts/lib/dashboard-writer.mjs +20 -4
  96. package/scripts/lib/git-status-summary.mjs +46 -0
  97. package/scripts/lib/governance-index-generator.mjs +174 -0
  98. package/scripts/lib/governance-sync.mjs +611 -0
  99. package/scripts/lib/governance-table-boundary.mjs +175 -0
  100. package/scripts/lib/harness-core.mjs +6 -0
  101. package/scripts/lib/lesson-maintenance.mjs +36 -29
  102. package/scripts/lib/markdown-utils.mjs +33 -0
  103. package/scripts/lib/migration-planner.mjs +4 -6
  104. package/scripts/lib/migration-support.mjs +1 -1
  105. package/scripts/lib/phase-kind.mjs +50 -0
  106. package/scripts/lib/preset-audit-contracts.mjs +37 -0
  107. package/scripts/lib/preset-engine.mjs +494 -0
  108. package/scripts/lib/preset-registry.mjs +776 -0
  109. package/scripts/lib/preset-resource-contracts.mjs +83 -0
  110. package/scripts/lib/review-confirm-git-gate.mjs +248 -0
  111. package/scripts/lib/status-builder.mjs +88 -0
  112. package/scripts/lib/status-dashboard-renderer.mjs +105 -0
  113. package/scripts/lib/subagent-authorization-audit.mjs +196 -0
  114. package/scripts/lib/task-audit-metadata.mjs +385 -0
  115. package/scripts/lib/task-audit-migration.mjs +350 -0
  116. package/scripts/lib/task-completion-consistency.mjs +26 -0
  117. package/scripts/lib/task-index.mjs +93 -0
  118. package/scripts/lib/task-lesson-candidates.mjs +242 -0
  119. package/scripts/lib/task-lesson-sedimentation.mjs +326 -0
  120. package/scripts/lib/task-lifecycle/create-task-helpers.mjs +67 -0
  121. package/scripts/lib/task-lifecycle/phase-sync.mjs +88 -0
  122. package/scripts/lib/task-lifecycle/review-confirm.mjs +112 -0
  123. package/scripts/lib/task-lifecycle/review-gates.mjs +73 -0
  124. package/scripts/lib/task-lifecycle/review-submission.mjs +63 -0
  125. package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +49 -0
  126. package/scripts/lib/task-lifecycle/template-files.mjs +53 -0
  127. package/scripts/lib/task-lifecycle/text-utils.mjs +24 -0
  128. package/scripts/lib/task-lifecycle.mjs +338 -477
  129. package/scripts/lib/task-metadata.mjs +118 -0
  130. package/scripts/lib/task-review-model.mjs +455 -0
  131. package/scripts/lib/task-scanner.mjs +193 -372
  132. package/scripts/lib/task-tombstone-commands.mjs +140 -0
  133. package/scripts/postinstall.mjs +14 -0
  134. package/skills/preset-creator/SKILL.md +179 -0
  135. package/skills/preset-creator/references/complex-task-skeleton/README.md +31 -0
  136. package/skills/preset-creator/references/complex-task-skeleton/artifacts/INDEX.md +12 -0
  137. package/skills/preset-creator/references/complex-task-skeleton/brief.md +43 -0
  138. package/skills/preset-creator/references/complex-task-skeleton/execution_strategy.md +71 -0
  139. package/skills/preset-creator/references/complex-task-skeleton/findings.md +24 -0
  140. package/skills/preset-creator/references/complex-task-skeleton/lesson_candidates.md +70 -0
  141. package/skills/preset-creator/references/complex-task-skeleton/long-running-task-contract.md +76 -0
  142. package/skills/preset-creator/references/complex-task-skeleton/progress.md +33 -0
  143. package/skills/preset-creator/references/complex-task-skeleton/references/INDEX.md +13 -0
  144. package/skills/preset-creator/references/complex-task-skeleton/review.md +107 -0
  145. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +111 -0
  146. package/skills/preset-creator/references/complex-task-skeleton/visual_map.md +50 -0
  147. package/skills/preset-creator/references/preset-package-skeleton.md +296 -0
  148. package/templates/AGENTS.md.template +24 -18
  149. package/templates/dashboard/assets/app-src/00-state.js +13 -0
  150. package/templates/dashboard/assets/app-src/10-router.js +5 -1
  151. package/templates/dashboard/assets/app-src/20-overview.js +18 -8
  152. package/templates/dashboard/assets/app-src/30-tasks.js +92 -246
  153. package/templates/dashboard/assets/app-src/35-task-detail.js +286 -0
  154. package/templates/dashboard/assets/app-src/45-review.js +241 -22
  155. package/templates/dashboard/assets/app-src/50-migration.js +24 -10
  156. package/templates/dashboard/assets/app-src/55-presets.js +375 -0
  157. package/templates/dashboard/assets/app-src/60-shared.js +3 -1
  158. package/templates/dashboard/assets/app-src/90-bindings.js +302 -29
  159. package/templates/dashboard/assets/app.css +1501 -376
  160. package/templates/dashboard/assets/app.css.manifest.json +10 -0
  161. package/templates/dashboard/assets/app.js +1240 -101
  162. package/templates/dashboard/assets/app.manifest.json +2 -0
  163. package/templates/dashboard/assets/css-src/00-foundation.css +346 -0
  164. package/templates/dashboard/assets/css-src/10-panels-flow.css +236 -0
  165. package/templates/dashboard/assets/css-src/20-briefs-controls.css +398 -0
  166. package/templates/dashboard/assets/css-src/30-task-index.css +739 -0
  167. package/templates/dashboard/assets/css-src/35-review-workspace.css +507 -0
  168. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +489 -0
  169. package/templates/dashboard/assets/css-src/45-presets.css +516 -0
  170. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +551 -0
  171. package/templates/dashboard/assets/i18n.js +263 -23
  172. package/templates/ledger/Harness-Ledger.md +13 -25
  173. package/templates/lessons/lesson-arch-process-change.md +1 -1
  174. package/templates/lessons/lesson-new-doc.md +1 -1
  175. package/templates/lessons/lesson-ref-change.md +1 -1
  176. package/templates/planning/INDEX.md +87 -0
  177. package/templates/planning/brief.md +1 -1
  178. package/templates/planning/execution_strategy.md +31 -0
  179. package/templates/planning/lesson_candidates.md +18 -6
  180. package/templates/planning/module_session_prompt.md +1 -0
  181. package/templates/planning/optional/artifacts/INDEX.md +3 -3
  182. package/templates/planning/optional/references/INDEX.md +3 -3
  183. package/templates/planning/review.md +41 -0
  184. package/templates/planning/task_plan.md +5 -21
  185. package/templates/planning/visual_map.md +13 -9
  186. package/templates/planning/visual_map.simple.md +52 -0
  187. package/templates/reference/execution-workflow-standard.md +31 -3
  188. package/templates/reference/pull-request-standard.md +80 -0
  189. package/templates/reference/repo-governance-standard.md +7 -6
  190. package/templates/reference/review-routing-standard.md +6 -0
  191. package/templates/reference/walkthrough-standard.md +2 -1
  192. package/templates/verifier/verifier-output.md +1 -1
  193. package/templates-zh-CN/AGENTS.md.template +25 -19
  194. package/templates-zh-CN/ledger/Harness-Ledger.md +17 -40
  195. package/templates-zh-CN/planning/INDEX.md +87 -0
  196. package/templates-zh-CN/planning/brief.md +1 -1
  197. package/templates-zh-CN/planning/execution_strategy.md +30 -0
  198. package/templates-zh-CN/planning/lesson_candidates.md +18 -6
  199. package/templates-zh-CN/planning/module_session_prompt.md +1 -0
  200. package/templates-zh-CN/planning/review.md +41 -1
  201. package/templates-zh-CN/planning/task_plan.md +4 -44
  202. package/templates-zh-CN/planning/visual_map.md +14 -7
  203. package/templates-zh-CN/planning/visual_map.simple.md +48 -0
  204. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  205. package/templates-zh-CN/reference/docs-library-standard.md +1 -1
  206. package/templates-zh-CN/reference/execution-workflow-standard.md +33 -7
  207. package/templates-zh-CN/reference/harness-ledger-standard.md +2 -2
  208. package/templates-zh-CN/reference/pull-request-standard.md +106 -0
  209. package/templates-zh-CN/reference/repo-governance-standard.md +4 -3
  210. package/templates-zh-CN/reference/review-routing-standard.md +8 -1
  211. package/templates-zh-CN/reference/walkthrough-standard.md +3 -2
  212. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +1 -1
  213. package/docs-release/assets/dashboard-overview-en.png +0 -0
  214. package/scripts/smoke-dashboard.mjs +0 -92
  215. package/scripts/test-harness.mjs +0 -1395
  216. package/templates/ssot/Feature-SSoT.md +0 -43
  217. package/templates/ssot/Lessons-SSoT.md +0 -44
  218. package/templates-zh-CN/ssot/Feature-SSoT.md +0 -49
  219. package/templates-zh-CN/ssot/Lessons-SSoT.md +0 -49
@@ -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
- promoteLessonCandidate,
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 new-task <task-id> [--module key] [--budget simple|standard|complex] [--preset legacy-migration] [--from-session session.json] [--long-running] [--title title] [--locale zh-CN|en-US] [--dry-run] [target]
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
- const watch = takeFlag("--watch");
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 (command === "migrate-plan") {
263
- const json = takeFlag("--json");
264
- const limit = Number.parseInt(takeOption("--limit", "20"), 10) || 20;
265
- try {
266
- const plan = buildMigrationPlan(targetArg(), { limit });
267
- if (json) {
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 locale = takeOption("--locale", "");
344
- const title = takeOption("--title", "");
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(updateModuleStep(targetArg(), moduleKey, stepId, { state }), null, 2));
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
- try {
98
- const raw = JSON.parse(fs.readFileSync(registryPath, "utf8"));
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: localOnly.map((file) => `private local-only file staged: ${file}`),
129
- warnings: [],
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 = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
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
- const files = [];
220
- function walk(relativePath) {
221
- const full = path.join(repoRoot, relativePath);
222
- if (!fs.existsSync(full)) return;
223
- const stat = fs.statSync(full);
224
- if (stat.isDirectory()) {
225
- for (const entry of fs.readdirSync(full)) walk(path.join(relativePath, entry));
226
- return;
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 changed = targets.some((target) => target.created > 0 || target.overwritten > 0);
264
- const onlySkipped = targets.every((target) => target.created === 0 && target.overwritten === 0 && target.skipped > 0);
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 = JSON.parse(fs.readFileSync(path.join(targetRoot, "package.json"), "utf8"));
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 = JSON.parse(fs.readFileSync(packagePath, "utf8"));
535
+ const pkg = readJsonSafe(packagePath, {});
482
536
  const scripts = { ...(pkg.scripts || {}) };
483
537
  const additions = {
484
538
  "harness:dev": "coding-agent-harness dev .",