coding-agent-harness 1.0.5 → 1.0.6

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 (260) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/README.md +63 -3
  3. package/README.zh-CN.md +52 -3
  4. package/SKILL.md +43 -43
  5. package/dist/build-dist.mjs +189 -0
  6. package/dist/check-dist-observation.mjs +428 -0
  7. package/dist/check-harness.mjs +489 -0
  8. package/dist/check-import-graph.mjs +511 -0
  9. package/dist/check-runtime-emit.mjs +304 -0
  10. package/dist/check-type-boundaries.mjs +139 -0
  11. package/dist/commands/dashboard-command.mjs +80 -0
  12. package/dist/commands/migration-command.mjs +152 -0
  13. package/dist/commands/preset-command.mjs +91 -0
  14. package/dist/commands/task-command.mjs +324 -0
  15. package/dist/harness.mjs +304 -0
  16. package/dist/lib/capability-registry.mjs +643 -0
  17. package/dist/lib/check-module-parallel.mjs +227 -0
  18. package/dist/lib/check-profiles.mjs +414 -0
  19. package/dist/lib/check-task-contracts.mjs +54 -0
  20. package/dist/lib/core-shared.mjs +254 -0
  21. package/dist/lib/dashboard-data.mjs +608 -0
  22. package/dist/lib/dashboard-workbench.mjs +334 -0
  23. package/dist/lib/dashboard-writer.mjs +200 -0
  24. package/dist/lib/git-status-summary.mjs +45 -0
  25. package/dist/lib/governance-index-generator.mjs +236 -0
  26. package/dist/lib/governance-sync.mjs +617 -0
  27. package/dist/lib/governance-table-boundary.mjs +161 -0
  28. package/{scripts → dist}/lib/harness-core.mjs +2 -0
  29. package/dist/lib/harness-paths.mjs +338 -0
  30. package/dist/lib/lesson-maintenance.mjs +139 -0
  31. package/dist/lib/markdown-utils.mjs +193 -0
  32. package/dist/lib/migration-planner.mjs +439 -0
  33. package/dist/lib/migration-support.mjs +317 -0
  34. package/dist/lib/phase-kind.mjs +46 -0
  35. package/dist/lib/preset-audit-contracts.mjs +40 -0
  36. package/dist/lib/preset-engine.mjs +516 -0
  37. package/dist/lib/preset-registry.mjs +831 -0
  38. package/dist/lib/preset-resource-contracts.mjs +83 -0
  39. package/dist/lib/review-confirm-git-gate.mjs +244 -0
  40. package/dist/lib/status-builder.mjs +87 -0
  41. package/{scripts → dist}/lib/status-dashboard-renderer.mjs +44 -46
  42. package/dist/lib/structure-migration.mjs +404 -0
  43. package/dist/lib/subagent-authorization-audit.mjs +198 -0
  44. package/dist/lib/task-audit-metadata.mjs +376 -0
  45. package/dist/lib/task-audit-migration.mjs +355 -0
  46. package/dist/lib/task-completion-consistency.mjs +29 -0
  47. package/dist/lib/task-index.mjs +133 -0
  48. package/dist/lib/task-lesson-candidates.mjs +239 -0
  49. package/dist/lib/task-lesson-sedimentation.mjs +300 -0
  50. package/dist/lib/task-lifecycle/create-task-helpers.mjs +84 -0
  51. package/dist/lib/task-lifecycle/phase-sync.mjs +82 -0
  52. package/dist/lib/task-lifecycle/review-confirm.mjs +93 -0
  53. package/dist/lib/task-lifecycle/review-gates.mjs +62 -0
  54. package/dist/lib/task-lifecycle/review-submission.mjs +52 -0
  55. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +54 -0
  56. package/dist/lib/task-lifecycle/template-files.mjs +52 -0
  57. package/dist/lib/task-lifecycle/text-utils.mjs +26 -0
  58. package/dist/lib/task-lifecycle.mjs +611 -0
  59. package/dist/lib/task-metadata.mjs +116 -0
  60. package/dist/lib/task-review-model.mjs +474 -0
  61. package/dist/lib/task-scanner.mjs +439 -0
  62. package/dist/lib/task-tombstone-commands.mjs +125 -0
  63. package/dist/postinstall.mjs +14 -0
  64. package/dist/run-built-tests.mjs +84 -0
  65. package/docs-release/README.md +1 -0
  66. package/docs-release/architecture/overview.md +12 -12
  67. package/docs-release/architecture/overview.zh-CN.md +12 -12
  68. package/docs-release/architecture/system-explainer/01-system-overview.md +15 -14
  69. package/docs-release/architecture/system-explainer/02-module-dependency.md +8 -8
  70. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +3 -3
  71. package/docs-release/architecture/system-explainer/04-check-and-governance.md +9 -7
  72. package/docs-release/architecture/system-explainer/05-data-flow.md +5 -5
  73. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +1 -4
  74. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +15 -14
  75. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +8 -8
  76. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +3 -3
  77. package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +10 -8
  78. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
  79. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +1 -4
  80. package/docs-release/guides/agent-installation.en-US.md +14 -8
  81. package/docs-release/guides/agent-installation.md +14 -8
  82. package/docs-release/guides/contributing.md +3 -3
  83. package/docs-release/guides/contributing.zh-CN.md +3 -3
  84. package/docs-release/guides/document-audience-and-surfaces.en-US.md +10 -10
  85. package/docs-release/guides/document-audience-and-surfaces.md +10 -10
  86. package/docs-release/guides/legacy-migration-agent-prompt.md +25 -2
  87. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +25 -2
  88. package/docs-release/guides/migration-playbook.en-US.md +63 -1
  89. package/docs-release/guides/migration-playbook.md +59 -1
  90. package/docs-release/guides/parent-control-repository-pattern.en-US.md +25 -25
  91. package/docs-release/guides/parent-control-repository-pattern.md +25 -25
  92. package/docs-release/guides/preset-development.md +2 -2
  93. package/docs-release/guides/repository-operating-models.en-US.md +21 -21
  94. package/docs-release/guides/repository-operating-models.md +21 -21
  95. package/docs-release/guides/task-state-machine.en-US.md +5 -5
  96. package/docs-release/guides/task-state-machine.md +5 -5
  97. package/docs-release/guides/typescript-runtime-migration-closeout.md +96 -0
  98. package/examples/minimal-project/AGENTS.md +2 -2
  99. package/examples/minimal-project/coding-agent-harness/harness.yaml +14 -0
  100. package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/progress.md +11 -0
  101. package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/review.md +1 -1
  102. package/package.json +20 -12
  103. package/presets/legacy-migration/preset.yaml +5 -5
  104. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  105. package/presets/lesson-sedimentation/preset.yaml +3 -3
  106. package/presets/module/preset.yaml +2 -2
  107. package/presets/module/templates/execution_strategy.append.md +1 -1
  108. package/presets/module/templates/task_plan.append.md +3 -3
  109. package/presets/standard-task/preset.yaml +2 -2
  110. package/references/adversarial-review-standard.md +2 -2
  111. package/references/agents-md-pattern.md +14 -14
  112. package/references/cadence-ledger.md +1 -1
  113. package/references/ci-cd-standard.md +1 -1
  114. package/references/delivery-operating-model-standard.md +4 -4
  115. package/references/docs-directory-standard.md +65 -159
  116. package/references/external-source-intake-standard.md +10 -10
  117. package/references/harness-ledger.md +5 -5
  118. package/references/legacy-12-phase-bootstrap.md +2 -2
  119. package/references/lessons-governance.md +15 -15
  120. package/references/long-running-task-standard.md +6 -6
  121. package/references/module-parallel-standard.md +34 -34
  122. package/references/planning-loop.md +6 -6
  123. package/references/project-onboarding-audit.md +4 -4
  124. package/references/regression-system.md +2 -2
  125. package/references/repo-governance-standard.md +4 -4
  126. package/references/review-routing-standard.md +1 -1
  127. package/references/ssot-governance.md +19 -19
  128. package/references/taskr-gap-analysis.md +5 -5
  129. package/references/walkthrough-closeout.md +14 -14
  130. package/references/worktree-parallel.md +3 -3
  131. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +1 -1
  132. package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
  133. package/templates/AGENTS.md.template +26 -26
  134. package/templates/architecture/README.md +4 -4
  135. package/templates/architecture/service-catalog.md +2 -2
  136. package/templates/architecture/services/service-template.md +1 -1
  137. package/templates/dashboard/assets/app-src/20-overview.js +11 -5
  138. package/templates/dashboard/assets/app-src/40-modules.js +1 -1
  139. package/templates/dashboard/assets/app.js +12 -6
  140. package/templates/dashboard/assets/i18n.js +4 -2
  141. package/templates/development/README.md +10 -10
  142. package/templates/development/cross-repo-debugging.md +3 -3
  143. package/templates/development/external-context/service-template.md +2 -2
  144. package/templates/development/external-source-packs/README.md +4 -4
  145. package/templates/integrations/README.md +4 -4
  146. package/templates/integrations/api-contract.md +2 -2
  147. package/templates/integrations/event-contract.md +2 -2
  148. package/templates/integrations/third-party/vendor-template.md +2 -2
  149. package/templates/integrations/webhook-contract.md +2 -2
  150. package/templates/ledger/Harness-Ledger.md +1 -1
  151. package/templates/planning/INDEX.md +1 -0
  152. package/templates/planning/module_session_prompt.md +1 -1
  153. package/templates/planning/task_plan.md +1 -1
  154. package/templates/planning/walkthrough.md +47 -0
  155. package/templates/reference/docs-library-standard.md +8 -8
  156. package/templates/reference/external-source-intake-standard.md +15 -15
  157. package/templates/reference/repo-governance-standard.md +1 -1
  158. package/templates/ssot/Module-Registry.md +1 -1
  159. package/templates/walkthrough/walkthrough-template.md +2 -2
  160. package/templates-zh-CN/AGENTS.md.template +26 -26
  161. package/templates-zh-CN/CLAUDE.md.template +1 -1
  162. package/templates-zh-CN/architecture/README.md +4 -4
  163. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  164. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  165. package/templates-zh-CN/development/README.md +10 -10
  166. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  167. package/templates-zh-CN/development/external-context/service-template.md +2 -2
  168. package/templates-zh-CN/development/external-source-packs/README.md +4 -4
  169. package/templates-zh-CN/integrations/README.md +4 -4
  170. package/templates-zh-CN/integrations/api-contract.md +2 -2
  171. package/templates-zh-CN/integrations/event-contract.md +2 -2
  172. package/templates-zh-CN/integrations/third-party/vendor-template.md +2 -2
  173. package/templates-zh-CN/integrations/webhook-contract.md +2 -2
  174. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  175. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  176. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  177. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  178. package/templates-zh-CN/planning/module_session_prompt.md +11 -11
  179. package/templates-zh-CN/planning/walkthrough.md +47 -0
  180. package/templates-zh-CN/reference/adversarial-review-standard.md +2 -2
  181. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  182. package/templates-zh-CN/reference/docs-library-standard.md +28 -28
  183. package/templates-zh-CN/reference/execution-workflow-standard.md +1 -1
  184. package/templates-zh-CN/reference/external-source-intake-standard.md +16 -16
  185. package/templates-zh-CN/reference/harness-ledger-standard.md +6 -6
  186. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  187. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  188. package/templates-zh-CN/reference/review-routing-standard.md +1 -1
  189. package/templates-zh-CN/reference/walkthrough-standard.md +7 -7
  190. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  191. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  192. package/templates-zh-CN/ssot/Delivery-SSoT.md +3 -3
  193. package/templates-zh-CN/ssot/Module-Registry.md +3 -3
  194. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  195. package/templates-zh-CN/walkthrough/walkthrough-template.md +5 -5
  196. package/tsconfig.dist.json +16 -0
  197. package/tsconfig.json +25 -0
  198. package/tsconfig.runtime.json +24 -0
  199. package/examples/minimal-project/.harness-capabilities.json +0 -8
  200. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +0 -11
  201. package/scripts/check-harness.mjs +0 -508
  202. package/scripts/commands/dashboard-command.mjs +0 -67
  203. package/scripts/commands/migration-command.mjs +0 -126
  204. package/scripts/commands/preset-command.mjs +0 -73
  205. package/scripts/commands/task-command.mjs +0 -328
  206. package/scripts/harness.mjs +0 -291
  207. package/scripts/lib/capability-registry.mjs +0 -587
  208. package/scripts/lib/check-module-parallel.mjs +0 -230
  209. package/scripts/lib/check-profiles.mjs +0 -372
  210. package/scripts/lib/check-task-contracts.mjs +0 -55
  211. package/scripts/lib/core-shared.mjs +0 -249
  212. package/scripts/lib/dashboard-data.mjs +0 -520
  213. package/scripts/lib/dashboard-workbench.mjs +0 -336
  214. package/scripts/lib/dashboard-writer.mjs +0 -202
  215. package/scripts/lib/git-status-summary.mjs +0 -46
  216. package/scripts/lib/governance-index-generator.mjs +0 -174
  217. package/scripts/lib/governance-sync.mjs +0 -611
  218. package/scripts/lib/governance-table-boundary.mjs +0 -175
  219. package/scripts/lib/lesson-maintenance.mjs +0 -152
  220. package/scripts/lib/markdown-utils.mjs +0 -191
  221. package/scripts/lib/migration-planner.mjs +0 -476
  222. package/scripts/lib/migration-support.mjs +0 -312
  223. package/scripts/lib/phase-kind.mjs +0 -50
  224. package/scripts/lib/preset-audit-contracts.mjs +0 -37
  225. package/scripts/lib/preset-engine.mjs +0 -494
  226. package/scripts/lib/preset-registry.mjs +0 -776
  227. package/scripts/lib/preset-resource-contracts.mjs +0 -83
  228. package/scripts/lib/review-confirm-git-gate.mjs +0 -248
  229. package/scripts/lib/status-builder.mjs +0 -88
  230. package/scripts/lib/subagent-authorization-audit.mjs +0 -196
  231. package/scripts/lib/task-audit-metadata.mjs +0 -385
  232. package/scripts/lib/task-audit-migration.mjs +0 -350
  233. package/scripts/lib/task-completion-consistency.mjs +0 -26
  234. package/scripts/lib/task-index.mjs +0 -93
  235. package/scripts/lib/task-lesson-candidates.mjs +0 -242
  236. package/scripts/lib/task-lesson-sedimentation.mjs +0 -326
  237. package/scripts/lib/task-lifecycle/create-task-helpers.mjs +0 -67
  238. package/scripts/lib/task-lifecycle/phase-sync.mjs +0 -88
  239. package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -112
  240. package/scripts/lib/task-lifecycle/review-gates.mjs +0 -73
  241. package/scripts/lib/task-lifecycle/review-submission.mjs +0 -63
  242. package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +0 -49
  243. package/scripts/lib/task-lifecycle/template-files.mjs +0 -53
  244. package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
  245. package/scripts/lib/task-lifecycle.mjs +0 -616
  246. package/scripts/lib/task-metadata.mjs +0 -118
  247. package/scripts/lib/task-review-model.mjs +0 -455
  248. package/scripts/lib/task-scanner.mjs +0 -503
  249. package/scripts/lib/task-tombstone-commands.mjs +0 -140
  250. package/scripts/postinstall.mjs +0 -14
  251. package/templates/walkthrough/Closeout-SSoT.md +0 -43
  252. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +0 -42
  253. /package/examples/minimal-project/{docs → coding-agent-harness/governance/generated}/Harness-Ledger.md +0 -0
  254. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/INDEX.md +0 -0
  255. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/brief.md +0 -0
  256. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/execution_strategy.md +0 -0
  257. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/findings.md +0 -0
  258. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/lesson_candidates.md +0 -0
  259. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/task_plan.md +0 -0
  260. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/visual_map.md +0 -0
