coding-agent-harness 1.0.4 → 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 (279) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/CONTRIBUTING.md +2 -2
  3. package/LICENSE +661 -21
  4. package/LICENSE-EXCEPTION.md +37 -0
  5. package/README.md +96 -4
  6. package/README.zh-CN.md +75 -4
  7. package/SKILL.md +52 -51
  8. package/dist/build-dist.mjs +189 -0
  9. package/dist/check-dist-observation.mjs +428 -0
  10. package/dist/check-harness.mjs +489 -0
  11. package/dist/check-import-graph.mjs +511 -0
  12. package/dist/check-runtime-emit.mjs +304 -0
  13. package/dist/check-type-boundaries.mjs +139 -0
  14. package/dist/commands/dashboard-command.mjs +80 -0
  15. package/dist/commands/migration-command.mjs +152 -0
  16. package/dist/commands/preset-command.mjs +91 -0
  17. package/dist/commands/task-command.mjs +324 -0
  18. package/dist/harness.mjs +304 -0
  19. package/dist/lib/capability-registry.mjs +643 -0
  20. package/dist/lib/check-module-parallel.mjs +227 -0
  21. package/dist/lib/check-profiles.mjs +414 -0
  22. package/dist/lib/check-task-contracts.mjs +54 -0
  23. package/dist/lib/core-shared.mjs +254 -0
  24. package/dist/lib/dashboard-data.mjs +608 -0
  25. package/dist/lib/dashboard-workbench.mjs +334 -0
  26. package/dist/lib/dashboard-writer.mjs +200 -0
  27. package/dist/lib/git-status-summary.mjs +45 -0
  28. package/dist/lib/governance-index-generator.mjs +236 -0
  29. package/dist/lib/governance-sync.mjs +617 -0
  30. package/dist/lib/governance-table-boundary.mjs +161 -0
  31. package/{scripts → dist}/lib/harness-core.mjs +3 -0
  32. package/dist/lib/harness-paths.mjs +338 -0
  33. package/dist/lib/lesson-maintenance.mjs +139 -0
  34. package/dist/lib/markdown-utils.mjs +193 -0
  35. package/dist/lib/migration-planner.mjs +439 -0
  36. package/dist/lib/migration-support.mjs +317 -0
  37. package/dist/lib/phase-kind.mjs +46 -0
  38. package/dist/lib/preset-audit-contracts.mjs +40 -0
  39. package/dist/lib/preset-engine.mjs +516 -0
  40. package/dist/lib/preset-registry.mjs +831 -0
  41. package/dist/lib/preset-resource-contracts.mjs +83 -0
  42. package/dist/lib/review-confirm-git-gate.mjs +244 -0
  43. package/dist/lib/status-builder.mjs +87 -0
  44. package/{scripts → dist}/lib/status-dashboard-renderer.mjs +48 -47
  45. package/dist/lib/structure-migration.mjs +404 -0
  46. package/dist/lib/subagent-authorization-audit.mjs +198 -0
  47. package/dist/lib/task-audit-metadata.mjs +376 -0
  48. package/dist/lib/task-audit-migration.mjs +355 -0
  49. package/dist/lib/task-completion-consistency.mjs +29 -0
  50. package/dist/lib/task-index.mjs +133 -0
  51. package/dist/lib/task-lesson-candidates.mjs +239 -0
  52. package/dist/lib/task-lesson-sedimentation.mjs +300 -0
  53. package/dist/lib/task-lifecycle/create-task-helpers.mjs +84 -0
  54. package/dist/lib/task-lifecycle/phase-sync.mjs +82 -0
  55. package/dist/lib/task-lifecycle/review-confirm.mjs +93 -0
  56. package/dist/lib/task-lifecycle/review-gates.mjs +62 -0
  57. package/dist/lib/task-lifecycle/review-submission.mjs +52 -0
  58. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +54 -0
  59. package/dist/lib/task-lifecycle/template-files.mjs +52 -0
  60. package/dist/lib/task-lifecycle/text-utils.mjs +26 -0
  61. package/dist/lib/task-lifecycle.mjs +611 -0
  62. package/dist/lib/task-metadata.mjs +116 -0
  63. package/dist/lib/task-review-model.mjs +474 -0
  64. package/dist/lib/task-scanner.mjs +439 -0
  65. package/dist/lib/task-tombstone-commands.mjs +125 -0
  66. package/dist/postinstall.mjs +14 -0
  67. package/dist/run-built-tests.mjs +84 -0
  68. package/docs-release/README.md +1 -0
  69. package/docs-release/architecture/overview.md +13 -13
  70. package/docs-release/architecture/overview.zh-CN.md +13 -13
  71. package/docs-release/architecture/system-explainer/01-system-overview.md +218 -0
  72. package/docs-release/architecture/system-explainer/02-module-dependency.md +257 -0
  73. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +304 -0
  74. package/docs-release/architecture/system-explainer/04-check-and-governance.md +241 -0
  75. package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
  76. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +300 -0
  77. package/docs-release/architecture/system-explainer/README.md +67 -0
  78. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +227 -0
  79. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +263 -0
  80. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +319 -0
  81. package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +252 -0
  82. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +290 -0
  83. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +320 -0
  84. package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
  85. package/docs-release/guides/agent-installation.en-US.md +22 -15
  86. package/docs-release/guides/agent-installation.md +23 -15
  87. package/docs-release/guides/contributing.md +3 -3
  88. package/docs-release/guides/contributing.zh-CN.md +3 -3
  89. package/docs-release/guides/document-audience-and-surfaces.en-US.md +10 -10
  90. package/docs-release/guides/document-audience-and-surfaces.md +10 -10
  91. package/docs-release/guides/legacy-migration-agent-prompt.md +25 -2
  92. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +25 -2
  93. package/docs-release/guides/migration-playbook.en-US.md +63 -1
  94. package/docs-release/guides/migration-playbook.md +59 -1
  95. package/docs-release/guides/parent-control-repository-pattern.en-US.md +25 -25
  96. package/docs-release/guides/parent-control-repository-pattern.md +25 -25
  97. package/docs-release/guides/preset-development.md +28 -4
  98. package/docs-release/guides/repository-operating-models.en-US.md +21 -21
  99. package/docs-release/guides/repository-operating-models.md +21 -21
  100. package/docs-release/guides/task-state-machine.en-US.md +35 -18
  101. package/docs-release/guides/task-state-machine.md +35 -18
  102. package/docs-release/guides/typescript-runtime-migration-closeout.md +96 -0
  103. package/examples/minimal-project/AGENTS.md +2 -2
  104. package/examples/minimal-project/coding-agent-harness/harness.yaml +14 -0
  105. package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/INDEX.md +60 -0
  106. package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/progress.md +11 -0
  107. package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/review.md +1 -1
  108. package/package.json +22 -13
  109. package/presets/legacy-migration/preset.yaml +5 -5
  110. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  111. package/presets/lesson-sedimentation/preset.yaml +3 -3
  112. package/presets/module/preset.yaml +2 -2
  113. package/presets/module/templates/execution_strategy.append.md +1 -1
  114. package/presets/module/templates/task_plan.append.md +3 -3
  115. package/presets/standard-task/preset.yaml +2 -2
  116. package/references/adversarial-review-standard.md +2 -2
  117. package/references/agents-md-pattern.md +14 -14
  118. package/references/cadence-ledger.md +1 -1
  119. package/references/ci-cd-standard.md +1 -1
  120. package/references/delivery-operating-model-standard.md +4 -4
  121. package/references/docs-directory-standard.md +65 -159
  122. package/references/external-source-intake-standard.md +10 -10
  123. package/references/harness-ledger.md +6 -6
  124. package/references/legacy-12-phase-bootstrap.md +2 -2
  125. package/references/lessons-governance.md +15 -15
  126. package/references/long-running-task-standard.md +6 -6
  127. package/references/module-parallel-standard.md +34 -34
  128. package/references/planning-loop.md +6 -6
  129. package/references/project-onboarding-audit.md +4 -4
  130. package/references/regression-system.md +2 -2
  131. package/references/repo-governance-standard.md +4 -4
  132. package/references/review-routing-standard.md +1 -1
  133. package/references/ssot-governance.md +19 -19
  134. package/references/taskr-gap-analysis.md +5 -5
  135. package/references/walkthrough-closeout.md +14 -14
  136. package/references/worktree-parallel.md +3 -3
  137. package/skills/preset-creator/references/complex-task-skeleton/brief.md +11 -0
  138. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +1 -1
  139. package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
  140. package/templates/AGENTS.md.template +31 -29
  141. package/templates/architecture/README.md +4 -4
  142. package/templates/architecture/service-catalog.md +2 -2
  143. package/templates/architecture/services/service-template.md +1 -1
  144. package/templates/dashboard/assets/app-src/00-state.js +12 -0
  145. package/templates/dashboard/assets/app-src/10-router.js +3 -0
  146. package/templates/dashboard/assets/app-src/20-overview.js +13 -3
  147. package/templates/dashboard/assets/app-src/35-task-detail.js +46 -6
  148. package/templates/dashboard/assets/app-src/40-modules.js +1 -1
  149. package/templates/dashboard/assets/app-src/55-presets.js +375 -0
  150. package/templates/dashboard/assets/app-src/60-shared.js +3 -1
  151. package/templates/dashboard/assets/app-src/90-bindings.js +131 -0
  152. package/templates/dashboard/assets/app.css +583 -0
  153. package/templates/dashboard/assets/app.css.manifest.json +1 -0
  154. package/templates/dashboard/assets/app.js +585 -11
  155. package/templates/dashboard/assets/app.manifest.json +1 -0
  156. package/templates/dashboard/assets/css-src/00-foundation.css +4 -0
  157. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +62 -0
  158. package/templates/dashboard/assets/css-src/45-presets.css +516 -0
  159. package/templates/dashboard/assets/i18n.js +144 -4
  160. package/templates/development/README.md +10 -10
  161. package/templates/development/cross-repo-debugging.md +3 -3
  162. package/templates/development/external-context/service-template.md +2 -2
  163. package/templates/development/external-source-packs/README.md +4 -4
  164. package/templates/integrations/README.md +4 -4
  165. package/templates/integrations/api-contract.md +2 -2
  166. package/templates/integrations/event-contract.md +2 -2
  167. package/templates/integrations/third-party/vendor-template.md +2 -2
  168. package/templates/integrations/webhook-contract.md +2 -2
  169. package/templates/ledger/Harness-Ledger.md +1 -1
  170. package/templates/planning/INDEX.md +88 -0
  171. package/templates/planning/brief.md +1 -1
  172. package/templates/planning/module_session_prompt.md +2 -1
  173. package/templates/planning/review.md +0 -18
  174. package/templates/planning/task_plan.md +5 -44
  175. package/templates/planning/visual_map.md +13 -9
  176. package/templates/planning/visual_map.simple.md +52 -0
  177. package/templates/planning/walkthrough.md +47 -0
  178. package/templates/reference/docs-library-standard.md +8 -8
  179. package/templates/reference/execution-workflow-standard.md +29 -2
  180. package/templates/reference/external-source-intake-standard.md +15 -15
  181. package/templates/reference/repo-governance-standard.md +1 -1
  182. package/templates/ssot/Module-Registry.md +1 -1
  183. package/templates/walkthrough/walkthrough-template.md +2 -2
  184. package/templates-zh-CN/AGENTS.md.template +31 -29
  185. package/templates-zh-CN/CLAUDE.md.template +1 -1
  186. package/templates-zh-CN/architecture/README.md +4 -4
  187. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  188. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  189. package/templates-zh-CN/development/README.md +10 -10
  190. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  191. package/templates-zh-CN/development/external-context/service-template.md +2 -2
  192. package/templates-zh-CN/development/external-source-packs/README.md +4 -4
  193. package/templates-zh-CN/integrations/README.md +4 -4
  194. package/templates-zh-CN/integrations/api-contract.md +2 -2
  195. package/templates-zh-CN/integrations/event-contract.md +2 -2
  196. package/templates-zh-CN/integrations/third-party/vendor-template.md +2 -2
  197. package/templates-zh-CN/integrations/webhook-contract.md +2 -2
  198. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  199. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  200. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  201. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  202. package/templates-zh-CN/planning/INDEX.md +87 -0
  203. package/templates-zh-CN/planning/brief.md +1 -1
  204. package/templates-zh-CN/planning/module_session_prompt.md +12 -11
  205. package/templates-zh-CN/planning/review.md +0 -18
  206. package/templates-zh-CN/planning/task_plan.md +3 -63
  207. package/templates-zh-CN/planning/visual_map.md +14 -7
  208. package/templates-zh-CN/planning/visual_map.simple.md +48 -0
  209. package/templates-zh-CN/planning/walkthrough.md +47 -0
  210. package/templates-zh-CN/reference/adversarial-review-standard.md +2 -2
  211. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  212. package/templates-zh-CN/reference/docs-library-standard.md +28 -28
  213. package/templates-zh-CN/reference/execution-workflow-standard.md +32 -7
  214. package/templates-zh-CN/reference/external-source-intake-standard.md +16 -16
  215. package/templates-zh-CN/reference/harness-ledger-standard.md +6 -6
  216. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  217. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  218. package/templates-zh-CN/reference/review-routing-standard.md +1 -1
  219. package/templates-zh-CN/reference/walkthrough-standard.md +7 -7
  220. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  221. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  222. package/templates-zh-CN/ssot/Delivery-SSoT.md +3 -3
  223. package/templates-zh-CN/ssot/Module-Registry.md +3 -3
  224. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  225. package/templates-zh-CN/walkthrough/walkthrough-template.md +5 -5
  226. package/tsconfig.dist.json +16 -0
  227. package/tsconfig.json +25 -0
  228. package/tsconfig.runtime.json +24 -0
  229. package/examples/minimal-project/.harness-capabilities.json +0 -8
  230. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +0 -11
  231. package/scripts/check-harness.mjs +0 -508
  232. package/scripts/commands/dashboard-command.mjs +0 -67
  233. package/scripts/commands/migration-command.mjs +0 -96
  234. package/scripts/commands/preset-command.mjs +0 -73
  235. package/scripts/commands/task-command.mjs +0 -327
  236. package/scripts/harness.mjs +0 -287
  237. package/scripts/lib/capability-registry.mjs +0 -591
  238. package/scripts/lib/check-module-parallel.mjs +0 -237
  239. package/scripts/lib/check-profiles.mjs +0 -418
  240. package/scripts/lib/check-task-contracts.mjs +0 -47
  241. package/scripts/lib/core-shared.mjs +0 -196
  242. package/scripts/lib/dashboard-data.mjs +0 -412
  243. package/scripts/lib/dashboard-workbench.mjs +0 -257
  244. package/scripts/lib/dashboard-writer.mjs +0 -198
  245. package/scripts/lib/git-status-summary.mjs +0 -46
  246. package/scripts/lib/governance-index-generator.mjs +0 -174
  247. package/scripts/lib/governance-sync.mjs +0 -514
  248. package/scripts/lib/governance-table-boundary.mjs +0 -175
  249. package/scripts/lib/lesson-maintenance.mjs +0 -152
  250. package/scripts/lib/markdown-utils.mjs +0 -158
  251. package/scripts/lib/migration-planner.mjs +0 -478
  252. package/scripts/lib/migration-support.mjs +0 -312
  253. package/scripts/lib/preset-audit-contracts.mjs +0 -37
  254. package/scripts/lib/preset-engine.mjs +0 -497
  255. package/scripts/lib/preset-registry.mjs +0 -627
  256. package/scripts/lib/preset-resource-contracts.mjs +0 -83
  257. package/scripts/lib/review-confirm-git-gate.mjs +0 -248
  258. package/scripts/lib/subagent-authorization-audit.mjs +0 -196
  259. package/scripts/lib/task-completion-consistency.mjs +0 -16
  260. package/scripts/lib/task-index.mjs +0 -93
  261. package/scripts/lib/task-lesson-candidates.mjs +0 -242
  262. package/scripts/lib/task-lesson-sedimentation.mjs +0 -326
  263. package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -101
  264. package/scripts/lib/task-lifecycle/review-gates.mjs +0 -70
  265. package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
  266. package/scripts/lib/task-lifecycle.mjs +0 -649
  267. package/scripts/lib/task-review-model.mjs +0 -469
  268. package/scripts/lib/task-scanner.mjs +0 -576
  269. package/scripts/lib/task-tombstone-commands.mjs +0 -140
  270. package/scripts/postinstall.mjs +0 -14
  271. package/templates/walkthrough/Closeout-SSoT.md +0 -43
  272. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +0 -42
  273. /package/examples/minimal-project/{docs → coding-agent-harness/governance/generated}/Harness-Ledger.md +0 -0
  274. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/brief.md +0 -0
  275. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/execution_strategy.md +0 -0
  276. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/findings.md +0 -0
  277. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/lesson_candidates.md +0 -0
  278. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/task_plan.md +0 -0
  279. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/visual_map.md +0 -0
