coding-agent-harness 1.0.8 → 1.1.0

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 (232) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/CONTRIBUTING.md +8 -4
  3. package/README.md +12 -2
  4. package/README.zh-CN.md +10 -2
  5. package/SKILL.md +14 -3
  6. package/dist/build-dist.mjs +19 -6
  7. package/dist/check-dist-observation.mjs +57 -29
  8. package/dist/check-harness.mjs +0 -1
  9. package/dist/check-import-graph.mjs +44 -27
  10. package/dist/check-lite-forbidden-surfaces.mjs +121 -0
  11. package/dist/check-no-ts-nocheck.mjs +7 -7
  12. package/dist/check-runtime-emit.mjs +10 -3
  13. package/dist/check-type-boundaries.mjs +51 -9
  14. package/dist/commands/dashboard-command.mjs +52 -14
  15. package/dist/commands/migration-command.mjs +18 -8
  16. package/dist/commands/module-command.mjs +142 -0
  17. package/dist/commands/preset-command.mjs +51 -12
  18. package/dist/commands/registry.mjs +483 -0
  19. package/dist/commands/task-command.mjs +109 -52
  20. package/dist/harness.mjs +6 -304
  21. package/dist/lib/capability-registry.mjs +229 -53
  22. package/dist/lib/check-module-parallel.mjs +1 -6
  23. package/dist/lib/check-profiles.mjs +39 -46
  24. package/dist/lib/check-task-contracts.mjs +6 -4
  25. package/dist/lib/command-registry.mjs +248 -0
  26. package/dist/lib/core-shared.mjs +78 -3
  27. package/dist/lib/dashboard-data.mjs +203 -22
  28. package/dist/lib/dashboard-workbench.mjs +245 -21
  29. package/dist/lib/dashboard-writer.mjs +4 -1
  30. package/dist/lib/git-status-summary.mjs +0 -1
  31. package/dist/lib/governance-index-generator.mjs +7 -5
  32. package/dist/lib/governance-sync.mjs +46 -121
  33. package/dist/lib/governance-table-boundary.mjs +1 -14
  34. package/dist/lib/harness-core.mjs +4 -1
  35. package/dist/lib/harness-paths.mjs +115 -1
  36. package/dist/lib/impact-classifier.mjs +420 -0
  37. package/dist/lib/lesson-maintenance.mjs +1 -2
  38. package/dist/lib/markdown-utils.mjs +50 -1
  39. package/dist/lib/migration-planner.mjs +31 -16
  40. package/dist/lib/migration-support.mjs +5 -4
  41. package/dist/lib/module-registry.mjs +296 -0
  42. package/dist/lib/preset-audit-contracts.mjs +24 -1
  43. package/dist/lib/preset-engine.mjs +67 -29
  44. package/dist/lib/preset-registry.mjs +361 -71
  45. package/dist/lib/preset-runner.mjs +292 -26
  46. package/dist/lib/review-confirm-git-gate.mjs +73 -19
  47. package/dist/lib/status-builder.mjs +23 -8
  48. package/dist/lib/structure-migration.mjs +6 -4
  49. package/dist/lib/subagent-authorization-audit.mjs +8 -2
  50. package/dist/lib/task-archive-eligibility.mjs +65 -0
  51. package/dist/lib/task-audit-metadata.mjs +25 -11
  52. package/dist/lib/task-audit-migration.mjs +21 -14
  53. package/dist/lib/task-discovery-contract.mjs +32 -0
  54. package/dist/lib/task-index.mjs +3 -2
  55. package/dist/lib/task-lesson-candidates.mjs +1 -2
  56. package/dist/lib/task-lesson-sedimentation.mjs +310 -9
  57. package/dist/lib/task-lifecycle/create-task-helpers.mjs +6 -3
  58. package/dist/lib/task-lifecycle/phase-sync.mjs +0 -1
  59. package/dist/lib/task-lifecycle/preset-interop.mjs +16 -0
  60. package/dist/lib/task-lifecycle/review-confirm.mjs +34 -2
  61. package/dist/lib/task-lifecycle/review-gates.mjs +12 -5
  62. package/dist/lib/task-lifecycle/review-submission.mjs +1 -2
  63. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +0 -1
  64. package/dist/lib/task-lifecycle/template-files.mjs +2 -5
  65. package/dist/lib/task-lifecycle.mjs +116 -160
  66. package/dist/lib/task-metadata.mjs +10 -5
  67. package/dist/lib/task-preset-contract-drift.mjs +45 -0
  68. package/dist/lib/task-repository.mjs +192 -0
  69. package/dist/lib/task-review-model.mjs +36 -17
  70. package/dist/lib/task-scanner.mjs +74 -23
  71. package/dist/lib/task-template-materials.mjs +131 -0
  72. package/dist/lib/task-tombstone-commands.mjs +186 -29
  73. package/dist/lib/types/check-profiles.js +1 -0
  74. package/dist/lib/types/impact.js +1 -0
  75. package/dist/lib/types/preset.js +1 -0
  76. package/dist/lib/types/task-lifecycle.js +1 -0
  77. package/dist/lib/types/task-scanner.js +1 -0
  78. package/dist/postinstall.mjs +2 -2
  79. package/dist/run-built-tests.mjs +10 -3
  80. package/docs-release/README.md +1 -0
  81. package/docs-release/architecture/document-contract-kernel/README.md +150 -0
  82. package/docs-release/architecture/document-contract-kernel/products/full-skill-overlay.md +29 -0
  83. package/docs-release/architecture/document-contract-kernel/products/lite-forbidden-surfaces.txt +26 -0
  84. package/docs-release/architecture/document-contract-kernel/products/lite-skill-overlay.md +37 -0
  85. package/docs-release/architecture/overview.md +2 -2
  86. package/docs-release/architecture/overview.zh-CN.md +2 -2
  87. package/docs-release/architecture/system-explainer/01-system-overview.md +11 -7
  88. package/docs-release/architecture/system-explainer/02-module-dependency.md +4 -4
  89. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +17 -12
  90. package/docs-release/architecture/system-explainer/05-data-flow.md +6 -6
  91. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +2 -2
  92. package/docs-release/architecture/system-explainer/README.md +1 -1
  93. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +12 -8
  94. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +5 -5
  95. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +19 -11
  96. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
  97. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +2 -2
  98. package/docs-release/architecture/system-explainer/en-US/README.md +1 -1
  99. package/docs-release/guides/agent-installation.en-US.md +4 -6
  100. package/docs-release/guides/agent-installation.md +11 -8
  101. package/docs-release/guides/contributing.md +10 -3
  102. package/docs-release/guides/contributing.zh-CN.md +10 -3
  103. package/docs-release/guides/legacy-migration-agent-prompt.md +1 -1
  104. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +1 -1
  105. package/docs-release/guides/migration-playbook.en-US.md +9 -6
  106. package/docs-release/guides/migration-playbook.md +9 -6
  107. package/docs-release/guides/preset-development.md +68 -2
  108. package/docs-release/guides/task-state-machine.en-US.md +8 -8
  109. package/docs-release/guides/task-state-machine.md +7 -7
  110. package/docs-release/guides/typescript-runtime-migration-closeout.md +17 -13
  111. package/package.json +16 -12
  112. package/postinstall.mjs +37 -0
  113. package/presets/legacy-migration/preset.yaml +5 -5
  114. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  115. package/presets/lesson-sedimentation/preset.yaml +3 -3
  116. package/presets/module/preset.yaml +2 -2
  117. package/presets/module/templates/execution_strategy.append.md +1 -1
  118. package/presets/module/templates/task_plan.append.md +3 -3
  119. package/presets/release-closeout/checks/check-release-package.mjs +6 -1
  120. package/presets/release-closeout/preset.yaml +9 -9
  121. package/presets/release-closeout/scripts/generate-release-package.mjs +387 -25
  122. package/presets/release-closeout/templates/task_plan.append.md +5 -5
  123. package/presets/standard-task/preset.yaml +2 -2
  124. package/references/agents-md-pattern.md +23 -17
  125. package/references/lessons-governance.md +2 -2
  126. package/references/module-parallel-standard.md +3 -6
  127. package/references/ssot-governance.md +2 -2
  128. package/references/taskr-gap-analysis.md +3 -3
  129. package/run-dist.mjs +34 -0
  130. package/skills/preset-creator/SKILL.md +40 -8
  131. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -8
  132. package/skills/preset-creator/references/preset-package-skeleton.md +15 -5
  133. package/skills/preset-creator/references/structure-aware-paths.md +112 -0
  134. package/templates/AGENTS.md.template +28 -26
  135. package/templates/architecture/README.md +2 -2
  136. package/templates/architecture/service-catalog.md +2 -2
  137. package/templates/architecture/services/service-template.md +1 -1
  138. package/templates/dashboard/assets/app-src/00-state.js +5 -1
  139. package/templates/dashboard/assets/app-src/10-router.js +7 -0
  140. package/templates/dashboard/assets/app-src/20-overview.js +8 -8
  141. package/templates/dashboard/assets/app-src/30-tasks.js +132 -40
  142. package/templates/dashboard/assets/app-src/32-task-swimlane.js +314 -0
  143. package/templates/dashboard/assets/app-src/35-task-detail.js +35 -5
  144. package/templates/dashboard/assets/app-src/40-modules.js +257 -41
  145. package/templates/dashboard/assets/app-src/45-review.js +127 -1
  146. package/templates/dashboard/assets/app-src/90-bindings.js +185 -2
  147. package/templates/dashboard/assets/app.css +928 -53
  148. package/templates/dashboard/assets/app.css.manifest.json +2 -0
  149. package/templates/dashboard/assets/app.js +1071 -98
  150. package/templates/dashboard/assets/app.manifest.json +1 -0
  151. package/templates/dashboard/assets/css-src/00-foundation.css +12 -6
  152. package/templates/dashboard/assets/css-src/10-panels-flow.css +2 -2
  153. package/templates/dashboard/assets/css-src/30-task-index.css +21 -13
  154. package/templates/dashboard/assets/css-src/31-archive.css +94 -0
  155. package/templates/dashboard/assets/css-src/32-task-swimlane.css +487 -0
  156. package/templates/dashboard/assets/css-src/35-review-workspace.css +78 -0
  157. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +191 -14
  158. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +23 -0
  159. package/templates/dashboard/assets/i18n.js +166 -2
  160. package/templates/development/README.md +9 -9
  161. package/templates/development/cross-repo-debugging.md +3 -3
  162. package/templates/development/external-context/service-template.md +1 -1
  163. package/templates/development/external-source-packs/README.md +2 -2
  164. package/templates/integrations/README.md +4 -4
  165. package/templates/integrations/api-contract.md +1 -1
  166. package/templates/integrations/event-contract.md +1 -1
  167. package/templates/integrations/third-party/vendor-template.md +1 -1
  168. package/templates/integrations/webhook-contract.md +1 -1
  169. package/templates/ledger/Harness-Ledger.md +1 -1
  170. package/templates/modules/module_brief.md +50 -0
  171. package/templates/modules/module_plan.md +49 -0
  172. package/templates/modules/registry_view.md +9 -0
  173. package/templates/modules/session_prompt_pack.md +55 -0
  174. package/templates/planning/brief.md +32 -8
  175. package/templates/planning/module_brief.md +28 -3
  176. package/templates/planning/module_plan.md +26 -11
  177. package/templates/planning/module_session_prompt.md +11 -2
  178. package/templates/planning/optional/slices/_slice-template/brief.md +28 -0
  179. package/templates/planning/review.md +1 -1
  180. package/templates/planning/visual_map.md +1 -1
  181. package/templates/reference/docs-library-standard.md +7 -7
  182. package/templates/reference/execution-workflow-standard.md +13 -0
  183. package/templates/reference/external-source-intake-standard.md +10 -10
  184. package/templates/reference/repo-governance-standard.md +1 -1
  185. package/templates/reference/review-routing-standard.md +4 -0
  186. package/templates/ssot/Module-Registry.md +4 -38
  187. package/templates/walkthrough/walkthrough-template.md +1 -1
  188. package/templates-zh-CN/AGENTS.md.template +27 -25
  189. package/templates-zh-CN/CLAUDE.md.template +1 -1
  190. package/templates-zh-CN/architecture/README.md +2 -2
  191. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  192. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  193. package/templates-zh-CN/development/README.md +9 -9
  194. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  195. package/templates-zh-CN/development/external-context/service-template.md +1 -1
  196. package/templates-zh-CN/development/external-source-packs/README.md +2 -2
  197. package/templates-zh-CN/integrations/README.md +4 -4
  198. package/templates-zh-CN/integrations/api-contract.md +1 -1
  199. package/templates-zh-CN/integrations/event-contract.md +1 -1
  200. package/templates-zh-CN/integrations/third-party/vendor-template.md +1 -1
  201. package/templates-zh-CN/integrations/webhook-contract.md +1 -1
  202. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  203. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  204. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  205. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  206. package/templates-zh-CN/modules/module_brief.md +47 -0
  207. package/templates-zh-CN/modules/module_plan.md +48 -0
  208. package/templates-zh-CN/modules/registry_view.md +9 -0
  209. package/templates-zh-CN/modules/session_prompt_pack.md +50 -0
  210. package/templates-zh-CN/planning/INDEX.md +1 -0
  211. package/templates-zh-CN/planning/brief.md +26 -7
  212. package/templates-zh-CN/planning/module_brief.md +24 -2
  213. package/templates-zh-CN/planning/module_plan.md +35 -29
  214. package/templates-zh-CN/planning/module_session_prompt.md +15 -11
  215. package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +28 -11
  216. package/templates-zh-CN/planning/review.md +1 -1
  217. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  218. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  219. package/templates-zh-CN/reference/docs-library-standard.md +27 -27
  220. package/templates-zh-CN/reference/execution-workflow-standard.md +12 -2
  221. package/templates-zh-CN/reference/external-source-intake-standard.md +10 -10
  222. package/templates-zh-CN/reference/harness-ledger-standard.md +3 -3
  223. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  224. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  225. package/templates-zh-CN/reference/review-routing-standard.md +3 -0
  226. package/templates-zh-CN/reference/walkthrough-standard.md +2 -2
  227. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  228. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  229. package/templates-zh-CN/ssot/Delivery-SSoT.md +2 -2
  230. package/templates-zh-CN/ssot/Module-Registry.md +5 -44
  231. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  232. package/templates-zh-CN/walkthrough/walkthrough-template.md +4 -4