@@ -0,0 +1,304 @@
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 { spawnSync } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
8
+ const defaultRepoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ const typescriptVersion = "5.9.3";
10
+ export function checkRuntimeEmitContract({ projectRoot = defaultRepoRoot, configPath, expectedDir, outDir, } = {}) {
11
+ const violations = [];
12
+ const usesDefaultConfig = configPath === undefined;
13
+ const resolvedConfigPath = configPath || path.join(projectRoot, "tsconfig.dist.json");
14
+ const absoluteProjectRoot = path.resolve(projectRoot);
15
+ const absoluteConfig = path.resolve(resolvedConfigPath);
16
+ const absoluteOutDir = outDir ? path.resolve(outDir) : fs.mkdtempSync(path.join(os.tmpdir(), "harness-runtime-emit-out-"));
17
+ const absoluteExpectedDir = expectedDir ? path.resolve(expectedDir) : usesDefaultConfig ? path.join(absoluteProjectRoot, "dist") : undefined;
18
+ const sourceFiles = collectSourceFiles(absoluteProjectRoot, { roots: absoluteExpectedDir && !usesDefaultConfig ? undefined : ["scripts"] });
19
+ if (!fs.existsSync(absoluteConfig)) {
20
+ return {
21
+ ok: false,
22
+ violations: [{ code: "missing-config", message: `runtime emit config not found: ${absoluteConfig}` }],
23
+ };
24
+ }
25
+ if (absoluteExpectedDir && !fs.existsSync(absoluteExpectedDir)) {
26
+ violations.push({
27
+ code: "missing-expected-dir",
28
+ message: `runtime emit expected directory not found: ${absoluteExpectedDir}`,
29
+ });
30
+ }
31
+ for (const source of sourceFiles) {
32
+ const content = fs.readFileSync(source, "utf8");
33
+ for (const specifier of parseLocalImportSpecifiers(content)) {
34
+ if (/\.(ts|mts)$/.test(specifier)) {
35
+ violations.push({
36
+ code: "typescript-source-import",
37
+ file: toPosix(path.relative(absoluteProjectRoot, source)),
38
+ specifier,
39
+ message: `${toPosix(path.relative(absoluteProjectRoot, source))} imports TypeScript source specifier ${specifier}`,
40
+ });
41
+ }
42
+ }
43
+ }
44
+ if (sourceFiles.length === 0 && !absoluteExpectedDir) {
45
+ return {
46
+ ok: violations.length === 0,
47
+ violations,
48
+ outDir: absoluteOutDir,
49
+ expectedDir: absoluteExpectedDir,
50
+ skippedEmit: true,
51
+ sourceFiles: 0,
52
+ };
53
+ }
54
+ const emit = runTypeScriptEmit({ projectRoot: absoluteProjectRoot, configPath: absoluteConfig, outDir: absoluteOutDir });
55
+ if (emit.status !== 0) {
56
+ violations.push({
57
+ code: "emit-failed",
58
+ message: `TypeScript runtime emit failed\nSTDOUT:\n${emit.stdout}\nSTDERR:\n${emit.stderr}`,
59
+ });
60
+ }
61
+ if (absoluteExpectedDir && fs.existsSync(absoluteExpectedDir) && emit.status === 0) {
62
+ compareDirectories({
63
+ expectedDir: absoluteExpectedDir,
64
+ actualDir: absoluteOutDir,
65
+ violations,
66
+ });
67
+ }
68
+ return {
69
+ ok: violations.length === 0,
70
+ violations,
71
+ outDir: absoluteOutDir,
72
+ expectedDir: absoluteExpectedDir,
73
+ skippedEmit: false,
74
+ sourceFiles: sourceFiles.length,
75
+ };
76
+ }
77
+ function runTypeScriptEmit({ projectRoot, configPath, outDir }) {
78
+ return spawnSync("npm", ["exec", "--yes", "--package", `typescript@${typescriptVersion}`, "--", "tsc", "-p", configPath, "--outDir", outDir, "--noCheck"], {
79
+ cwd: projectRoot,
80
+ encoding: "utf8",
81
+ });
82
+ }
83
+ function compareDirectories({ expectedDir, actualDir, violations }) {
84
+ const expectedFiles = collectFiles(expectedDir).filter((file) => file.endsWith(".mjs")).sort();
85
+ const actualFiles = collectFiles(actualDir).filter((file) => file.endsWith(".mjs")).sort();
86
+ const expectedRelatives = expectedFiles.map((file) => toPosix(path.relative(expectedDir, file)));
87
+ const actualRelatives = actualFiles.map((file) => toPosix(path.relative(actualDir, file)));
88
+ for (const relative of expectedRelatives) {
89
+ if (!actualRelatives.includes(relative)) {
90
+ violations.push({
91
+ code: "missing-emitted-file",
92
+ file: relative,
93
+ message: `expected emitted file missing: ${relative}`,
94
+ });
95
+ }
96
+ }
97
+ for (const relative of actualRelatives) {
98
+ if (!expectedRelatives.includes(relative)) {
99
+ violations.push({
100
+ code: "unexpected-emitted-file",
101
+ file: relative,
102
+ message: `unexpected emitted file: ${relative}`,
103
+ });
104
+ continue;
105
+ }
106
+ const expected = fs.readFileSync(path.join(expectedDir, relative), "utf8");
107
+ const actual = fs.readFileSync(path.join(actualDir, relative), "utf8");
108
+ if (expected !== actual) {
109
+ violations.push({
110
+ code: "emit-drift",
111
+ file: relative,
112
+ message: `emitted .mjs drift detected: ${relative}`,
113
+ });
114
+ }
115
+ }
116
+ }
117
+ function collectSourceFiles(projectRoot, { roots } = {}) {
118
+ const files = [];
119
+ const sourceRoots = roots?.length ? roots.map((root) => path.join(projectRoot, root)) : [projectRoot];
120
+ for (const sourceRoot of sourceRoots) {
121
+ if (fs.existsSync(sourceRoot))
122
+ walk(sourceRoot, files, (file) => file.endsWith(".mts"), projectRoot);
123
+ }
124
+ return files.sort();
125
+ }
126
+ function collectFiles(directory) {
127
+ const files = [];
128
+ if (fs.existsSync(directory))
129
+ walk(directory, files, () => true);
130
+ return files.sort();
131
+ }
132
+ function walk(current, files, predicate, sourceRoot) {
133
+ const stat = fs.lstatSync(current);
134
+ if (stat.isSymbolicLink())
135
+ return;
136
+ if (stat.isDirectory()) {
137
+ const name = path.basename(current);
138
+ const topLevel = sourceRoot ? path.relative(sourceRoot, current).split(path.sep)[0] : "";
139
+ if (name === "node_modules" || name === ".git" || name === ".worktrees" || name === "tmp" || topLevel === "fixtures")
140
+ return;
141
+ for (const entry of fs.readdirSync(current))
142
+ walk(path.join(current, entry), files, predicate, sourceRoot);
143
+ return;
144
+ }
145
+ if (stat.isFile() && predicate(current))
146
+ files.push(current);
147
+ }
148
+ function parseLocalImportSpecifiers(content) {
149
+ const specifiers = [];
150
+ let index = 0;
151
+ while (index < content.length) {
152
+ const skipped = skipNonCode(content, index);
153
+ if (skipped !== index) {
154
+ index = skipped;
155
+ continue;
156
+ }
157
+ if (isKeywordAt(content, index, "import")) {
158
+ const afterKeyword = skipWhitespace(content, index + "import".length);
159
+ if (content[afterKeyword] === "(") {
160
+ const specifier = readFirstStringArgument(content, afterKeyword + 1);
161
+ if (isLocalSpecifier(specifier))
162
+ specifiers.push(specifier);
163
+ index = afterKeyword + 1;
164
+ continue;
165
+ }
166
+ const statement = content.slice(index, findStatementEnd(content, index));
167
+ const sideEffect = statement.match(/\bimport\s+["']([^"']+)["']/s);
168
+ const fromImport = statement.match(/\bfrom\s*["']([^"']+)["']/s);
169
+ const specifier = fromImport?.[1] || sideEffect?.[1];
170
+ if (isLocalSpecifier(specifier))
171
+ specifiers.push(specifier);
172
+ }
173
+ else if (isKeywordAt(content, index, "export")) {
174
+ const statement = content.slice(index, findStatementEnd(content, index));
175
+ const fromExport = statement.match(/\bfrom\s*["']([^"']+)["']/s);
176
+ if (isLocalSpecifier(fromExport?.[1]))
177
+ specifiers.push(fromExport[1]);
178
+ }
179
+ index += 1;
180
+ }
181
+ return specifiers;
182
+ }
183
+ function isLocalSpecifier(specifier) {
184
+ return typeof specifier === "string" && (specifier.startsWith("./") || specifier.startsWith("../"));
185
+ }
186
+ function skipNonCode(content, index) {
187
+ const char = content[index];
188
+ const next = content[index + 1];
189
+ if (char === "/" && next === "/") {
190
+ const lineEnd = content.indexOf("\n", index + 2);
191
+ return lineEnd === -1 ? content.length : lineEnd + 1;
192
+ }
193
+ if (char === "/" && next === "*") {
194
+ const commentEnd = content.indexOf("*/", index + 2);
195
+ return commentEnd === -1 ? content.length : commentEnd + 2;
196
+ }
197
+ if (char === "'" || char === '"' || char === "`")
198
+ return skipString(content, index, char);
199
+ return index;
200
+ }
201
+ function skipString(content, index, quote) {
202
+ let cursor = index + 1;
203
+ while (cursor < content.length) {
204
+ if (content[cursor] === "\\") {
205
+ cursor += 2;
206
+ continue;
207
+ }
208
+ if (content[cursor] === quote)
209
+ return cursor + 1;
210
+ cursor += 1;
211
+ }
212
+ return content.length;
213
+ }
214
+ function isKeywordAt(content, index, keyword) {
215
+ if (!content.startsWith(keyword, index))
216
+ return false;
217
+ const before = content[index - 1];
218
+ const after = content[index + keyword.length];
219
+ return !isIdentifierChar(before) && !isIdentifierChar(after);
220
+ }
221
+ function isIdentifierChar(char) {
222
+ return typeof char === "string" && /[A-Za-z0-9_$]/.test(char);
223
+ }
224
+ function skipWhitespace(content, index) {
225
+ let cursor = index;
226
+ while (/\s/.test(content[cursor] || ""))
227
+ cursor += 1;
228
+ return cursor;
229
+ }
230
+ function findStatementEnd(content, index) {
231
+ let cursor = index;
232
+ while (cursor < content.length) {
233
+ const skipped = skipNonCode(content, cursor);
234
+ if (skipped !== cursor) {
235
+ cursor = skipped;
236
+ continue;
237
+ }
238
+ if (content[cursor] === ";")
239
+ return cursor + 1;
240
+ if (content[cursor] === "\n")
241
+ return cursor;
242
+ cursor += 1;
243
+ }
244
+ return content.length;
245
+ }
246
+ function readFirstStringArgument(content, index) {
247
+ const start = skipWhitespace(content, index);
248
+ const quote = content[start];
249
+ if (quote !== "'" && quote !== '"')
250
+ return undefined;
251
+ let cursor = start + 1;
252
+ let value = "";
253
+ while (cursor < content.length) {
254
+ const char = content[cursor];
255
+ if (char === "\\") {
256
+ value += content[cursor + 1] || "";
257
+ cursor += 2;
258
+ continue;
259
+ }
260
+ if (char === quote)
261
+ return value;
262
+ value += char;
263
+ cursor += 1;
264
+ }
265
+ return undefined;
266
+ }
267
+ function toPosix(value) {
268
+ return value.split(path.sep).join("/");
269
+ }
270
+ function parseArgs(argv) {
271
+ const args = {};
272
+ for (let index = 0; index < argv.length; index += 1) {
273
+ const arg = argv[index];
274
+ if (arg === "--project-root")
275
+ args.projectRoot = argv[++index];
276
+ else if (arg === "--config")
277
+ args.configPath = argv[++index];
278
+ else if (arg === "--expected-dir")
279
+ args.expectedDir = argv[++index];
280
+ else if (arg === "--out-dir")
281
+ args.outDir = argv[++index];
282
+ else
283
+ throw new Error(`Unknown argument: ${arg}`);
284
+ }
285
+ return args;
286
+ }
287
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
288
+ try {
289
+ const result = checkRuntimeEmitContract(parseArgs(process.argv.slice(2)));
290
+ if (!result.ok) {
291
+ console.error(result.violations.map((violation) => violation.message).join("\n"));
292
+ process.exit(1);
293
+ }
294
+ if (result.skippedEmit) {
295
+ console.log("Runtime emit contract passed (no runtime .mts sources)");
296
+ process.exit(0);
297
+ }
298
+ console.log("Runtime emit contract passed");
299
+ }
300
+ catch (error) {
301
+ console.error(error instanceof Error ? error.message : String(error));
302
+ process.exit(1);
303
+ }
304
+ }
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+ // @ts-nocheck
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ const defaultRepoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
+ const sourceRoots = ["scripts", "tests"];
8
+ const importPattern = /\b(import|export)\s+(type\s+)?(?:[^'"]*?\s+from\s+)?["']([^"']+)["']|\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
9
+ const tsEscapePattern = /@(ts-ignore|ts-expect-error)\b|(?:^|[^A-Za-z0-9_$])(?:as\s+any|:\s*any\b)/;
10
+ export function checkTypeBoundaries({ repoRoot = defaultRepoRoot } = {}) {
11
+ const files = collectSourceFiles(repoRoot);
12
+ const violations = [];
13
+ for (const file of files) {
14
+ const absolutePath = path.join(repoRoot, file);
15
+ const content = fs.readFileSync(absolutePath, "utf8");
16
+ const imports = parseImports(content);
17
+ if (file.endsWith(".ts")) {
18
+ const lines = content.split(/\r?\n/);
19
+ for (const [index, line] of lines.entries()) {
20
+ if (tsEscapePattern.test(line)) {
21
+ violations.push({
22
+ code: "ts-escape-hatch",
23
+ file,
24
+ line: index + 1,
25
+ message: `${file}:${index + 1} uses a TypeScript escape hatch that requires review`,
26
+ });
27
+ }
28
+ }
29
+ }
30
+ for (const imported of imports) {
31
+ if (!isLocalSpecifier(imported.specifier))
32
+ continue;
33
+ const target = resolveLocalSpecifier(repoRoot, file, imported.specifier);
34
+ if (file.endsWith(".mjs") && (hasTypeScriptSourceExtension(imported.specifier) || hasTypeScriptSourceExtension(target))) {
35
+ violations.push({
36
+ code: "mjs-imports-ts",
37
+ file,
38
+ specifier: imported.specifier,
39
+ message: `${file} imports TypeScript from runtime .mjs: ${imported.specifier}`,
40
+ });
41
+ }
42
+ if (target && isSharedTypesPath(target) && !isTypeOnlyTypeScriptImport(file, imported)) {
43
+ violations.push({
44
+ code: "types-value-import",
45
+ file,
46
+ specifier: imported.specifier,
47
+ message: `${file} value-imports shared type island: ${imported.specifier}`,
48
+ });
49
+ }
50
+ }
51
+ }
52
+ return { ok: violations.length === 0, violations };
53
+ }
54
+ function collectSourceFiles(repoRoot) {
55
+ const files = [];
56
+ for (const root of sourceRoots) {
57
+ const absoluteRoot = path.join(repoRoot, root);
58
+ if (!fs.existsSync(absoluteRoot))
59
+ continue;
60
+ walk(absoluteRoot, files, repoRoot);
61
+ }
62
+ return files.sort();
63
+ }
64
+ function walk(current, files, repoRoot) {
65
+ const stat = fs.lstatSync(current);
66
+ if (stat.isSymbolicLink())
67
+ return;
68
+ if (stat.isDirectory()) {
69
+ const name = path.basename(current);
70
+ if (name === "node_modules" || name === ".worktrees" || name === "tmp")
71
+ return;
72
+ for (const entry of fs.readdirSync(current))
73
+ walk(path.join(current, entry), files, repoRoot);
74
+ return;
75
+ }
76
+ if (stat.isFile() && /\.(mjs|mts|ts)$/.test(current)) {
77
+ files.push(path.relative(repoRoot, current).split(path.sep).join("/"));
78
+ }
79
+ }
80
+ function parseImports(content) {
81
+ const imports = [];
82
+ for (const match of content.matchAll(importPattern)) {
83
+ imports.push({
84
+ kind: match[1] || "import",
85
+ typeOnly: match[2] === "type ",
86
+ specifier: match[3] || match[4],
87
+ });
88
+ }
89
+ return imports;
90
+ }
91
+ function isLocalSpecifier(specifier) {
92
+ return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/");
93
+ }
94
+ function resolveLocalSpecifier(repoRoot, importer, specifier) {
95
+ const importerDir = path.dirname(path.join(repoRoot, importer));
96
+ const basePath = specifier.startsWith("/") ? path.join(repoRoot, specifier) : path.resolve(importerDir, specifier);
97
+ const candidates = candidatePaths(basePath);
98
+ for (const candidate of candidates) {
99
+ if (fs.existsSync(candidate))
100
+ return path.relative(repoRoot, candidate).split(path.sep).join("/");
101
+ }
102
+ const relative = path.relative(repoRoot, basePath).split(path.sep).join("/");
103
+ return relative.startsWith("..") ? undefined : relative;
104
+ }
105
+ function candidatePaths(basePath) {
106
+ const extension = path.extname(basePath);
107
+ if (extension) {
108
+ const paths = [basePath];
109
+ if (extension === ".js")
110
+ paths.push(basePath.slice(0, -3) + ".ts", basePath.slice(0, -3) + ".mts");
111
+ return paths;
112
+ }
113
+ return [
114
+ basePath,
115
+ `${basePath}.mjs`,
116
+ `${basePath}.mts`,
117
+ `${basePath}.ts`,
118
+ `${basePath}.js`,
119
+ path.join(basePath, "index.mjs"),
120
+ path.join(basePath, "index.ts"),
121
+ ];
122
+ }
123
+ function isSharedTypesPath(relativePath) {
124
+ return relativePath === "scripts/lib/types" || relativePath.startsWith("scripts/lib/types/");
125
+ }
126
+ function hasTypeScriptSourceExtension(filePath) {
127
+ return typeof filePath === "string" && /\.(mts|ts)$/.test(filePath);
128
+ }
129
+ function isTypeOnlyTypeScriptImport(file, imported) {
130
+ return file.endsWith(".ts") && imported.kind === "import" && imported.typeOnly;
131
+ }
132
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
133
+ const result = checkTypeBoundaries();
134
+ if (!result.ok) {
135
+ console.error(result.violations.map((violation) => violation.message).join("\n"));
136
+ process.exit(1);
137
+ }
138
+ console.log("Type boundary guards passed");
139
+ }
@@ -0,0 +1,80 @@
1
+ // @ts-nocheck
2
+ // Dashboard command parsing stays behavior-first until command handler types are modeled.
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { normalizeTarget, serveDashboardWorkbench, writeDashboardFolder, writeDashboardSingleFile, } from "../lib/harness-core.mjs";
6
+ import { dashboardWatchRoots } from "../lib/harness-paths.mjs";
7
+ export async function runDashboardCommand({ takeFlag, takeOption, targetArg }) {
8
+ const watch = takeFlag("--watch");
9
+ const workbench = takeFlag("--workbench");
10
+ const out = takeOption("--out", path.join("tmp", "harness-dashboard.html"));
11
+ const outDir = takeOption("--out-dir", "");
12
+ const host = takeOption("--host", "127.0.0.1");
13
+ const port = takeOption("--port", "0");
14
+ const localeOverride = takeOption("--locale", "");
15
+ const opts = localeOverride ? { localeOverride } : {};
16
+ if (workbench) {
17
+ if (!outDir) {
18
+ console.error("dashboard --workbench requires --out-dir so regenerated data has a stable folder");
19
+ process.exit(2);
20
+ }
21
+ try {
22
+ assertV2DashboardTarget(targetArg());
23
+ await serveDashboardWorkbench(outDir, targetArg(), { ...opts, host, port });
24
+ }
25
+ catch (error) {
26
+ console.error(error.message);
27
+ process.exit(1);
28
+ }
29
+ }
30
+ if (watch) {
31
+ if (!outDir) {
32
+ console.error("dashboard --watch requires --out-dir so updates are written to a stable folder");
33
+ process.exit(2);
34
+ }
35
+ const target = targetArg();
36
+ assertV2DashboardTarget(target);
37
+ const normalizedTarget = normalizeTarget(target);
38
+ const watchRoots = dashboardWatchRoots(normalizedTarget.harness);
39
+ const regenerate = () => {
40
+ try {
41
+ console.log(writeDashboardFolder(outDir, target, opts));
42
+ console.log(`dashboard regenerated: ${new Date().toISOString()}`);
43
+ }
44
+ catch (error) {
45
+ console.error(`dashboard regeneration failed: ${error.message}`);
46
+ }
47
+ };
48
+ regenerate();
49
+ let timer = null;
50
+ const watchers = watchRoots.map((watchRoot) => fs.watch(watchRoot, { recursive: true }, () => {
51
+ clearTimeout(timer);
52
+ timer = setTimeout(regenerate, 300);
53
+ }));
54
+ const close = () => {
55
+ for (const watcher of watchers)
56
+ watcher.close();
57
+ clearTimeout(timer);
58
+ process.exit(0);
59
+ };
60
+ process.on("SIGINT", close);
61
+ process.on("SIGTERM", close);
62
+ console.log(`watching ${watchRoots.join(", ")}`);
63
+ await new Promise(() => { });
64
+ }
65
+ assertV2DashboardTarget(targetArg());
66
+ if (outDir) {
67
+ console.log(writeDashboardFolder(outDir, targetArg(), opts));
68
+ }
69
+ else {
70
+ console.log(writeDashboardSingleFile(out, targetArg(), opts));
71
+ }
72
+ process.exit(0);
73
+ }
74
+ function assertV2DashboardTarget(target) {
75
+ const normalizedTarget = normalizeTarget(target);
76
+ if (normalizedTarget.harness?.version === 2)
77
+ return;
78
+ console.error("dashboard requires v2 harness structure; run `harness migrate-structure --plan` then `harness migrate-structure --apply`");
79
+ process.exit(1);
80
+ }
@@ -0,0 +1,152 @@
1
+ // @ts-nocheck
2
+ import { applyStructureMigration, buildMigrationPlan, planStructureMigration, runMigration, verifyMigrationSession, } from "../lib/harness-core.mjs";
3
+ import { applyTaskAuditIndexMigration, planTaskAuditIndexMigration, } from "../lib/task-audit-migration.mjs";
4
+ export function runMigrationCommand(command, { args, takeFlag, takeOption, targetArg }) {
5
+ if (command === "migrate-structure") {
6
+ const json = takeFlag("--json");
7
+ const apply = takeFlag("--apply");
8
+ const planOnly = takeFlag("--plan");
9
+ const force = takeFlag("--force");
10
+ try {
11
+ const result = apply && !planOnly
12
+ ? applyStructureMigration(targetArg(), { force })
13
+ : planStructureMigration(targetArg());
14
+ if (json) {
15
+ console.log(JSON.stringify(result, null, 2));
16
+ }
17
+ else {
18
+ console.log(`Structure migration ${result.applied ? "applied" : "plan"}: ${result.target}`);
19
+ console.log(`manifest: ${result.manifest}`);
20
+ console.log(`actions: ${result.summary.actions}`);
21
+ for (const action of result.actions || [])
22
+ console.log(`- ${action.action}: ${action.source || "(none)"} -> ${action.destination}`);
23
+ }
24
+ }
25
+ catch (error) {
26
+ console.error(error.message);
27
+ process.exit(1);
28
+ }
29
+ return;
30
+ }
31
+ if (command === "migrate-task-audit-index") {
32
+ const json = takeFlag("--json");
33
+ const apply = takeFlag("--apply");
34
+ const planOnly = takeFlag("--plan");
35
+ try {
36
+ const result = apply && !planOnly
37
+ ? applyTaskAuditIndexMigration(targetArg())
38
+ : planTaskAuditIndexMigration(targetArg());
39
+ if (json) {
40
+ console.log(JSON.stringify(result, null, 2));
41
+ }
42
+ else {
43
+ console.log(`Task audit INDEX migration ${result.result}: ${result.target}`);
44
+ console.log(`actions: ${result.summary.actions}`);
45
+ console.log(`legacy audit blocks: ${result.summary.legacyAuditBlocks}`);
46
+ console.log(`failures: ${result.summary.failures}`);
47
+ for (const action of result.actions || [])
48
+ console.log(`- ${action.taskId}: ${action.legacyBlocks.join(", ")}`);
49
+ for (const failure of result.failures || [])
50
+ console.error(`Failure: ${failure.taskId}: ${failure.failure}`);
51
+ }
52
+ process.exit(result.failures?.length ? 1 : 0);
53
+ }
54
+ catch (error) {
55
+ if (json && error.plan)
56
+ console.error(JSON.stringify(error.plan, null, 2));
57
+ else
58
+ console.error(error.message);
59
+ process.exit(1);
60
+ }
61
+ }
62
+ if (command === "migrate-plan") {
63
+ const json = takeFlag("--json");
64
+ const limit = Number.parseInt(takeOption("--limit", "20"), 10) || 20;
65
+ try {
66
+ const plan = buildMigrationPlan(targetArg(), { limit });
67
+ if (json) {
68
+ console.log(JSON.stringify(plan, null, 2));
69
+ }
70
+ else {
71
+ console.log(`Migration Plan: ${plan.target}`);
72
+ console.log(`mode: ${plan.mode}`);
73
+ console.log(`warnings: ${plan.summary.warnings}`);
74
+ console.log(`task actions: ${plan.summary.taskActions}`);
75
+ console.log(`visual map actions: ${plan.summary.visualMapActions}`);
76
+ console.log(`legacy visual-only tasks: ${plan.summary.legacyVisualOnly}`);
77
+ console.log(`weak briefs: ${plan.summary.weakBrief}`);
78
+ console.log(`unknown classifications: ${plan.summary.unknownClassification}`);
79
+ console.log(`full cutover eligible: ${plan.summary.fullCutoverEligible ? "yes" : "no"}`);
80
+ console.log(`review actions: ${plan.summary.reviewSchemaGaps}`);
81
+ console.log(`legacy actions: ${plan.summary.legacyReferenceGaps}`);
82
+ console.log(`legacy residuals: ${plan.summary.legacyResiduals}`);
83
+ console.log(`recommended capabilities: ${plan.summary.recommendedCapabilities.join(", ") || "none"}`);
84
+ console.log("\nPhases:");
85
+ for (const phase of plan.phases)
86
+ console.log(`- ${phase.id}: ${phase.title}`);
87
+ console.log("\nTop task actions:");
88
+ for (const action of plan.taskActions)
89
+ console.log(`- ${action.taskId}: add ${action.files.join(", ")}`);
90
+ console.log("\nTop review actions:");
91
+ for (const action of plan.reviewActions)
92
+ console.log(`- ${action.path}: add ${action.missing.join(", ")}`);
93
+ console.log("\nTop legacy residuals:");
94
+ for (const action of plan.legacyResiduals)
95
+ console.log(`- ${action.taskId}: ${action.missing} (${action.reason})`);
96
+ console.log("\nNext commands:");
97
+ for (const next of plan.nextCommands)
98
+ console.log(`- ${next}`);
99
+ }
100
+ }
101
+ catch (error) {
102
+ console.error(error.message);
103
+ process.exit(1);
104
+ }
105
+ return;
106
+ }
107
+ if (command === "migrate-run") {
108
+ const locale = takeOption("--locale", "");
109
+ const assumeLocale = takeFlag("--assume-locale");
110
+ const allowDirty = takeFlag("--allow-dirty");
111
+ const planOnly = takeFlag("--plan-only");
112
+ const outDir = takeOption("--out-dir", "");
113
+ const sessionDir = takeOption("--session-dir", "");
114
+ try {
115
+ console.log(JSON.stringify(runMigration(targetArg(), {
116
+ locale,
117
+ assumeLocale,
118
+ allowDirty,
119
+ planOnly,
120
+ outDir,
121
+ sessionDir,
122
+ }), null, 2));
123
+ }
124
+ catch (error) {
125
+ console.error(error.message);
126
+ process.exit(1);
127
+ }
128
+ return;
129
+ }
130
+ if (command === "migrate-verify") {
131
+ const json = takeFlag("--json");
132
+ const fullCutover = takeFlag("--full-cutover");
133
+ const sessionPath = args.shift();
134
+ if (!sessionPath) {
135
+ console.error("Missing session.json path");
136
+ process.exit(2);
137
+ }
138
+ const result = verifyMigrationSession(sessionPath, { fullCutover });
139
+ if (json) {
140
+ console.log(JSON.stringify(result, null, 2));
141
+ }
142
+ else {
143
+ for (const failure of result.failures)
144
+ console.error(`Failure: ${failure}`);
145
+ for (const warning of result.warnings)
146
+ console.log(`Warning: ${warning}`);
147
+ console.log(`Migration verify ${result.status}: ${result.sessionPath}`);
148
+ }
149
+ process.exit(result.status === "pass" ? 0 : 1);
150
+ }
151
+ throw new Error(`Unsupported migration command: ${command}`);
152
+ }