@@ -0,0 +1,189 @@
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 repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ const typescriptVersion = "5.9.3";
10
+ export function buildRuntimeDist({ projectRoot = repoRoot, configPath = path.join(projectRoot, "tsconfig.dist.json"), outDir = path.join(projectRoot, "dist"), } = {}) {
11
+ const absoluteProjectRoot = path.resolve(projectRoot);
12
+ const absoluteConfig = path.resolve(configPath);
13
+ const absoluteOutDir = path.resolve(outDir);
14
+ const defaultDist = path.join(absoluteProjectRoot, "dist");
15
+ const buildOutDir = absoluteOutDir === defaultDist
16
+ ? fs.mkdtempSync(path.join(os.tmpdir(), "coding-agent-harness-dist-build-"))
17
+ : absoluteOutDir;
18
+ if (!fs.existsSync(absoluteConfig)) {
19
+ return {
20
+ ok: false,
21
+ error: `dist build config not found: ${absoluteConfig}`,
22
+ };
23
+ }
24
+ if (isDangerousOutDir({ projectRoot: absoluteProjectRoot, outDir: absoluteOutDir })) {
25
+ return {
26
+ ok: false,
27
+ error: `refusing to clean unsafe dist output directory: ${absoluteOutDir}`,
28
+ };
29
+ }
30
+ fs.rmSync(buildOutDir, { recursive: true, force: true });
31
+ const emit = spawnSync("npm", ["exec", "--yes", "--package", `typescript@${typescriptVersion}`, "--", "tsc", "-p", absoluteConfig, "--outDir", buildOutDir, "--noCheck"], {
32
+ cwd: absoluteProjectRoot,
33
+ encoding: "utf8",
34
+ });
35
+ if (emit.status !== 0) {
36
+ return {
37
+ ok: false,
38
+ error: `TypeScript dist build failed\nSTDOUT:\n${emit.stdout}\nSTDERR:\n${emit.stderr}`,
39
+ status: emit.status,
40
+ };
41
+ }
42
+ if (buildOutDir !== absoluteOutDir) {
43
+ syncDirectory(buildOutDir, absoluteOutDir);
44
+ fs.rmSync(buildOutDir, { recursive: true, force: true });
45
+ }
46
+ const files = collectFiles(absoluteOutDir).filter((file) => file.endsWith(".mjs")).sort();
47
+ const relativeFiles = files.map((file) => toPosix(path.relative(absoluteOutDir, file)));
48
+ const requiredFiles = [
49
+ "harness.mjs",
50
+ "postinstall.mjs",
51
+ "lib/harness-core.mjs",
52
+ "commands/task-command.mjs",
53
+ ];
54
+ const missingFiles = requiredFiles.filter((file) => !relativeFiles.includes(file));
55
+ if (missingFiles.length > 0) {
56
+ return {
57
+ ok: false,
58
+ error: `dist build missing required runtime files: ${missingFiles.join(", ")}`,
59
+ outDir: absoluteOutDir,
60
+ files: relativeFiles,
61
+ };
62
+ }
63
+ return {
64
+ ok: true,
65
+ outDir: absoluteOutDir,
66
+ files: relativeFiles,
67
+ };
68
+ }
69
+ function isDangerousOutDir({ projectRoot, outDir }) {
70
+ const parsed = path.parse(outDir);
71
+ if (outDir === parsed.root)
72
+ return true;
73
+ if (outDir === projectRoot)
74
+ return true;
75
+ const defaultDist = path.join(projectRoot, "dist");
76
+ if (outDir === defaultDist || outDir.startsWith(`${defaultDist}${path.sep}`))
77
+ return false;
78
+ const relativeToProject = path.relative(projectRoot, outDir);
79
+ if (relativeToProject && !relativeToProject.startsWith("..") && !path.isAbsolute(relativeToProject))
80
+ return true;
81
+ const tempRoot = fs.realpathSync.native(os.tmpdir());
82
+ const outputParent = fs.existsSync(path.dirname(outDir)) ? fs.realpathSync.native(path.dirname(outDir)) : path.resolve(path.dirname(outDir));
83
+ if (outputParent === tempRoot || outputParent.startsWith(`${tempRoot}${path.sep}`))
84
+ return false;
85
+ return true;
86
+ }
87
+ function collectFiles(directory) {
88
+ const files = [];
89
+ if (fs.existsSync(directory))
90
+ walk(directory, files);
91
+ return files;
92
+ }
93
+ function walk(current, files) {
94
+ const stat = fs.lstatSync(current);
95
+ if (stat.isSymbolicLink())
96
+ return;
97
+ if (stat.isDirectory()) {
98
+ for (const entry of fs.readdirSync(current))
99
+ walk(path.join(current, entry), files);
100
+ return;
101
+ }
102
+ if (stat.isFile())
103
+ files.push(current);
104
+ }
105
+ function syncDirectory(sourceDir, targetDir) {
106
+ fs.mkdirSync(targetDir, { recursive: true });
107
+ const sourceEntries = new Set(fs.readdirSync(sourceDir));
108
+ for (const entry of sourceEntries) {
109
+ const source = path.join(sourceDir, entry);
110
+ const target = path.join(targetDir, entry);
111
+ const stat = fs.lstatSync(source);
112
+ if (stat.isDirectory()) {
113
+ syncDirectory(source, target);
114
+ }
115
+ else if (stat.isFile()) {
116
+ const tempTarget = `${target}.tmp-${process.pid}`;
117
+ fs.copyFileSync(source, tempTarget);
118
+ fs.renameSync(tempTarget, target);
119
+ }
120
+ }
121
+ for (const entry of fs.readdirSync(targetDir)) {
122
+ if (sourceEntries.has(entry))
123
+ continue;
124
+ if (entry.includes(".tmp-"))
125
+ continue;
126
+ fs.rmSync(path.join(targetDir, entry), { recursive: true, force: true });
127
+ }
128
+ }
129
+ function toPosix(value) {
130
+ return value.split(path.sep).join("/");
131
+ }
132
+ function parseArgs(argv) {
133
+ const options = {
134
+ projectRoot: repoRoot,
135
+ configPath: path.join(repoRoot, "tsconfig.dist.json"),
136
+ outDir: path.join(repoRoot, "dist"),
137
+ json: false,
138
+ quiet: false,
139
+ };
140
+ for (let index = 0; index < argv.length; index += 1) {
141
+ const arg = argv[index];
142
+ if (arg === "--json") {
143
+ options.json = true;
144
+ }
145
+ else if (arg === "--quiet") {
146
+ options.quiet = true;
147
+ }
148
+ else if (arg === "--out-dir") {
149
+ options.outDir = path.resolve(options.projectRoot, requireValue(argv, index, arg));
150
+ index += 1;
151
+ }
152
+ else if (arg === "--config") {
153
+ options.configPath = path.resolve(options.projectRoot, requireValue(argv, index, arg));
154
+ index += 1;
155
+ }
156
+ else {
157
+ throw new Error(`Unknown build-dist option: ${arg}`);
158
+ }
159
+ }
160
+ return options;
161
+ }
162
+ function requireValue(argv, index, option) {
163
+ const value = argv[index + 1];
164
+ if (!value)
165
+ throw new Error(`${option} requires a value`);
166
+ return value;
167
+ }
168
+ if (import.meta.url === `file://${process.argv[1]}`) {
169
+ let options;
170
+ try {
171
+ options = parseArgs(process.argv.slice(2));
172
+ }
173
+ catch (error) {
174
+ console.error(error.message);
175
+ process.exit(1);
176
+ }
177
+ const result = buildRuntimeDist(options);
178
+ if (options.json) {
179
+ console.log(JSON.stringify(result, null, 2));
180
+ }
181
+ else if (result.ok && !options.quiet) {
182
+ console.log(`Runtime dist build completed: ${path.relative(repoRoot, result.outDir) || "."} (${result.files.length} files)`);
183
+ }
184
+ else if (!result.ok) {
185
+ console.error(result.error);
186
+ }
187
+ if (!result.ok)
188
+ process.exit(result.status || 1);
189
+ }
@@ -0,0 +1,428 @@
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, pathToFileURL } from "node:url";
8
+ const defaultProjectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ export function checkDistObservation({ projectRoot = defaultProjectRoot, runPack = true, runInstallSmoke = true, runCommandMatrix = true, } = {}) {
10
+ const root = path.resolve(projectRoot);
11
+ const failures = [];
12
+ const observations = {
13
+ packageRuntime: {},
14
+ inventory: {},
15
+ package: {},
16
+ installSmoke: {},
17
+ commandMatrix: [],
18
+ };
19
+ const pkg = readJson(path.join(root, "package.json"), failures, "package-json");
20
+ if (!pkg)
21
+ return { ok: false, failures, observations };
22
+ expectEqual(failures, "package-bin-not-dist", pkg.bin?.harness, "dist/harness.mjs", "package bin.harness must resolve to dist/harness.mjs");
23
+ const distRuntimeScripts = {
24
+ check: "node dist/harness.mjs check --profile source-package .",
25
+ "check:private": "node dist/harness.mjs check --profile private-harness .harness-private",
26
+ status: "node dist/harness.mjs status --json .",
27
+ dashboard: "node dist/harness.mjs dashboard --out tmp/harness-dashboard.html examples/minimal-project",
28
+ "dashboard:folder": "node dist/harness.mjs dashboard --out-dir tmp/harness-dashboard examples/minimal-project",
29
+ postinstall: "node dist/postinstall.mjs",
30
+ "observe:dist": "node dist/check-dist-observation.mjs --skip-pack --skip-install-smoke",
31
+ };
32
+ for (const [name, expected] of Object.entries(distRuntimeScripts)) {
33
+ expectEqual(failures, `package-script-${name}-not-dist`, pkg.scripts?.[name], expected, `package script ${name} must run from dist`);
34
+ }
35
+ observations.packageRuntime = {
36
+ bin: pkg.bin?.harness,
37
+ scripts: Object.fromEntries(Object.keys(distRuntimeScripts).map((name) => [name, pkg.scripts?.[name]])),
38
+ };
39
+ // `npm pack --dry-run` runs `prepack`, which refreshes committed `dist/`.
40
+ // Run it before inspecting dist so the observation is from one stable build.
41
+ if (runPack) {
42
+ const pack = spawnSync("npm", ["pack", "--dry-run", "--json"], {
43
+ cwd: root,
44
+ encoding: "utf8",
45
+ maxBuffer: 32 * 1024 * 1024,
46
+ });
47
+ if (pack.status !== 0) {
48
+ failures.push({ code: "pack-dry-run-failed", message: `npm pack dry-run failed\nSTDOUT:\n${pack.stdout}\nSTDERR:\n${pack.stderr}` });
49
+ }
50
+ else {
51
+ const packed = JSON.parse(pack.stdout)[0].files.map((file) => file.path).sort();
52
+ observations.package = {
53
+ entryCount: packed.length,
54
+ hasDistHarness: packed.includes("dist/harness.mjs"),
55
+ hasDistPostinstall: packed.includes("dist/postinstall.mjs"),
56
+ hasDistObservationGate: packed.includes("dist/check-dist-observation.mjs"),
57
+ hasScriptsHarness: packed.includes("scripts/harness.mjs"),
58
+ hasScripts: packed.some((file) => file.startsWith("scripts/")),
59
+ hasTests: packed.some((file) => file.startsWith("tests/")),
60
+ };
61
+ for (const required of ["dist/harness.mjs", "dist/postinstall.mjs", "dist/check-dist-observation.mjs"]) {
62
+ if (!packed.includes(required))
63
+ failures.push({ code: "packed-file-missing", file: required, message: `package missing ${required}` });
64
+ }
65
+ if (observations.package.hasScripts)
66
+ failures.push({ code: "package-includes-scripts", message: "package must not include scripts/** after historical shim deletion" });
67
+ if (observations.package.hasTests)
68
+ failures.push({ code: "package-includes-tests", message: "package must not include tests/**" });
69
+ }
70
+ }
71
+ const requiredDist = ["dist/harness.mjs", "dist/postinstall.mjs", "dist/check-dist-observation.mjs"];
72
+ for (const relative of requiredDist) {
73
+ if (!fs.existsSync(path.join(root, relative))) {
74
+ failures.push({ code: "missing-dist-runtime", message: `missing dist runtime artifact: ${relative}` });
75
+ }
76
+ }
77
+ const distFiles = collectFiles(path.join(root, "dist")).filter((file) => file.endsWith(".mjs"));
78
+ for (const file of distFiles) {
79
+ const relative = toPosix(path.relative(root, file));
80
+ const content = fs.readFileSync(file, "utf8");
81
+ for (const specifier of parseImportSpecifiers(content)) {
82
+ if (/\.(?:ts|mts)$/.test(specifier)) {
83
+ failures.push({ code: "dist-imports-typescript-source", file: relative, message: `${relative} imports TypeScript source ${specifier}` });
84
+ }
85
+ if (specifier.includes("scripts/") && specifier.endsWith(".mjs")) {
86
+ failures.push({ code: "dist-imports-scripts-shim", file: relative, message: `${relative} imports historical scripts shim ${specifier}` });
87
+ }
88
+ }
89
+ }
90
+ const scriptShims = collectFiles(path.join(root, "scripts")).filter((file) => file.endsWith(".mjs"));
91
+ const testShims = collectFiles(path.join(root, "tests")).filter((file) => file.endsWith(".mjs"));
92
+ const unpairedScriptShims = scriptShims.filter((file) => !fs.existsSync(file.replace(/\.mjs$/, ".mts")));
93
+ const unpairedTestShims = testShims.filter((file) => !fs.existsSync(file.replace(/\.mjs$/, ".mts")));
94
+ for (const file of [...unpairedScriptShims, ...unpairedTestShims]) {
95
+ failures.push({
96
+ code: "historical-shim-without-typescript-source",
97
+ file: toPosix(path.relative(root, file)),
98
+ message: `${toPosix(path.relative(root, file))} has no adjacent .mts source twin`,
99
+ });
100
+ }
101
+ observations.inventory = {
102
+ distMjs: distFiles.length,
103
+ scriptShims: scriptShims.length,
104
+ testShims: testShims.length,
105
+ unpairedScriptShims: unpairedScriptShims.length,
106
+ unpairedTestShims: unpairedTestShims.length,
107
+ };
108
+ if (scriptShims.length > 0) {
109
+ failures.push({ code: "historical-script-shims-remain", message: `PR-28 final inventory must have 0 scripts/**/*.mjs files; found ${scriptShims.length}` });
110
+ }
111
+ if (testShims.length > 0) {
112
+ failures.push({ code: "historical-test-shims-remain", message: `PR-28 final inventory must have 0 tests/**/*.mjs files; found ${testShims.length}` });
113
+ }
114
+ if (runCommandMatrix) {
115
+ runMatrix(root, failures, observations.commandMatrix);
116
+ }
117
+ if (runInstallSmoke) {
118
+ runInstalledPackageSmoke(root, failures, observations);
119
+ }
120
+ return {
121
+ ok: failures.length === 0,
122
+ failures,
123
+ observations,
124
+ };
125
+ }
126
+ function runMatrix(root, failures, commandMatrix) {
127
+ const distHarness = path.join(root, "dist/harness.mjs");
128
+ const matrix = [
129
+ { id: "help", args: ["--help"] },
130
+ { id: "status", args: ["status", "--json", "."] },
131
+ { id: "task-list", args: ["task-list", "--json", "."] },
132
+ { id: "preset-list", args: ["preset", "list", "--json", "."] },
133
+ { id: "source-check", args: ["check", "--profile", "source-package", "."] },
134
+ { id: "target-check", args: ["check", "--profile", "target-project", "examples/minimal-project"] },
135
+ { id: "migrate-plan", args: ["migrate-plan", "--json", "--limit", "20", "examples/minimal-project"] },
136
+ { id: "dashboard", args: ["dashboard", "--out-dir", path.join("tmp", `pr-27-observation-dashboard-${process.pid}`), "examples/minimal-project"] },
137
+ ];
138
+ for (const entry of matrix) {
139
+ const result = spawnSync(process.execPath, [distHarness, ...entry.args], {
140
+ cwd: root,
141
+ encoding: "utf8",
142
+ maxBuffer: 16 * 1024 * 1024,
143
+ });
144
+ commandMatrix.push({ id: entry.id, status: result.status });
145
+ if (result.status !== 0) {
146
+ failures.push({
147
+ code: "dist-command-failed",
148
+ command: entry.id,
149
+ message: `dist command ${entry.id} failed\nSTDOUT:\n${result.stdout}\nSTDERR:\n${result.stderr}`,
150
+ });
151
+ }
152
+ }
153
+ const postinstall = spawnSync(process.execPath, [path.join(root, "dist/postinstall.mjs")], {
154
+ cwd: root,
155
+ encoding: "utf8",
156
+ env: { ...process.env, CODING_AGENT_HARNESS_SKIP_POSTINSTALL: "1" },
157
+ });
158
+ commandMatrix.push({ id: "postinstall-skip", status: postinstall.status });
159
+ if (postinstall.status !== 0) {
160
+ failures.push({
161
+ code: "dist-postinstall-failed",
162
+ message: `dist postinstall failed\nSTDOUT:\n${postinstall.stdout}\nSTDERR:\n${postinstall.stderr}`,
163
+ });
164
+ }
165
+ }
166
+ function runInstalledPackageSmoke(root, failures, observations) {
167
+ const node24 = findNode24();
168
+ if (!node24) {
169
+ failures.push({ code: "node24-not-found", message: "install smoke requires a Node 24 executable" });
170
+ return;
171
+ }
172
+ const nodeBin = path.dirname(node24);
173
+ const nodeVersion = spawnSync(node24, ["--version"], { encoding: "utf8" }).stdout.trim();
174
+ if (!nodeVersion.startsWith("v24.")) {
175
+ failures.push({ code: "node24-version-mismatch", actual: nodeVersion, message: `install smoke must run on Node 24, got ${nodeVersion}` });
176
+ return;
177
+ }
178
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "harness-dist-observation-install-"));
179
+ const packDir = path.join(tempRoot, "pack");
180
+ const consumer = path.join(tempRoot, "consumer");
181
+ const home = path.join(tempRoot, "home");
182
+ fs.mkdirSync(packDir, { recursive: true });
183
+ fs.mkdirSync(consumer, { recursive: true });
184
+ fs.mkdirSync(home, { recursive: true });
185
+ const npmEnv = isolatedEnv({ nodeBin, home });
186
+ const pack = spawnSync("npm", ["pack", "--silent", "--pack-destination", packDir], {
187
+ cwd: root,
188
+ encoding: "utf8",
189
+ env: npmEnv,
190
+ maxBuffer: 32 * 1024 * 1024,
191
+ });
192
+ if (pack.status !== 0) {
193
+ failures.push({ code: "install-smoke-pack-failed", message: `npm pack failed\nSTDOUT:\n${pack.stdout}\nSTDERR:\n${pack.stderr}` });
194
+ return;
195
+ }
196
+ const tarball = path.join(packDir, pack.stdout.trim().split(/\r?\n/).at(-1));
197
+ fs.writeFileSync(path.join(consumer, "package.json"), JSON.stringify({ private: true, type: "module" }, null, 2));
198
+ const install = spawnSync("npm", ["install", "--silent", "--no-audit", "--no-fund", tarball], {
199
+ cwd: consumer,
200
+ encoding: "utf8",
201
+ env: npmEnv,
202
+ maxBuffer: 32 * 1024 * 1024,
203
+ });
204
+ if (install.status !== 0) {
205
+ failures.push({ code: "install-smoke-install-failed", message: `npm install packed tarball failed\nSTDOUT:\n${install.stdout}\nSTDERR:\n${install.stderr}` });
206
+ return;
207
+ }
208
+ const packageRoot = path.join(consumer, "node_modules/coding-agent-harness");
209
+ const bin = path.join(consumer, "node_modules/.bin/harness");
210
+ const pkg = readJson(path.join(packageRoot, "package.json"), failures, "installed-package-json");
211
+ if (!pkg)
212
+ return;
213
+ const binTarget = fs.existsSync(bin) ? fs.readlinkSync(bin) : "";
214
+ observations.installSmoke = {
215
+ nodeVersion,
216
+ tempRoot,
217
+ binTarget,
218
+ bin: pkg.bin?.harness,
219
+ postinstall: pkg.scripts?.postinstall,
220
+ observeDist: pkg.scripts?.["observe:dist"],
221
+ hasTests: fs.existsSync(path.join(packageRoot, "tests")),
222
+ hasScripts: fs.existsSync(path.join(packageRoot, "scripts")),
223
+ scriptsDisabled: [],
224
+ steps: [],
225
+ };
226
+ expectEqual(failures, "installed-bin-not-dist", pkg.bin?.harness, "dist/harness.mjs", "installed package bin.harness must resolve to dist/harness.mjs");
227
+ expectEqual(failures, "installed-postinstall-not-dist", pkg.scripts?.postinstall, "node dist/postinstall.mjs", "installed package postinstall must resolve to dist/postinstall.mjs");
228
+ expectEqual(failures, "installed-observe-dist-not-dist", pkg.scripts?.["observe:dist"], "node dist/check-dist-observation.mjs --skip-pack --skip-install-smoke", "installed observe:dist must resolve to dist/check-dist-observation.mjs");
229
+ if (!binTarget.includes("dist/harness.mjs")) {
230
+ failures.push({ code: "installed-bin-link-not-dist", message: `installed bin link does not target dist/harness.mjs: ${binTarget}` });
231
+ }
232
+ for (const relative of ["dist/harness.mjs", "dist/postinstall.mjs", "dist/check-dist-observation.mjs"]) {
233
+ if (!fs.existsSync(path.join(packageRoot, relative)))
234
+ failures.push({ code: "installed-file-missing", file: relative, message: `installed package missing ${relative}` });
235
+ }
236
+ if (observations.installSmoke.hasTests)
237
+ failures.push({ code: "installed-package-includes-tests", message: "installed package must not include tests/**" });
238
+ if (observations.installSmoke.hasScripts)
239
+ failures.push({ code: "installed-package-includes-scripts", message: "installed package must not include scripts/** after historical shim deletion" });
240
+ const installedScripts = path.join(packageRoot, "scripts");
241
+ if (fs.existsSync(installedScripts)) {
242
+ fs.renameSync(installedScripts, `${installedScripts}.disabled-by-dist-observation`);
243
+ observations.installSmoke.scriptsDisabled.push("scripts/");
244
+ }
245
+ const runtimeEnv = isolatedEnv({ nodeBin, home, extraPath: [path.join(consumer, "node_modules", ".bin")] });
246
+ runInstalledMatrix(root, runtimeEnv, failures, observations.installSmoke.steps);
247
+ const postinstall = spawnSync(node24, [path.join(packageRoot, "dist/postinstall.mjs")], {
248
+ cwd: packageRoot,
249
+ encoding: "utf8",
250
+ env: { ...runtimeEnv, CODING_AGENT_HARNESS_SKIP_POSTINSTALL: "1" },
251
+ });
252
+ observations.installSmoke.steps.push({ id: "installed-dist-postinstall", status: postinstall.status });
253
+ if (postinstall.status !== 0)
254
+ failures.push({ code: "installed-postinstall-failed", message: `installed dist postinstall failed\nSTDOUT:\n${postinstall.stdout}\nSTDERR:\n${postinstall.stderr}` });
255
+ const installedObservation = spawnSync(node24, [path.join(packageRoot, "dist/check-dist-observation.mjs"), "--project-root", packageRoot, "--skip-pack", "--skip-install-smoke", "--skip-command-matrix", "--json"], {
256
+ cwd: packageRoot,
257
+ encoding: "utf8",
258
+ env: runtimeEnv,
259
+ maxBuffer: 32 * 1024 * 1024,
260
+ });
261
+ observations.installSmoke.steps.push({ id: "installed-observation", status: installedObservation.status });
262
+ if (installedObservation.status !== 0) {
263
+ failures.push({ code: "installed-observation-failed", message: `installed observation failed\nSTDOUT:\n${installedObservation.stdout}\nSTDERR:\n${installedObservation.stderr}` });
264
+ }
265
+ else {
266
+ const installedResult = JSON.parse(installedObservation.stdout);
267
+ observations.installSmoke.observationOk = installedResult.ok;
268
+ if (!installedResult.ok)
269
+ failures.push({ code: "installed-observation-not-ok", message: JSON.stringify(installedResult.failures, null, 2) });
270
+ }
271
+ }
272
+ function runInstalledMatrix(root, runtimeEnv, failures, steps) {
273
+ const matrix = [
274
+ { id: "installed-help", cwd: root, args: ["--help"] },
275
+ { id: "installed-status", cwd: root, args: ["status", "--json", "."] },
276
+ { id: "installed-task-list", cwd: root, args: ["task-list", "--json", "."] },
277
+ { id: "installed-preset-list", cwd: root, args: ["preset", "list", "--json", "."] },
278
+ { id: "installed-source-check", cwd: root, args: ["check", "--profile", "source-package", "."] },
279
+ { id: "installed-target-check", cwd: root, args: ["check", "--profile", "target-project", "examples/minimal-project"] },
280
+ { id: "installed-migrate-plan", cwd: root, args: ["migrate-plan", "--json", "--limit", "20", "examples/minimal-project"] },
281
+ { id: "installed-dashboard", cwd: root, args: ["dashboard", "--out-dir", path.join("tmp", `pr-27-installed-observation-dashboard-${process.pid}`), "examples/minimal-project"] },
282
+ ];
283
+ for (const entry of matrix) {
284
+ const result = spawnSync("harness", entry.args, {
285
+ cwd: entry.cwd,
286
+ encoding: "utf8",
287
+ env: runtimeEnv,
288
+ maxBuffer: 16 * 1024 * 1024,
289
+ });
290
+ steps.push({ id: entry.id, status: result.status });
291
+ if (result.status !== 0) {
292
+ failures.push({
293
+ code: "installed-command-failed",
294
+ command: entry.id,
295
+ message: `installed command ${entry.id} failed after scripts/ isolation\nSTDOUT:\n${result.stdout}\nSTDERR:\n${result.stderr}`,
296
+ });
297
+ }
298
+ }
299
+ }
300
+ function findNode24() {
301
+ const candidates = [
302
+ process.env.NODE24,
303
+ process.env.NODE24_PATH,
304
+ process.execPath,
305
+ path.join(os.homedir(), ".nvm", "versions", "node", "v24.16.0", "bin", "node"),
306
+ path.join(os.homedir(), ".nvm", "versions", "node", "v24.13.1", "bin", "node"),
307
+ "/opt/homebrew/opt/node@24/bin/node",
308
+ "/usr/local/opt/node@24/bin/node",
309
+ ].filter(Boolean);
310
+ for (const candidate of candidates) {
311
+ if (!fs.existsSync(candidate))
312
+ continue;
313
+ const version = spawnSync(candidate, ["--version"], { encoding: "utf8" });
314
+ if (version.status === 0 && version.stdout.trim().startsWith("v24."))
315
+ return candidate;
316
+ }
317
+ return undefined;
318
+ }
319
+ function isolatedEnv({ nodeBin, home = process.env.HOME, extraPath = [] }) {
320
+ return {
321
+ ...process.env,
322
+ HOME: home,
323
+ npm_config_cache: path.join(home, ".npm"),
324
+ PATH: [...extraPath, nodeBin, "/usr/bin", "/bin"].join(path.delimiter),
325
+ };
326
+ }
327
+ function readJson(file, failures, code) {
328
+ try {
329
+ return JSON.parse(fs.readFileSync(file, "utf8"));
330
+ }
331
+ catch (error) {
332
+ failures.push({ code, message: `failed to read ${file}: ${error.message}` });
333
+ return undefined;
334
+ }
335
+ }
336
+ function expectEqual(failures, code, actual, expected, message) {
337
+ if (actual !== expected)
338
+ failures.push({ code, actual, expected, message });
339
+ }
340
+ function parseImportSpecifiers(content) {
341
+ const specifiers = [];
342
+ for (const match of content.matchAll(/\bfrom\s*["']([^"']+)["']/g))
343
+ specifiers.push(match[1]);
344
+ for (const match of content.matchAll(/\bimport\s*["']([^"']+)["']/g))
345
+ specifiers.push(match[1]);
346
+ for (const match of content.matchAll(/\bimport\s*\(\s*["']([^"']+)["']/g))
347
+ specifiers.push(match[1]);
348
+ return specifiers;
349
+ }
350
+ function collectFiles(directory) {
351
+ const files = [];
352
+ if (!fs.existsSync(directory))
353
+ return files;
354
+ walk(directory, files);
355
+ return files.sort();
356
+ }
357
+ function walk(current, files) {
358
+ const stat = fs.lstatSync(current);
359
+ if (stat.isSymbolicLink())
360
+ return;
361
+ if (stat.isDirectory()) {
362
+ for (const entry of fs.readdirSync(current))
363
+ walk(path.join(current, entry), files);
364
+ return;
365
+ }
366
+ if (stat.isFile())
367
+ files.push(current);
368
+ }
369
+ function toPosix(value) {
370
+ return value.split(path.sep).join("/");
371
+ }
372
+ function parseArgs(argv) {
373
+ const options = { json: false, runPack: true, runInstallSmoke: true, runCommandMatrix: true, projectRoot: defaultProjectRoot };
374
+ for (let index = 0; index < argv.length; index += 1) {
375
+ const arg = argv[index];
376
+ if (arg === "--json")
377
+ options.json = true;
378
+ else if (arg === "--skip-pack")
379
+ options.runPack = false;
380
+ else if (arg === "--skip-install-smoke")
381
+ options.runInstallSmoke = false;
382
+ else if (arg === "--skip-command-matrix")
383
+ options.runCommandMatrix = false;
384
+ else if (arg === "--project-root") {
385
+ options.projectRoot = path.resolve(requireValue(argv, index, arg));
386
+ index += 1;
387
+ }
388
+ else {
389
+ throw new Error(`Unknown check-dist-observation option: ${arg}`);
390
+ }
391
+ }
392
+ return options;
393
+ }
394
+ function requireValue(argv, index, option) {
395
+ const value = argv[index + 1];
396
+ if (!value)
397
+ throw new Error(`${option} requires a value`);
398
+ return value;
399
+ }
400
+ function isMainModule() {
401
+ if (!process.argv[1])
402
+ return false;
403
+ try {
404
+ return fs.realpathSync.native(fileURLToPath(import.meta.url)) === fs.realpathSync.native(process.argv[1]);
405
+ }
406
+ catch {
407
+ return import.meta.url === pathToFileURL(process.argv[1]).href;
408
+ }
409
+ }
410
+ if (isMainModule()) {
411
+ let options;
412
+ try {
413
+ options = parseArgs(process.argv.slice(2));
414
+ }
415
+ catch (error) {
416
+ console.error(error.message);
417
+ process.exit(1);
418
+ }
419
+ const result = checkDistObservation(options);
420
+ if (options.json)
421
+ console.log(JSON.stringify(result, null, 2));
422
+ else if (result.ok)
423
+ console.log(`Dist observation gate passed: ${options.projectRoot}`);
424
+ else
425
+ console.error(result.failures.map((failure) => failure.message).join("\n"));
426
+ if (!result.ok)
427
+ process.exit(1);
428
+ }