@@ -1,21 +1,35 @@
1
- // @ts-nocheck
2
- import { confirmTaskReview, createTask, readPresetPackage, buildTaskIndex, createLessonSedimentationTask, archiveTask, listLifecycleTasks, promoteLessonCandidate, reopenTask, softDeleteTask, supersedeTask, updateModuleStep, updateTaskPhase, updateTaskLifecycle, } from "../lib/harness-core.mjs";
1
+ import { createTask, readPresetPackage, buildTaskIndex, createLessonSedimentationTask, archiveTask, deleteTask, listLifecycleTasks, promoteLessonCandidate, reopenTask, supersedeTask, updateModuleStep, updateTaskPhase, updateTaskLifecycle, } from "../lib/harness-core.mjs";
2
+ import { takeRepeatedOptionsFromArgs } from "../lib/command-registry.mjs";
3
3
  export function runTaskCommand(command, { args, takeFlag, takeOption, targetArg }) {
4
4
  if (command === "new-task") {
5
5
  const dryRun = takeFlag("--dry-run");
6
6
  const locale = takeOption("--locale", "");
7
7
  const title = takeOption("--title", "");
8
8
  const moduleKey = takeOption("--module", "");
9
+ const registerModule = takeFlag("--register-module");
10
+ const moduleRegistration = {
11
+ title: takeOption("--module-title", ""),
12
+ prefix: takeOption("--module-prefix", ""),
13
+ status: takeOption("--module-status", "planned"),
14
+ branch: takeOption("--module-branch", ""),
15
+ owner: takeOption("--module-owner", "coordinator"),
16
+ currentStep: takeOption("--module-current-step", ""),
17
+ locale,
18
+ scope: takeRepeatedOptionsFromArgs(args, "--module-scope"),
19
+ shared: takeRepeatedOptionsFromArgs(args, "--module-shared"),
20
+ dependsOn: takeRepeatedOptionsFromArgs(args, "--module-depends-on"),
21
+ };
9
22
  const budget = takeOption("--budget", "standard");
10
23
  const preset = takeOption("--preset", "");
11
24
  const fromSession = takeOption("--from-session", "");
12
25
  const longRunning = takeFlag("--long-running");
13
26
  try {
14
27
  const parsed = parseNewTaskArgs(args, { preset, fromSession });
15
- console.log(JSON.stringify(createTask(parsed.target, parsed.taskId, { title, locale, dryRun, moduleKey, budget, longRunning, preset, fromSession, presetArgs: parsed.presetArgs, automaticTaskId: parsed.automaticTaskId }), null, 2));
28
+ const createOptions = { title, locale, dryRun, moduleKey, budget, longRunning, preset, fromSession, presetArgs: parsed.presetArgs, automaticTaskId: parsed.automaticTaskId, registerModule, moduleRegistration };
29
+ console.log(JSON.stringify(invokeCreateTask(parsed.target, parsed.taskId, createOptions), null, 2));
16
30
  }
17
31
  catch (error) {
18
- console.error(error.message);
32
+ console.error(errorMessage(error));
19
33
  process.exit(1);
20
34
  }
21
35
  return;
@@ -34,7 +48,7 @@ export function runTaskCommand(command, { args, takeFlag, takeOption, targetArg
34
48
  console.log(JSON.stringify(updateTaskPhase(targetArg(), taskId, phaseId, { state, completion, evidenceStatus }), null, 2));
35
49
  }
36
50
  catch (error) {
37
- console.error(error.message);
51
+ console.error(errorMessage(error));
38
52
  process.exit(1);
39
53
  }
40
54
  return;
@@ -47,37 +61,19 @@ export function runTaskCommand(command, { args, takeFlag, takeOption, targetArg
47
61
  console.error("Missing task id");
48
62
  process.exit(2);
49
63
  }
50
- const lifecycle = {
64
+ const lifecycleByCommand = {
51
65
  "task-start": { event: "task-start", state: "in_progress" },
52
66
  "task-log": { event: "task-log", state: "" },
53
67
  "task-block": { event: "task-block", state: "blocked" },
54
68
  "task-review": { event: "task-review", state: "review" },
55
69
  "task-complete": { event: "task-complete", state: "done" },
56
- }[command];
70
+ };
71
+ const lifecycle = lifecycleByCommand[command];
57
72
  try {
58
73
  console.log(JSON.stringify(updateTaskLifecycle(targetArg(), taskId, { ...lifecycle, message, evidence }), null, 2));
59
74
  }
60
75
  catch (error) {
61
- console.error(error.message);
62
- process.exit(1);
63
- }
64
- return;
65
- }
66
- if (command === "review-confirm") {
67
- const reviewer = takeOption("--reviewer", "Human Reviewer");
68
- const message = takeOption("--message", "");
69
- const evidence = takeOption("--evidence", "");
70
- const confirmText = takeOption("--confirm", "");
71
- const taskId = args.shift();
72
- if (!taskId) {
73
- console.error("Missing task id");
74
- process.exit(2);
75
- }
76
- try {
77
- console.log(JSON.stringify(confirmTaskReview(targetArg(), taskId, { reviewer, message, evidence, confirmText }), null, 2));
78
- }
79
- catch (error) {
80
- console.error(formatTaskCommandError(error));
76
+ console.error(errorMessage(error));
81
77
  process.exit(1);
82
78
  }
83
79
  return;
@@ -95,7 +91,7 @@ export function runTaskCommand(command, { args, takeFlag, takeOption, targetArg
95
91
  console.log(JSON.stringify(promoteLessonCandidate(targetArg(), taskId, candidateId, { dryRun, apply }), null, 2));
96
92
  }
97
93
  catch (error) {
98
- console.error(error.message);
94
+ console.error(errorMessage(error));
99
95
  process.exit(1);
100
96
  }
101
97
  return;
@@ -113,7 +109,7 @@ export function runTaskCommand(command, { args, takeFlag, takeOption, targetArg
113
109
  console.log(JSON.stringify(createLessonSedimentationTask(targetArg(), taskId, candidateId, { dryRun, title }), null, 2));
114
110
  }
115
111
  catch (error) {
116
- console.error(error.message);
112
+ console.error(errorMessage(error));
117
113
  process.exit(1);
118
114
  }
119
115
  return;
@@ -152,40 +148,49 @@ export function runTaskCommand(command, { args, takeFlag, takeOption, targetArg
152
148
  if (command === "task-supersede") {
153
149
  const by = takeOption("--by", "");
154
150
  const reason = takeOption("--reason", "");
151
+ const deletedBy = takeOption("--deleted-by", "");
152
+ const confirm = takeOption("--confirm", "");
153
+ const allowOpenFindings = takeFlag("--allow-open-findings");
155
154
  const taskId = args.shift();
156
155
  if (!taskId) {
157
156
  console.error("Missing task id");
158
157
  process.exit(2);
159
158
  }
160
159
  try {
161
- console.log(JSON.stringify(supersedeTask(targetArg(), taskId, { by, reason }), null, 2));
160
+ console.log(JSON.stringify(supersedeTask(targetArg(), taskId, { by, reason, deletedBy, confirm, allowOpenFindings }), null, 2));
162
161
  }
163
162
  catch (error) {
164
- console.error(error.message);
163
+ console.error(errorMessage(error));
165
164
  process.exit(1);
166
165
  }
167
166
  return;
168
167
  }
169
168
  if (["task-delete", "task-archive", "task-reopen"].includes(command)) {
170
169
  const soft = takeFlag("--soft");
170
+ const hard = takeFlag("--hard");
171
171
  const reason = takeOption("--reason", "");
172
+ const deletedBy = command === "task-delete" ? takeOption("--deleted-by", "") : "";
173
+ const confirm = command === "task-delete" ? takeOption("--confirm", "") : "";
174
+ const allowOpenFindings = command === "task-delete" ? takeFlag("--allow-open-findings") : false;
175
+ const archivedBy = command === "task-archive" ? takeOption("--archived-by", "") : "";
176
+ const archiveFields = command === "task-archive" ? takeRepeatedKeyValueOptions(args, "--archive-field") : {};
172
177
  const taskId = args.shift();
173
178
  if (!taskId) {
174
179
  console.error("Missing task id");
175
180
  process.exit(2);
176
181
  }
177
182
  try {
178
- if (command === "task-delete" && !soft)
179
- throw new Error("task-delete only supports --soft; hard delete is intentionally disabled.");
183
+ if (command === "task-delete" && soft && hard)
184
+ throw new Error("task-delete accepts only one of --soft or --hard");
180
185
  const result = command === "task-delete"
181
- ? softDeleteTask(targetArg(), taskId, { reason })
186
+ ? deleteTask(targetArg(), taskId, { hard, reason, deletedBy, confirm, allowOpenFindings })
182
187
  : command === "task-archive"
183
- ? archiveTask(targetArg(), taskId, { reason })
188
+ ? archiveTask(targetArg(), taskId, { reason, archivedBy, archiveFields })
184
189
  : reopenTask(targetArg(), taskId, { reason });
185
190
  console.log(JSON.stringify(result, null, 2));
186
191
  }
187
192
  catch (error) {
188
- console.error(error.message);
193
+ console.error(errorMessage(error));
189
194
  process.exit(1);
190
195
  }
191
196
  return;
@@ -202,14 +207,15 @@ export function runTaskCommand(command, { args, takeFlag, takeOption, targetArg
202
207
  console.log(JSON.stringify(updateModuleStep(targetArg(), moduleKey, stepId, { state }), null, 2));
203
208
  }
204
209
  catch (error) {
205
- console.error(error.message);
210
+ console.error(errorMessage(error));
206
211
  process.exit(1);
207
212
  }
208
213
  return;
209
214
  }
210
215
  throw new Error(`Unsupported task command: ${command}`);
211
216
  }
212
- function parseNewTaskArgs(args, { preset = "" } = {}) {
217
+ function parseNewTaskArgs(args, { preset = "", fromSession = "" } = {}) {
218
+ void fromSession;
213
219
  const values = [...args];
214
220
  const presetPackage = preset ? readPresetPackageForNewTask(preset, values) : null;
215
221
  const parsed = splitPresetArgsAndPositionals(values, presetPackage);
@@ -221,6 +227,9 @@ function parseNewTaskArgs(args, { preset = "" } = {}) {
221
227
  presetArgs: parsed.presetArgs,
222
228
  };
223
229
  }
230
+ function invokeCreateTask(target, taskId, options) {
231
+ return Reflect.apply(createTask, undefined, [target, taskId, options]);
232
+ }
224
233
  function readPresetPackageForNewTask(preset, values) {
225
234
  const candidates = presetDiscoveryTargetCandidates(values);
226
235
  let fallbackPackage = null;
@@ -301,25 +310,73 @@ function resolveNewTaskPositionals(positionals) {
301
310
  throw new Error(`Too many positional arguments for new-task: ${positionals.join(", ")}`);
302
311
  }
303
312
  function formatTaskCommandError(error) {
304
- const lines = [error.message];
305
- if (Array.isArray(error.recovery) && error.recovery.length > 0) {
313
+ const recovery = readArrayProperty(error, "recovery");
314
+ const details = readRecordProperty(error, "details");
315
+ const lines = [errorMessage(error)];
316
+ if (recovery.length > 0) {
306
317
  lines.push("", "Recovery:");
307
- for (const item of error.recovery)
308
- lines.push(`- ${item}`);
318
+ for (const item of recovery)
319
+ lines.push(`- ${String(item)}`);
309
320
  }
310
- if (error.details?.entries?.length) {
321
+ const entries = readArrayProperty(details, "entries");
322
+ if (entries.length) {
311
323
  lines.push("", "Blocking Git status:");
312
- for (const entry of error.details.entries)
313
- lines.push(`- ${entry.raw || entry.path}`);
324
+ for (const entry of entries) {
325
+ const raw = readProperty(entry, "raw");
326
+ const entryPath = readProperty(entry, "path");
327
+ lines.push(`- ${String(raw || entryPath || "")}`);
328
+ }
314
329
  }
315
- if (error.details?.disallowed?.length) {
330
+ const disallowed = readArrayProperty(details, "disallowed");
331
+ if (disallowed.length) {
316
332
  lines.push("", "Disallowed paths:");
317
- for (const item of error.details.disallowed)
318
- lines.push(`- ${item}`);
333
+ for (const item of disallowed)
334
+ lines.push(`- ${String(item)}`);
319
335
  }
320
- if (error.details?.stderr)
321
- lines.push("", error.details.stderr);
322
- if (error.details?.stdout)
323
- lines.push("", error.details.stdout);
336
+ const stderr = readProperty(details, "stderr");
337
+ const stdout = readProperty(details, "stdout");
338
+ if (stderr)
339
+ lines.push("", String(stderr));
340
+ if (stdout)
341
+ lines.push("", String(stdout));
324
342
  return lines.join("\n");
325
343
  }
344
+ function takeRepeatedKeyValueOptions(args, flag) {
345
+ const fields = {};
346
+ for (let index = 0; index < args.length;) {
347
+ if (args[index] !== flag) {
348
+ index += 1;
349
+ continue;
350
+ }
351
+ const raw = args[index + 1] || "";
352
+ args.splice(index, 2);
353
+ const separator = raw.indexOf("=");
354
+ if (separator <= 0)
355
+ throw new Error(`${flag} requires key=value`);
356
+ const key = raw.slice(0, separator).trim();
357
+ const value = raw.slice(separator + 1).trim();
358
+ if (!key || /[\r\n|]/.test(key))
359
+ throw new Error(`${flag} has invalid field key: ${key || "<empty>"}`);
360
+ if (Object.prototype.hasOwnProperty.call(fields, key))
361
+ throw new Error(`${flag} duplicate field key: ${key}`);
362
+ fields[key] = value;
363
+ }
364
+ return fields;
365
+ }
366
+ function readArrayProperty(value, key) {
367
+ const property = readProperty(value, key);
368
+ return Array.isArray(property) ? property : [];
369
+ }
370
+ function readRecordProperty(value, key) {
371
+ const property = readProperty(value, key);
372
+ return isRecord(property) ? property : null;
373
+ }
374
+ function readProperty(value, key) {
375
+ return isRecord(value) ? value[key] : undefined;
376
+ }
377
+ function isRecord(value) {
378
+ return typeof value === "object" && value !== null;
379
+ }
380
+ function errorMessage(error) {
381
+ return error instanceof Error ? error.message : String(error);
382
+ }
package/dist/harness.mjs CHANGED
@@ -1,305 +1,7 @@
1
1
  #!/usr/bin/env node
2
- // @ts-nocheck
3
- import fs from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import { createInterface } from "node:readline/promises";
7
- import { addCapability, buildStatus, doctorUserSkill, installUserSkill, normalizeLocale, rebuildGovernanceIndexes, serveDashboardWorkbench, validateSourcePackageBoundary, writeInitFiles, } from "./lib/harness-core.mjs";
8
- import { runDashboardCommand } from "./commands/dashboard-command.mjs";
9
- import { runMigrationCommand } from "./commands/migration-command.mjs";
10
- import { runPresetCommand } from "./commands/preset-command.mjs";
11
- import { runTaskCommand } from "./commands/task-command.mjs";
12
- const args = process.argv.slice(2);
13
- const command = args.shift() || "help";
14
- function takeFlag(name, fallback = false) {
15
- const index = args.indexOf(name);
16
- if (index < 0)
17
- return fallback;
18
- args.splice(index, 1);
19
- return true;
20
- }
21
- function takeOption(name, fallback = "") {
22
- const index = args.indexOf(name);
23
- if (index < 0)
24
- return fallback;
25
- const value = args[index + 1] || fallback;
26
- args.splice(index, 2);
27
- return value;
28
- }
29
- async function resolveInitLocale(requestedLocale) {
30
- if (requestedLocale)
31
- return normalizeLocale(requestedLocale);
32
- if (!process.stdin.isTTY || !process.stdout.isTTY)
33
- return "en-US";
34
- const prompt = [
35
- "Select harness language / 选择初始化语言:",
36
- " 1. 中文 (zh-CN)",
37
- " 2. English (en-US)",
38
- "Language [1/2, default 2]: ",
39
- ].join("\n");
40
- const reader = createInterface({ input: process.stdin, output: process.stdout });
41
- try {
42
- const answer = (await reader.question(prompt)).trim().toLowerCase();
43
- if (["1", "zh", "zh-cn", "cn", "中文"].includes(answer))
44
- return "zh-CN";
45
- if (["2", "en", "en-us", "english", "英文", ""].includes(answer))
46
- return "en-US";
47
- console.error(`Unknown language selection: ${answer}. Falling back to en-US.`);
48
- return "en-US";
49
- }
50
- finally {
51
- reader.close();
52
- }
53
- }
54
- async function confirmUserInstall({ yes = false, dryRun = false, agent = "codex" } = {}) {
55
- if (yes || dryRun)
56
- return true;
57
- if (!process.stdin.isTTY || !process.stdout.isTTY)
58
- return false;
59
- const reader = createInterface({ input: process.stdin, output: process.stdout });
60
- try {
61
- const answer = (await reader.question(`Install Coding Agent Harness into user skill directory for ${agent}? [y/N] `)).trim().toLowerCase();
62
- return ["y", "yes"].includes(answer);
63
- }
64
- finally {
65
- reader.close();
66
- }
67
- }
68
- function targetArg() {
69
- return args[args.length - 1] && !args[args.length - 1].startsWith("-") ? args[args.length - 1] : ".";
70
- }
71
- function printHelp() {
72
- console.log(`Coding Agent Harness
73
-
74
- Usage:
75
- harness check [--profile source-package|private-harness|target-project] [target]
76
- harness status [--json] [--strict] [target]
77
- harness dev [--no-open] [--out-dir folder] [--host 127.0.0.1] [--port n] [target]
78
- harness dashboard [--out file.html] [--out-dir folder] [--workbench] [--host 127.0.0.1] [--port n] [target]
79
- harness init [--dry-run] [--locale zh-CN|en-US] [--capabilities core,dashboard] [--add-npm-scripts] [target]
80
- harness add-capability <name> [--dry-run] [--locale zh-CN|en-US] [target]
81
- harness migrate-plan [--json] [--limit n] [target]
82
- harness migrate-structure [--plan|--apply] [--force] [--json] [target]
83
- harness migrate-task-audit-index [--plan] [--apply] [--json] [target]
84
- harness migrate-run [--locale zh-CN|en-US] [--assume-locale] [--allow-dirty] [--plan-only] [--out-dir folder] [--session-dir folder] [target]
85
- harness migrate-verify [--json] [--full-cutover] <session.json>
86
- harness governance rebuild [--dry-run] [--archive] [--apply] [target]
87
- harness preset list [--json] [target]
88
- harness preset inspect <id> [--json] [target]
89
- harness preset check <id> [--json] [target]
90
- harness preset install <folder|zip|builtin-id> [--project] [--force] [--json] [target]
91
- harness preset seed [--project] [--force] [--dry-run] [--json] [target]
92
- harness preset uninstall <id> [--project] [--json] [target]
93
- harness preset run <id> <plan|scaffold|check> --task <task-id> [--json] [target]
94
- 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]
95
- harness task-start <task-id> [--message text] [target]
96
- harness task-phase <task-id> <phase-id> [--state done] [--completion 100] [--evidence present] [target]
97
- harness task-log <task-id> --message text [--evidence type:PATH:summary] [target]
98
- harness task-block <task-id> [--message text] [target]
99
- harness task-review <task-id> [--message text] [target]
100
- harness review-confirm <task-id> --confirm task-id [--reviewer name] [--message text] [target]
101
- harness lesson-promote <task-id> <candidate-id> [--dry-run|--apply] [target]
102
- harness lesson-sediment <task-id> <candidate-id> [--dry-run] [--title title] [target]
103
- harness task-complete <task-id> [--message text] [target]
104
- harness task-list [--json] [--state state] [--module key] [--queue queue] [--preset id] [--review status] [--lesson status] [--missing-materials] [--include-archived] [--search text] [target]
105
- harness task-index [--json] [target]
106
- harness task-supersede <old-task-id> --by <new-task-id> [--reason text] [target]
107
- harness task-delete <task-id> --soft [--reason text] [target]
108
- harness task-archive <task-id> [--reason text] [target]
109
- harness task-reopen <task-id> [--reason text] [target]
110
- harness module-step <module-key> <step-id> [--state done|in-progress|blocked] [target]
111
- harness install-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir] [--dry-run] [--force] [--skip-presets] [--yes]
112
- harness doctor-user [--agent codex|claude|gemini|openclaw|agents|all] [--home dir]
113
-
114
- If init runs in an interactive terminal and --locale is omitted, it asks for a
115
- language. Non-interactive init defaults to en-US.
116
-
117
- Preset discovery:
118
- Project presets live in <target>/.coding-agent-harness/presets/<preset-id>/.
119
- User presets live in ~/.coding-agent-harness/presets/<preset-id>/.
120
- Harness discovers project presets first when a target is supplied, then user
121
- presets, then bundled package presets under presets/<preset-id>/.
122
- "harness init" seeds bundled presets into the target project. "harness
123
- install-user" and npm postinstall seed bundled presets into the user root.
124
- Use "harness preset seed" to repair or re-run preset seeding.
125
- Use "harness preset install" with a local preset folder, .zip archive, or
126
- bundled preset id. Preset archives must contain preset.yaml at the archive
127
- root or inside one top-level folder.
128
- Use "harness preset list --json" to see available presets, their source,
129
- purpose, compatible budgets, and manifest path. Use "harness preset inspect
130
- <id> --json" for the full preset manifest summary.
131
- `);
132
- }
133
- function exitWithReport(report) {
134
- for (const warning of report.warnings || [])
135
- console.log(`Warning: ${warning}`);
136
- for (const failure of report.failures || [])
137
- console.error(`Failure: ${failure}`);
138
- process.exit((report.failures || []).length > 0 ? 1 : 0);
139
- }
140
- if (command === "help" || command === "--help" || command === "-h") {
141
- printHelp();
142
- }
143
- else if (args[0] === "help" || args.includes("--help") || args.includes("-h")) {
144
- printHelp();
145
- }
146
- else if (command === "check") {
147
- const profile = takeOption("--profile", "target-project");
148
- const strict = takeFlag("--strict");
149
- const target = targetArg();
150
- const failures = [];
151
- const warnings = [];
152
- if (profile === "source-package") {
153
- for (const required of ["package.json", "dist/harness.mjs", "dist/check-harness.mjs", "templates/planning/task_plan.md"]) {
154
- if (!fs.existsSync(path.resolve(target, required)))
155
- failures.push(`missing source package file: ${required}`);
156
- }
157
- const boundary = validateSourcePackageBoundary(target);
158
- failures.push(...boundary.failures);
159
- warnings.push(...boundary.warnings);
160
- }
161
- const status = buildStatus(target, { skipLegacyCheck: profile === "source-package", strictLegacy: strict, strict, allowLegacyTarget: profile === "source-package" });
162
- failures.push(...status.checkState.details.failures);
163
- warnings.push(...status.checkState.details.warnings);
164
- if (!["source-package", "private-harness", "target-project"].includes(profile))
165
- failures.push(`unknown profile: ${profile}`);
166
- if (failures.length === 0)
167
- console.log(`Harness check passed (${profile}): ${path.resolve(target)}`);
168
- exitWithReport({ failures: [...new Set(failures)], warnings: [...new Set(warnings)] });
169
- }
170
- else if (command === "status") {
171
- const json = takeFlag("--json");
172
- const strict = takeFlag("--strict");
173
- const status = buildStatus(targetArg(), { strictLegacy: strict, strict });
174
- if (json) {
175
- console.log(JSON.stringify(status, null, 2));
176
- }
177
- else {
178
- console.log(`${status.project.name}: ${status.checkState.status} (${status.checkState.failures} failures, ${status.checkState.warnings} warnings)`);
179
- console.log(`mode: ${status.mode}`);
180
- console.log(`capabilities: ${status.capabilities.map((capability) => `${capability.name}:${capability.state}`).join(", ")}`);
181
- console.log(`tasks: ${status.tasks.length}`);
182
- }
183
- process.exitCode = status.checkState.status === "fail" ? 1 : 0;
184
- }
185
- else if (command === "dev") {
186
- const open = !takeFlag("--no-open");
187
- const outDir = takeOption("--out-dir", "");
188
- const host = takeOption("--host", "127.0.0.1");
189
- const port = takeOption("--port", "0");
190
- const localeOverride = takeOption("--locale", "");
191
- const target = targetArg();
192
- const usesDefaultOutDir = !outDir;
193
- const dashboardOutDir = outDir || defaultDevOutDir(target);
194
- const opts = { ...(localeOverride ? { localeOverride } : {}), recoverGeneratedDashboard: usesDefaultOutDir };
195
- try {
196
- await serveDashboardWorkbench(dashboardOutDir, target, { ...opts, host, port, autoRefresh: true, open, label: "harness dev" });
197
- }
198
- catch (error) {
199
- console.error(error.message);
200
- process.exit(1);
201
- }
202
- }
203
- else if (command === "dashboard") {
204
- await runDashboardCommand({ takeFlag, takeOption, targetArg });
205
- }
206
- else if (command === "init") {
207
- const dryRun = takeFlag("--dry-run");
208
- const addNpmScripts = takeFlag("--add-npm-scripts");
209
- const locale = await resolveInitLocale(takeOption("--locale", ""));
210
- const capabilities = takeOption("--capabilities", "core").split(",").map((item) => item.trim()).filter(Boolean);
211
- try {
212
- const result = writeInitFiles(targetArg(), capabilities, { dryRun, locale, addNpmScripts });
213
- 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));
214
- }
215
- catch (error) {
216
- console.error(error.message);
217
- process.exit(1);
218
- }
219
- }
220
- else if (command === "add-capability") {
221
- const dryRun = takeFlag("--dry-run");
222
- const locale = normalizeLocale(takeOption("--locale", ""));
223
- const capability = args.shift();
224
- if (!capability) {
225
- console.error("Missing capability name");
226
- process.exit(2);
227
- }
228
- try {
229
- const result = addCapability(targetArg(), capability, { dryRun, locale });
230
- console.log(JSON.stringify({ dryRun, registry: result.registry, changes: result.changes, report: result.report }, null, 2));
231
- }
232
- catch (error) {
233
- console.error(error.message);
234
- process.exit(1);
235
- }
236
- }
237
- else if (["migrate-plan", "migrate-structure", "migrate-run", "migrate-verify", "migrate-task-audit-index"].includes(command)) {
238
- runMigrationCommand(command, { args, takeFlag, takeOption, targetArg });
239
- }
240
- else if (command === "governance") {
241
- const subcommand = args.shift() || "";
242
- if (subcommand !== "rebuild") {
243
- console.error(`Unknown governance subcommand: ${subcommand || "(missing)"}`);
244
- process.exit(2);
245
- }
246
- const dryRun = takeFlag("--dry-run");
247
- const archive = takeFlag("--archive");
248
- const apply = takeFlag("--apply");
249
- try {
250
- console.log(JSON.stringify(rebuildGovernanceIndexes(targetArg(), { dryRun, archive, apply }), null, 2));
251
- }
252
- catch (error) {
253
- console.error(error.message);
254
- process.exit(1);
255
- }
256
- }
257
- else if (command === "preset") {
258
- runPresetCommand({ args, takeFlag, targetArg });
259
- }
260
- 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)) {
261
- runTaskCommand(command, { args, takeFlag, takeOption, targetArg });
262
- }
263
- else if (command === "install-user") {
264
- const dryRun = takeFlag("--dry-run");
265
- const force = takeFlag("--force");
266
- const yes = takeFlag("--yes") || takeFlag("-y");
267
- const skipPresets = takeFlag("--skip-presets");
268
- takeFlag("--global");
269
- const agent = takeOption("--agent", "codex");
270
- const home = takeOption("--home", "");
271
- if (!(await confirmUserInstall({ yes, dryRun, agent }))) {
272
- console.error("Refusing to write user skill files without confirmation. Re-run with --yes or --dry-run.");
273
- process.exit(2);
274
- }
275
- try {
276
- console.log(JSON.stringify(installUserSkill({ agent, home, dryRun, force, seedPresets: !skipPresets }), null, 2));
277
- }
278
- catch (error) {
279
- console.error(error.message);
280
- process.exit(1);
281
- }
282
- }
283
- else if (command === "doctor-user") {
284
- const agent = takeOption("--agent", "codex");
285
- const home = takeOption("--home", "");
286
- try {
287
- const report = doctorUserSkill({ agent, home });
288
- console.log(JSON.stringify(report, null, 2));
289
- process.exit(report.status === "pass" ? 0 : 1);
290
- }
291
- catch (error) {
292
- console.error(error.message);
293
- process.exit(1);
294
- }
295
- }
296
- else {
297
- printHelp();
298
- process.exit(2);
299
- }
300
- function defaultDevOutDir(targetInput) {
301
- const target = path.resolve(targetInput || ".");
302
- const name = path.basename(target) || "project";
303
- const hash = Buffer.from(target).toString("hex").slice(0, 16);
304
- return path.join(os.tmpdir(), "coding-agent-harness-dev", `${name}-${hash}`);
305
- }
2
+ import { commandRegistry } from "./commands/registry.mjs";
3
+ import { dispatchCommand } from "./lib/command-registry.mjs";
4
+ await Promise.resolve(dispatchCommand(commandRegistry, process.argv.slice(2))).catch((error) => {
5
+ console.error(error instanceof Error ? error.message : String(error));
6
+ process.exit(1);
7
+ });