@deftai/directive 0.126.2 → 0.128.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 (420) hide show
  1. package/bin/deft-hook +14 -0
  2. package/bin/deft-hook.cmd +14 -0
  3. package/dist/bin.js +4319 -3
  4. package/package.json +11 -21
  5. package/dist/agents-refresh.d.ts +0 -11
  6. package/dist/agents-refresh.js +0 -90
  7. package/dist/authz.d.ts +0 -7
  8. package/dist/authz.js +0 -732
  9. package/dist/bin.d.ts +0 -3
  10. package/dist/branch-fixtures.d.ts +0 -45
  11. package/dist/branch-fixtures.js +0 -156
  12. package/dist/cache.d.ts +0 -4
  13. package/dist/cache.js +0 -10
  14. package/dist/capacity-backfill.d.ts +0 -3
  15. package/dist/capacity-backfill.js +0 -9
  16. package/dist/capacity-show.d.ts +0 -3
  17. package/dist/capacity-show.js +0 -9
  18. package/dist/change-init.d.ts +0 -3
  19. package/dist/change-init.js +0 -59
  20. package/dist/changelog-check.d.ts +0 -3
  21. package/dist/changelog-check.js +0 -52
  22. package/dist/check.d.ts +0 -15
  23. package/dist/check.js +0 -150
  24. package/dist/cli-package-version.d.ts +0 -3
  25. package/dist/cli-package-version.js +0 -23
  26. package/dist/cli-router/index.d.ts +0 -8
  27. package/dist/cli-router/index.js +0 -44
  28. package/dist/cli-router/route-argv.d.ts +0 -40
  29. package/dist/cli-router/route-argv.js +0 -371
  30. package/dist/codebase-default-extractor.d.ts +0 -3
  31. package/dist/codebase-default-extractor.js +0 -9
  32. package/dist/codebase-map-fresh.d.ts +0 -3
  33. package/dist/codebase-map-fresh.js +0 -9
  34. package/dist/codebase-map.d.ts +0 -3
  35. package/dist/codebase-map.js +0 -9
  36. package/dist/codebase-projection-registry.d.ts +0 -3
  37. package/dist/codebase-projection-registry.js +0 -9
  38. package/dist/codebase-provider.d.ts +0 -3
  39. package/dist/codebase-provider.js +0 -9
  40. package/dist/commit-lint.d.ts +0 -3
  41. package/dist/commit-lint.js +0 -44
  42. package/dist/content-validate-cli/validate-links.d.ts +0 -11
  43. package/dist/content-validate-cli/validate-links.js +0 -37
  44. package/dist/content-validate-cli/validate-strategy-output.d.ts +0 -13
  45. package/dist/content-validate-cli/validate-strategy-output.js +0 -56
  46. package/dist/content-validate-cli/verify-capacity.d.ts +0 -12
  47. package/dist/content-validate-cli/verify-capacity.js +0 -54
  48. package/dist/coverage-hotspots.d.ts +0 -21
  49. package/dist/coverage-hotspots.js +0 -198
  50. package/dist/design-critique-spend-resolve.d.ts +0 -18
  51. package/dist/design-critique-spend-resolve.js +0 -191
  52. package/dist/directive-finish-loop.d.ts +0 -3
  53. package/dist/directive-finish-loop.js +0 -10
  54. package/dist/dispatch.d.ts +0 -193
  55. package/dist/dispatch.js +0 -3091
  56. package/dist/doctor.d.ts +0 -19
  57. package/dist/doctor.js +0 -155
  58. package/dist/entrypoint.d.ts +0 -6
  59. package/dist/entrypoint.js +0 -19
  60. package/dist/escalation-cli.d.ts +0 -4
  61. package/dist/escalation-cli.js +0 -301
  62. package/dist/eval-health.d.ts +0 -13
  63. package/dist/eval-health.js +0 -63
  64. package/dist/eval-report.d.ts +0 -15
  65. package/dist/eval-report.js +0 -97
  66. package/dist/eval-run.d.ts +0 -17
  67. package/dist/eval-run.js +0 -122
  68. package/dist/eval-triggers.d.ts +0 -12
  69. package/dist/eval-triggers.js +0 -56
  70. package/dist/framework-check-updates.d.ts +0 -10
  71. package/dist/framework-check-updates.js +0 -68
  72. package/dist/gates-cli/_helpers.d.ts +0 -24
  73. package/dist/gates-cli/_helpers.js +0 -115
  74. package/dist/gates-cli/verify-bridge-drift.d.ts +0 -11
  75. package/dist/gates-cli/verify-bridge-drift.js +0 -47
  76. package/dist/gates-cli/verify-go-freeze.d.ts +0 -11
  77. package/dist/gates-cli/verify-go-freeze.js +0 -48
  78. package/dist/hook-bin.d.ts +0 -3
  79. package/dist/hook-bin.js +0 -11
  80. package/dist/hook-dispatch.d.ts +0 -80
  81. package/dist/hook-dispatch.js +0 -186
  82. package/dist/human-presence-mint.d.ts +0 -70
  83. package/dist/human-presence-mint.js +0 -204
  84. package/dist/identity-banner.d.ts +0 -13
  85. package/dist/identity-banner.js +0 -19
  86. package/dist/index.d.ts +0 -11
  87. package/dist/index.js +0 -14
  88. package/dist/init-cli/constants.d.ts +0 -42
  89. package/dist/init-cli/constants.js +0 -42
  90. package/dist/init-cli/help.d.ts +0 -9
  91. package/dist/init-cli/help.js +0 -43
  92. package/dist/init-cli/init.d.ts +0 -26
  93. package/dist/init-cli/init.js +0 -80
  94. package/dist/init-cli/migrate.d.ts +0 -3
  95. package/dist/init-cli/migrate.js +0 -51
  96. package/dist/init-cli/resolve-binary.d.ts +0 -34
  97. package/dist/init-cli/resolve-binary.js +0 -78
  98. package/dist/init-cli/run-deft-install.d.ts +0 -23
  99. package/dist/init-cli/run-deft-install.js +0 -71
  100. package/dist/init-cli/update.d.ts +0 -5
  101. package/dist/init-cli/update.js +0 -38
  102. package/dist/install-cli/coverage-map.d.ts +0 -14
  103. package/dist/install-cli/coverage-map.js +0 -113
  104. package/dist/install-uninstall.d.ts +0 -3
  105. package/dist/install-uninstall.js +0 -44
  106. package/dist/install-upgrade.d.ts +0 -41
  107. package/dist/install-upgrade.js +0 -86
  108. package/dist/lifecycle-cli/coverage-map.d.ts +0 -16
  109. package/dist/lifecycle-cli/coverage-map.js +0 -254
  110. package/dist/lifecycle-cli/helpers.d.ts +0 -8
  111. package/dist/lifecycle-cli/helpers.js +0 -20
  112. package/dist/lifecycle-event.d.ts +0 -11
  113. package/dist/lifecycle-event.js +0 -45
  114. package/dist/lifecycle-stats.d.ts +0 -12
  115. package/dist/lifecycle-stats.js +0 -100
  116. package/dist/migrate-category-b.d.ts +0 -9
  117. package/dist/migrate-category-b.js +0 -52
  118. package/dist/migrate-preflight.d.ts +0 -10
  119. package/dist/migrate-preflight.js +0 -85
  120. package/dist/migrate-xbrief.d.ts +0 -11
  121. package/dist/migrate-xbrief.js +0 -88
  122. package/dist/occupancy-grant.d.ts +0 -18
  123. package/dist/occupancy-grant.js +0 -121
  124. package/dist/occupancy-heartbeat.d.ts +0 -8
  125. package/dist/occupancy-heartbeat.js +0 -63
  126. package/dist/occupancy-release.d.ts +0 -23
  127. package/dist/occupancy-release.js +0 -102
  128. package/dist/occupancy-steal.d.ts +0 -10
  129. package/dist/occupancy-steal.js +0 -83
  130. package/dist/occupancy-unrecognized.d.ts +0 -3
  131. package/dist/occupancy-unrecognized.js +0 -5
  132. package/dist/orchestration-cli/coverage-map.d.ts +0 -21
  133. package/dist/orchestration-cli/coverage-map.js +0 -225
  134. package/dist/orchestration-cli/helpers.d.ts +0 -11
  135. package/dist/orchestration-cli/helpers.js +0 -35
  136. package/dist/ownership-doctor.d.ts +0 -10
  137. package/dist/ownership-doctor.js +0 -96
  138. package/dist/ownership-fix.d.ts +0 -10
  139. package/dist/ownership-fix.js +0 -93
  140. package/dist/plan-sequence.d.ts +0 -14
  141. package/dist/plan-sequence.js +0 -258
  142. package/dist/policy-fixtures.d.ts +0 -33
  143. package/dist/policy-fixtures.js +0 -118
  144. package/dist/policy.d.ts +0 -37
  145. package/dist/policy.js +0 -744
  146. package/dist/pr-closing-keywords.d.ts +0 -3
  147. package/dist/pr-closing-keywords.js +0 -10
  148. package/dist/pr-finish-loop.d.ts +0 -3
  149. package/dist/pr-finish-loop.js +0 -10
  150. package/dist/pr-merge-readiness.d.ts +0 -3
  151. package/dist/pr-merge-readiness.js +0 -10
  152. package/dist/pr-monitor.d.ts +0 -3
  153. package/dist/pr-monitor.js +0 -10
  154. package/dist/pr-protected-issues.d.ts +0 -3
  155. package/dist/pr-protected-issues.js +0 -10
  156. package/dist/pr-wait-mergeable.d.ts +0 -3
  157. package/dist/pr-wait-mergeable.js +0 -10
  158. package/dist/pr-watch.d.ts +0 -3
  159. package/dist/pr-watch.js +0 -10
  160. package/dist/pre-pr-run.d.ts +0 -39
  161. package/dist/pre-pr-run.js +0 -514
  162. package/dist/preflight-cache.d.ts +0 -18
  163. package/dist/preflight-cache.js +0 -113
  164. package/dist/preflight-gh.d.ts +0 -15
  165. package/dist/preflight-gh.js +0 -143
  166. package/dist/probe-session.d.ts +0 -3
  167. package/dist/probe-session.js +0 -10
  168. package/dist/release-e2e-fixtures.d.ts +0 -29
  169. package/dist/release-e2e-fixtures.js +0 -38
  170. package/dist/release-e2e.d.ts +0 -3
  171. package/dist/release-e2e.js +0 -10
  172. package/dist/release-publish.d.ts +0 -3
  173. package/dist/release-publish.js +0 -10
  174. package/dist/release-rollback.d.ts +0 -3
  175. package/dist/release-rollback.js +0 -10
  176. package/dist/release-wait-npm.d.ts +0 -3
  177. package/dist/release-wait-npm.js +0 -10
  178. package/dist/release.d.ts +0 -3
  179. package/dist/release.js +0 -10
  180. package/dist/render-cli/deft-ts-runner.d.ts +0 -17
  181. package/dist/render-cli/deft-ts-runner.js +0 -19
  182. package/dist/render-cli/export-spec-cli.d.ts +0 -2
  183. package/dist/render-cli/export-spec-cli.js +0 -6
  184. package/dist/render-cli/prd-render-cli.d.ts +0 -2
  185. package/dist/render-cli/prd-render-cli.js +0 -37
  186. package/dist/render-cli/project-render-cli.d.ts +0 -2
  187. package/dist/render-cli/project-render-cli.js +0 -6
  188. package/dist/render-cli/rule-map-cli.d.ts +0 -2
  189. package/dist/render-cli/rule-map-cli.js +0 -5
  190. package/dist/render-cli/spec-reconstruct-cli.d.ts +0 -3
  191. package/dist/render-cli/spec-reconstruct-cli.js +0 -15
  192. package/dist/render-cli/spec-render-cli.d.ts +0 -2
  193. package/dist/render-cli/spec-render-cli.js +0 -39
  194. package/dist/render-cli/spec-validate-cli.d.ts +0 -2
  195. package/dist/render-cli/spec-validate-cli.js +0 -38
  196. package/dist/review-monitor-register.d.ts +0 -19
  197. package/dist/review-monitor-register.js +0 -183
  198. package/dist/review-monitor-release.d.ts +0 -14
  199. package/dist/review-monitor-release.js +0 -123
  200. package/dist/scm-sync-default.d.ts +0 -34
  201. package/dist/scm-sync-default.js +0 -210
  202. package/dist/scope-lifecycle.d.ts +0 -4
  203. package/dist/scope-lifecycle.js +0 -34
  204. package/dist/scope-record-approved-scope.d.ts +0 -29
  205. package/dist/scope-record-approved-scope.js +0 -319
  206. package/dist/scope-record-intent-constraint.d.ts +0 -16
  207. package/dist/scope-record-intent-constraint.js +0 -202
  208. package/dist/scope-record-observable-scope.d.ts +0 -16
  209. package/dist/scope-record-observable-scope.js +0 -204
  210. package/dist/session-ready.d.ts +0 -15
  211. package/dist/session-ready.js +0 -144
  212. package/dist/session-start.d.ts +0 -56
  213. package/dist/session-start.js +0 -581
  214. package/dist/slice.d.ts +0 -4
  215. package/dist/slice.js +0 -34
  216. package/dist/story-ready-fixtures.d.ts +0 -47
  217. package/dist/story-ready-fixtures.js +0 -137
  218. package/dist/subagent-monitor.d.ts +0 -3
  219. package/dist/subagent-monitor.js +0 -10
  220. package/dist/subagent-pre-cancel.d.ts +0 -21
  221. package/dist/subagent-pre-cancel.js +0 -240
  222. package/dist/subagent-steer-write.d.ts +0 -41
  223. package/dist/subagent-steer-write.js +0 -333
  224. package/dist/toolchain-check.d.ts +0 -8
  225. package/dist/toolchain-check.js +0 -54
  226. package/dist/triage-actions.d.ts +0 -23
  227. package/dist/triage-actions.js +0 -233
  228. package/dist/triage-aux-a-fixtures.d.ts +0 -33
  229. package/dist/triage-aux-a-fixtures.js +0 -107
  230. package/dist/triage-aux-b-fixtures.d.ts +0 -30
  231. package/dist/triage-aux-b-fixtures.js +0 -167
  232. package/dist/triage-bootstrap-fixtures.d.ts +0 -42
  233. package/dist/triage-bootstrap-fixtures.js +0 -139
  234. package/dist/triage-bootstrap.d.ts +0 -24
  235. package/dist/triage-bootstrap.js +0 -175
  236. package/dist/triage-bulk.d.ts +0 -17
  237. package/dist/triage-bulk.js +0 -154
  238. package/dist/triage-classify-fixtures.d.ts +0 -38
  239. package/dist/triage-classify-fixtures.js +0 -137
  240. package/dist/triage-classify.d.ts +0 -45
  241. package/dist/triage-classify.js +0 -287
  242. package/dist/triage-evaluate.d.ts +0 -14
  243. package/dist/triage-evaluate.js +0 -153
  244. package/dist/triage-help.d.ts +0 -3
  245. package/dist/triage-help.js +0 -10
  246. package/dist/triage-queue-fixtures.d.ts +0 -59
  247. package/dist/triage-queue-fixtures.js +0 -284
  248. package/dist/triage-queue.d.ts +0 -38
  249. package/dist/triage-queue.js +0 -391
  250. package/dist/triage-reconcile.d.ts +0 -11
  251. package/dist/triage-reconcile.js +0 -76
  252. package/dist/triage-refresh.d.ts +0 -7
  253. package/dist/triage-refresh.js +0 -37
  254. package/dist/triage-scope-drift.d.ts +0 -12
  255. package/dist/triage-scope-drift.js +0 -117
  256. package/dist/triage-scope-fixtures.d.ts +0 -32
  257. package/dist/triage-scope-fixtures.js +0 -131
  258. package/dist/triage-scope.d.ts +0 -4
  259. package/dist/triage-scope.js +0 -34
  260. package/dist/triage-smoketest.d.ts +0 -3
  261. package/dist/triage-smoketest.js +0 -43
  262. package/dist/triage-strip-withdrawn.d.ts +0 -21
  263. package/dist/triage-strip-withdrawn.js +0 -102
  264. package/dist/triage-subscribe.d.ts +0 -15
  265. package/dist/triage-subscribe.js +0 -157
  266. package/dist/triage-summary.d.ts +0 -14
  267. package/dist/triage-summary.js +0 -83
  268. package/dist/triage-welcome.d.ts +0 -14
  269. package/dist/triage-welcome.js +0 -121
  270. package/dist/ts-check-lane.d.ts +0 -9
  271. package/dist/ts-check-lane.js +0 -49
  272. package/dist/umbrella-current-shape.d.ts +0 -9
  273. package/dist/umbrella-current-shape.js +0 -56
  274. package/dist/vbrief-activate.d.ts +0 -3
  275. package/dist/vbrief-activate.js +0 -7
  276. package/dist/vbrief-build.d.ts +0 -3
  277. package/dist/vbrief-build.js +0 -8
  278. package/dist/vbrief-preflight-fixtures.d.ts +0 -32
  279. package/dist/vbrief-preflight-fixtures.js +0 -79
  280. package/dist/vbrief-preflight.d.ts +0 -13
  281. package/dist/vbrief-preflight.js +0 -141
  282. package/dist/vbrief-reconcile.d.ts +0 -3
  283. package/dist/vbrief-reconcile.js +0 -7
  284. package/dist/vbrief-validate.d.ts +0 -3
  285. package/dist/vbrief-validate.js +0 -8
  286. package/dist/vbrief-validation.d.ts +0 -3
  287. package/dist/vbrief-validation.js +0 -8
  288. package/dist/verify-ac.d.ts +0 -28
  289. package/dist/verify-ac.js +0 -439
  290. package/dist/verify-agents-md-advisory.d.ts +0 -19
  291. package/dist/verify-agents-md-advisory.js +0 -65
  292. package/dist/verify-agents-md-budget.d.ts +0 -12
  293. package/dist/verify-agents-md-budget.js +0 -71
  294. package/dist/verify-branch.d.ts +0 -14
  295. package/dist/verify-branch.js +0 -81
  296. package/dist/verify-class-checks.d.ts +0 -11
  297. package/dist/verify-class-checks.js +0 -71
  298. package/dist/verify-cohort-review-monitors.d.ts +0 -5
  299. package/dist/verify-cohort-review-monitors.js +0 -11
  300. package/dist/verify-completed-tracked.d.ts +0 -19
  301. package/dist/verify-completed-tracked.js +0 -202
  302. package/dist/verify-completed-write-guard.d.ts +0 -13
  303. package/dist/verify-completed-write-guard.js +0 -84
  304. package/dist/verify-consumer-check-contract.d.ts +0 -12
  305. package/dist/verify-consumer-check-contract.js +0 -70
  306. package/dist/verify-consumer-header-placeholder.d.ts +0 -12
  307. package/dist/verify-consumer-header-placeholder.js +0 -73
  308. package/dist/verify-consumer-test-lane.d.ts +0 -12
  309. package/dist/verify-consumer-test-lane.js +0 -57
  310. package/dist/verify-durable-effect-acquisition.d.ts +0 -13
  311. package/dist/verify-durable-effect-acquisition.js +0 -70
  312. package/dist/verify-encoding.d.ts +0 -15
  313. package/dist/verify-encoding.js +0 -83
  314. package/dist/verify-eval-health-relocation.d.ts +0 -16
  315. package/dist/verify-eval-health-relocation.js +0 -93
  316. package/dist/verify-eval-triggers-relocation.d.ts +0 -14
  317. package/dist/verify-eval-triggers-relocation.js +0 -74
  318. package/dist/verify-evaluator-surface.d.ts +0 -15
  319. package/dist/verify-evaluator-surface.js +0 -87
  320. package/dist/verify-forward-coverage.d.ts +0 -19
  321. package/dist/verify-forward-coverage.js +0 -144
  322. package/dist/verify-hooks-installed.d.ts +0 -24
  323. package/dist/verify-hooks-installed.js +0 -140
  324. package/dist/verify-intent-constraint.d.ts +0 -17
  325. package/dist/verify-intent-constraint.js +0 -110
  326. package/dist/verify-investigation.d.ts +0 -3
  327. package/dist/verify-investigation.js +0 -10
  328. package/dist/verify-judgment-gates.d.ts +0 -3
  329. package/dist/verify-judgment-gates.js +0 -10
  330. package/dist/verify-l4-owner.d.ts +0 -15
  331. package/dist/verify-l4-owner.js +0 -132
  332. package/dist/verify-lifecycle-visible.d.ts +0 -13
  333. package/dist/verify-lifecycle-visible.js +0 -68
  334. package/dist/verify-literal-ac.d.ts +0 -14
  335. package/dist/verify-literal-ac.js +0 -168
  336. package/dist/verify-no-task-runtime.d.ts +0 -3
  337. package/dist/verify-no-task-runtime.js +0 -16
  338. package/dist/verify-observable-scope.d.ts +0 -17
  339. package/dist/verify-observable-scope.js +0 -110
  340. package/dist/verify-operator-scope-limit.d.ts +0 -17
  341. package/dist/verify-operator-scope-limit.js +0 -285
  342. package/dist/verify-orphan-active.d.ts +0 -17
  343. package/dist/verify-orphan-active.js +0 -135
  344. package/dist/verify-ownership.d.ts +0 -10
  345. package/dist/verify-ownership.js +0 -90
  346. package/dist/verify-persisted-planning-narratives.d.ts +0 -12
  347. package/dist/verify-persisted-planning-narratives.js +0 -82
  348. package/dist/verify-plan-sequence.d.ts +0 -14
  349. package/dist/verify-plan-sequence.js +0 -138
  350. package/dist/verify-pr-closeout-attestable.d.ts +0 -15
  351. package/dist/verify-pr-closeout-attestable.js +0 -114
  352. package/dist/verify-presentation-ceiling.d.ts +0 -11
  353. package/dist/verify-presentation-ceiling.js +0 -71
  354. package/dist/verify-presentation-coverage.d.ts +0 -14
  355. package/dist/verify-presentation-coverage.js +0 -90
  356. package/dist/verify-review-monitor.d.ts +0 -32
  357. package/dist/verify-review-monitor.js +0 -362
  358. package/dist/verify-scope-provenance.d.ts +0 -16
  359. package/dist/verify-scope-provenance.js +0 -91
  360. package/dist/verify-session-ritual.d.ts +0 -23
  361. package/dist/verify-session-ritual.js +0 -144
  362. package/dist/verify-source-cli/rule-ownership-lint.d.ts +0 -12
  363. package/dist/verify-source-cli/rule-ownership-lint.js +0 -61
  364. package/dist/verify-source-cli/verify-biome-config.d.ts +0 -11
  365. package/dist/verify-source-cli/verify-biome-config.js +0 -46
  366. package/dist/verify-source-cli/verify-closing-keywords.d.ts +0 -35
  367. package/dist/verify-source-cli/verify-closing-keywords.js +0 -139
  368. package/dist/verify-source-cli/verify-consumer-hard-stops.d.ts +0 -16
  369. package/dist/verify-source-cli/verify-consumer-hard-stops.js +0 -99
  370. package/dist/verify-source-cli/verify-contained-writes.d.ts +0 -13
  371. package/dist/verify-source-cli/verify-contained-writes.js +0 -67
  372. package/dist/verify-source-cli/verify-content-manifest.d.ts +0 -12
  373. package/dist/verify-source-cli/verify-content-manifest.js +0 -61
  374. package/dist/verify-source-cli/verify-contract-drift.d.ts +0 -11
  375. package/dist/verify-source-cli/verify-contract-drift.js +0 -47
  376. package/dist/verify-source-cli/verify-cursor-tier1.d.ts +0 -12
  377. package/dist/verify-source-cli/verify-cursor-tier1.js +0 -51
  378. package/dist/verify-source-cli/verify-deposit-closure.d.ts +0 -12
  379. package/dist/verify-source-cli/verify-deposit-closure.js +0 -52
  380. package/dist/verify-source-cli/verify-openclaw-tier1.d.ts +0 -12
  381. package/dist/verify-source-cli/verify-openclaw-tier1.js +0 -51
  382. package/dist/verify-source-cli/verify-scm-boundary.d.ts +0 -13
  383. package/dist/verify-source-cli/verify-scm-boundary.js +0 -66
  384. package/dist/verify-source-cli/verify-semantic-single-source.d.ts +0 -11
  385. package/dist/verify-source-cli/verify-semantic-single-source.js +0 -49
  386. package/dist/verify-source-cli/verify-skill-external-fetch-gate.d.ts +0 -11
  387. package/dist/verify-source-cli/verify-skill-external-fetch-gate.js +0 -46
  388. package/dist/verify-source-cli/verify-spec-drift.d.ts +0 -3
  389. package/dist/verify-source-cli/verify-spec-drift.js +0 -15
  390. package/dist/verify-source-cli/verify-spec-prd-fresh.d.ts +0 -3
  391. package/dist/verify-source-cli/verify-spec-prd-fresh.js +0 -15
  392. package/dist/verify-source-cli/verify-stubs.d.ts +0 -11
  393. package/dist/verify-source-cli/verify-stubs.js +0 -27
  394. package/dist/verify-source-cli/verify-telemetry-coverage.d.ts +0 -13
  395. package/dist/verify-source-cli/verify-telemetry-coverage.js +0 -67
  396. package/dist/verify-source-cli/verify-xbrief-drift.d.ts +0 -15
  397. package/dist/verify-source-cli/verify-xbrief-drift.js +0 -80
  398. package/dist/verify-story-ready.d.ts +0 -18
  399. package/dist/verify-story-ready.js +0 -190
  400. package/dist/verify-subagent-alive.d.ts +0 -21
  401. package/dist/verify-subagent-alive.js +0 -219
  402. package/dist/verify-subagent-steer.d.ts +0 -19
  403. package/dist/verify-subagent-steer.js +0 -136
  404. package/dist/verify-test-boundary.d.ts +0 -12
  405. package/dist/verify-test-boundary.js +0 -80
  406. package/dist/verify-tools.d.ts +0 -13
  407. package/dist/verify-tools.js +0 -67
  408. package/dist/verify-wip-cap.d.ts +0 -13
  409. package/dist/verify-wip-cap.js +0 -62
  410. package/dist/windows-bin-ps1.d.ts +0 -39
  411. package/dist/windows-bin-ps1.js +0 -226
  412. package/dist/wip-cap-fixtures.d.ts +0 -37
  413. package/dist/wip-cap-fixtures.js +0 -122
  414. package/dist/wsl-ownership-guard.d.ts +0 -2
  415. package/dist/wsl-ownership-guard.js +0 -15
  416. package/dist/xbrief-create.d.ts +0 -3
  417. package/dist/xbrief-create.js +0 -9
  418. package/dist/xbrief-verify.d.ts +0 -3
  419. package/dist/xbrief-verify.js +0 -9
  420. package/scripts/remove-windows-ps1-shims.mjs +0 -26
package/dist/dispatch.js DELETED
@@ -1,3091 +0,0 @@
1
- /**
2
- * Unified `directive <verb> [args]` dispatcher (#1828 s0).
3
- * Routes to ported command modules in packages/cli and packages/core.
4
- */
5
- import { spawnSync } from "node:child_process";
6
- import { createHash } from "node:crypto";
7
- import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, readSync, rmSync, statSync, writeFileSync, } from "node:fs";
8
- import { homedir, tmpdir } from "node:os";
9
- import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
10
- import { engineInfo, userConfig } from "@deftai/directive-core";
11
- import { interceptHelp } from "@deftai/directive-core/dist/triage/help/index.js";
12
- import { parseInitArgv, runInitDepositCli } from "@deftai/directive-core/init-deposit";
13
- import { appendAuditLog, disclosureLine, migrateLegacyPolicyKey, PLAN_POLICY_KEY, POLICY_AUDIT_NOOP_STDOUT, policyColonInvocation, policySetInvocation, projectDefinitionPath, resolvePolicy, resolveWipCap, setPolicy, stampChangedToken, } from "@deftai/directive-core/policy";
14
- import { defaultWhich } from "@deftai/directive-core/scm";
15
- import { KNOWN_SUBAGENT_BACKEND_IDS, probeSubagentBackends, resolveSwarmSubagentBackend, } from "@deftai/directive-core/swarm";
16
- import { withProjectDefinitionMutation, } from "@deftai/directive-core/vbrief-build";
17
- import { identityBanner } from "./identity-banner.js";
18
- const HANDLER_KEYS = [
19
- "run",
20
- "main",
21
- "mainEntry",
22
- "launchMain",
23
- "completeCohortMain",
24
- "readinessMain",
25
- "verifyReviewCleanMain",
26
- "worktreesMain",
27
- ];
28
- /** CLI modules in packages/cli/src (excluding parity harnesses and bin/index). */
29
- export const CLI_MODULE_VERBS = [
30
- "agents-refresh",
31
- "cache",
32
- "check",
33
- "capacity-backfill",
34
- "capacity-show",
35
- "codebase-default-extractor",
36
- "codebase-map",
37
- "codebase-map-fresh",
38
- "codebase-projection-registry",
39
- "codebase-provider",
40
- "doctor",
41
- "ownership-doctor",
42
- "ownership-fix",
43
- "verify-ownership",
44
- "install-upgrade",
45
- "install-uninstall",
46
- "migrate-preflight",
47
- "migrate-xbrief",
48
- "migrate-category-b",
49
- "framework-check-updates",
50
- "hook-dispatch",
51
- "umbrella-current-shape",
52
- "changelog-check",
53
- "change-init",
54
- "commit-lint",
55
- "coverage-hotspots",
56
- "policy",
57
- "authz",
58
- "escalation-cli",
59
- "pr-closing-keywords",
60
- "pr-merge-readiness",
61
- "pr-monitor",
62
- "pr-protected-issues",
63
- "pr-wait-mergeable",
64
- "pr-watch",
65
- "pr-finish-loop",
66
- "directive-finish-loop",
67
- "pre-pr-run",
68
- "preflight-cache",
69
- "preflight-gh",
70
- "probe-session",
71
- "release",
72
- "release-e2e",
73
- "release-publish",
74
- "release-rollback",
75
- "release-wait-npm",
76
- "scope-lifecycle",
77
- "scope-record-approved-scope",
78
- "scope-record-observable-scope",
79
- "scope-record-intent-constraint",
80
- "scm-sync-default",
81
- "lifecycle-event",
82
- "lifecycle-stats",
83
- "session-start",
84
- "session-ready",
85
- "occupancy-steal",
86
- "occupancy-release",
87
- "occupancy-heartbeat",
88
- "occupancy-grant",
89
- "plan-sequence",
90
- "slice",
91
- "subagent-monitor",
92
- "toolchain-check",
93
- "triage-actions",
94
- "triage-bootstrap",
95
- "triage-bulk",
96
- "triage-classify",
97
- "triage-strip-withdrawn",
98
- "triage-evaluate",
99
- "triage-help",
100
- "triage-queue",
101
- "triage-reconcile",
102
- "triage-refresh",
103
- "triage-scope",
104
- "triage-scope-drift",
105
- "triage-smoketest",
106
- "triage-subscribe",
107
- "triage-summary",
108
- "triage-welcome",
109
- "ts-check-lane",
110
- "vbrief-activate",
111
- "vbrief-build",
112
- "vbrief-preflight",
113
- "vbrief-reconcile",
114
- "vbrief-validate",
115
- "vbrief-validation",
116
- "xbrief-create",
117
- "xbrief-verify",
118
- "xbrief-adopt-stored-plan-id",
119
- "verify-branch",
120
- "verify-encoding",
121
- "verify-forward-coverage",
122
- "verify-test-boundary",
123
- "verify-class-checks",
124
- "verify-presentation-coverage",
125
- "verify-scope-provenance",
126
- "verify-consumer-check-contract",
127
- "verify-hooks-installed",
128
- "verify-investigation",
129
- "verify-judgment-gates",
130
- "verify-no-task-runtime",
131
- "validate-links",
132
- "validate-strategy-output",
133
- "verify-biome-config",
134
- "verify-bridge-drift",
135
- "verify-capacity",
136
- "verify-contained-writes",
137
- "verify-telemetry-coverage",
138
- "verify-content-manifest",
139
- "verify-deposit-closure",
140
- "verify-consumer-hard-stops",
141
- "verify-closing-keywords",
142
- "verify-skill-external-fetch-gate",
143
- "verify-semantic-single-source",
144
- "verify-contract-drift",
145
- "verify-cursor-tier1",
146
- "verify-openclaw-tier1",
147
- "verify-go-freeze",
148
- "verify-scm-boundary",
149
- "verify-session-ritual",
150
- "verify-plan-sequence",
151
- "verify-stubs",
152
- "verify-xbrief-drift",
153
- "verify-spec-prd-fresh",
154
- "verify-spec-drift",
155
- "rule-ownership-lint",
156
- "verify-story-ready",
157
- "verify-review-monitor",
158
- "verify-cohort-review-monitors",
159
- "verify-l4-owner",
160
- "verify-subagent-alive",
161
- "verify-subagent-steer",
162
- "subagent-pre-cancel",
163
- "subagent-steer-write",
164
- "design-critique-spend-resolve",
165
- "review-monitor-register",
166
- "review-monitor-release",
167
- "verify-tools",
168
- "verify-wip-cap",
169
- "verify-orphan-active",
170
- "verify-pr-closeout-attestable",
171
- "verify-lifecycle-visible",
172
- "verify-completed-tracked",
173
- "verify-completed-write-guard",
174
- "verify-literal-ac",
175
- "verify-ac",
176
- "verify-agents-md-budget",
177
- "verify-consumer-header-placeholder",
178
- "verify-agents-md-advisory",
179
- "verify-persisted-planning-narratives",
180
- "verify-eval-health-relocation",
181
- "verify-eval-triggers-relocation",
182
- "verify-evaluator-surface",
183
- "verify-observable-scope",
184
- "verify-operator-scope-limit",
185
- "verify-presentation-ceiling",
186
- "verify-intent-constraint",
187
- "verify-durable-effect-acquisition",
188
- "verify-consumer-test-lane",
189
- "eval-health",
190
- "eval-run",
191
- "eval-report",
192
- "eval-triggers",
193
- ];
194
- /** Core-only CLI entrypoints without a packages/cli wrapper. */
195
- export const CORE_MODULE_VERBS = [
196
- "scm",
197
- "scm-readiness",
198
- "github-auth-modes",
199
- "github-body",
200
- "issue-emit",
201
- "issue-ingest",
202
- "issue-sync-from-xbrief",
203
- "reconcile-issues",
204
- "swarm-launch",
205
- "swarm-complete-cohort",
206
- "swarm-finalize-cohort",
207
- "swarm-finalize-owed",
208
- "swarm-pre-dispatch",
209
- "swarm-readiness",
210
- "swarm-routing-verify",
211
- "swarm-routing-set",
212
- "swarm-verify-review-clean",
213
- "swarm-worktrees",
214
- "framework-commands",
215
- "pack-render",
216
- "packs-slice",
217
- "prd-render",
218
- "export-spec",
219
- "project-render",
220
- "project-write-narratives",
221
- "roadmap-render",
222
- "rule-map",
223
- "spec-render",
224
- "spec-reconstruct",
225
- "spec-validate",
226
- "code-structure-validate",
227
- "pack-migrate-skills",
228
- "pack-migrate-rules",
229
- "pack-migrate-strategies",
230
- "pack-migrate-patterns",
231
- "pack-migrate-swarm-spec",
232
- "policy-set",
233
- "setup-ghx",
234
- "scope-undo",
235
- "scope-demote",
236
- "scope-decompose",
237
- "changelog-resolve-unreleased",
238
- "architecture-preflight-sor",
239
- "feedback-file",
240
- "value-readback",
241
- "product-signal",
242
- "freshness-report",
243
- "decision-write",
244
- "decision-list",
245
- "docs-impact",
246
- "tutorial",
247
- "migrate-clause-ids",
248
- "migrate-confidence",
249
- "rule-to-verb-parity",
250
- ];
251
- /** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
252
- export const TRIAGE_ACTION_ALIAS_SUBCOMMANDS = {
253
- "triage:accept": "accept",
254
- "triage:reject": "reject",
255
- "triage:defer": "defer",
256
- "triage:needs-ac": "needs-ac",
257
- "triage:mark-duplicate": "mark-duplicate",
258
- "triage:status": "status",
259
- "triage:reset": "reset",
260
- "triage:history": "history",
261
- };
262
- const TRIAGE_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(TRIAGE_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "triage-actions"]));
263
- /** Colon aliases for policy subcommands (mirrors cli-router SUBCOMMAND_ROUTES). */
264
- export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
265
- "policy:show": "show",
266
- "policy:enforce-branches": "enforce-branches",
267
- "policy:allow-direct-commits": "allow-direct-commits",
268
- "policy:allow-destructive-gh-verbs": "allow-destructive-gh-verbs",
269
- "policy:enforce-destructive-gh-verbs": "enforce-destructive-gh-verbs",
270
- "policy:allow-bot-merge": "allow-bot-merge",
271
- "policy:enable-value-feedback": "enable-value-feedback",
272
- "policy:set-spec-guard-enforcement": "set-spec-guard-enforcement",
273
- "policy:disable-host-hooks": "disable-host-hooks",
274
- "policy:set-ceremony-dial": "set-ceremony-dial",
275
- "policy:clear-value-feedback": "clear-value-feedback",
276
- "policy:disable-directive": "disable-directive",
277
- "policy:enable-directive": "enable-directive",
278
- };
279
- const POLICY_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(POLICY_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "policy"]));
280
- /** Colon aliases for authz subcommands (#2944). */
281
- export const AUTHZ_ACTION_ALIAS_SUBCOMMANDS = {
282
- "authz:show": "show",
283
- "authz:uat-start": "uat-start",
284
- "authz:uat-suspend": "uat-suspend",
285
- "authz:grant": "grant",
286
- "authz:revoke": "revoke",
287
- };
288
- const AUTHZ_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(AUTHZ_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "authz"]));
289
- /** Colon aliases for escalation subcommands (#518). */
290
- export const ESCALATION_ACTION_ALIAS_SUBCOMMANDS = {
291
- "escalation:file": "file",
292
- "escalation:list": "list",
293
- "escalation:resolve": "resolve",
294
- "escalation:batch-approve": "batch-approve",
295
- };
296
- const ESCALATION_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(ESCALATION_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "escalation-cli"]));
297
- /** Colon aliases for plan-sequence subcommands (#2402). */
298
- export const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS = {
299
- "plan-sequence:set": "set",
300
- "plan-sequence:current": "current",
301
- "plan-sequence:clear": "clear",
302
- "plan-sequence:advance": "advance",
303
- };
304
- const PLAN_SEQUENCE_COLON_ALIASES = Object.fromEntries(Object.keys(PLAN_SEQUENCE_ALIAS_SUBCOMMANDS).map((alias) => [alias, "plan-sequence"]));
305
- /** Colon aliases for the Directive Tutorial (#4981). */
306
- export const TUTORIAL_ALIAS_SUBCOMMANDS = {
307
- "tutorial:offer": "offer",
308
- "tutorial:start": "start",
309
- "tutorial:inspect": "inspect",
310
- "tutorial:advance": "advance",
311
- "tutorial:resume": "resume",
312
- "tutorial:skip": "skip",
313
- "tutorial:leave": "leave",
314
- "tutorial:reset": "reset",
315
- "tutorial:decline": "decline",
316
- "tutorial:defer": "defer",
317
- };
318
- const TUTORIAL_COLON_ALIASES = Object.fromEntries(Object.keys(TUTORIAL_ALIAS_SUBCOMMANDS).map((alias) => [alias, "tutorial"]));
319
- /** Colon aliases for product-signal subcommands (#2693). */
320
- export const PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS = {
321
- "product-signal:status": "status",
322
- "product-signal:enable": "enable",
323
- "product-signal:consent": "consent",
324
- "product-signal:submit": "submit",
325
- "product-signal:bootstrap-sink": "bootstrap-sink",
326
- };
327
- const PRODUCT_SIGNAL_COLON_ALIASES = Object.fromEntries(Object.keys(PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS).map((alias) => [alias, "product-signal"]));
328
- /** Colon aliases for freshness subcommands (#3117). */
329
- export const FRESHNESS_ALIAS_SUBCOMMANDS = {
330
- "freshness:report": "report",
331
- "freshness:bind": "bind",
332
- "session:freshness": "report",
333
- freshness: "report",
334
- };
335
- const FRESHNESS_COLON_ALIASES = Object.fromEntries(Object.keys(FRESHNESS_ALIAS_SUBCOMMANDS).map((alias) => [alias, "freshness-report"]));
336
- /** Task-style aliases (framework_commands / Taskfile names). */
337
- export const VERB_ALIASES = {
338
- "pre-pr:run": "pre-pr-run",
339
- "hook:dispatch": "hook-dispatch",
340
- "verify:encoding": "verify-encoding",
341
- "verify:forward-coverage": "verify-forward-coverage",
342
- "verify:test-boundary": "verify-test-boundary",
343
- "verify:class-checks": "verify-class-checks",
344
- "verify:presentation-ceiling": "verify-presentation-ceiling",
345
- "verify:presentation-coverage": "verify-presentation-coverage",
346
- "verify:scope-provenance": "verify-scope-provenance",
347
- "scope:record-approved-scope": "scope-record-approved-scope",
348
- "scope:record-observable-scope": "scope-record-observable-scope",
349
- "scope:record-intent-constraint": "scope-record-intent-constraint",
350
- "verify:consumer-check-contract": "verify-consumer-check-contract",
351
- "coverage:hotspots": "coverage-hotspots",
352
- "verify:branch": "verify-branch",
353
- "verify:vbrief-conformance": "vbrief-validate",
354
- "verify:wip-cap": "verify-wip-cap",
355
- "verify:orphan-active": "verify-orphan-active",
356
- "verify:pr-closeout-attestable": "verify-pr-closeout-attestable",
357
- "verify:lifecycle-visible": "verify-lifecycle-visible",
358
- "verify:completed-tracked": "verify-completed-tracked",
359
- "verify:completed-write-guard": "verify-completed-write-guard",
360
- "verify:literal-ac": "verify-literal-ac",
361
- "verify:literal-acceptance": "verify-literal-ac",
362
- "verify:ac": "verify-ac",
363
- "verify:acceptance": "verify-ac",
364
- "verify:agents-md-budget": "verify-agents-md-budget",
365
- "verify:consumer-header-placeholder": "verify-consumer-header-placeholder",
366
- "verify:agents-md-advisory": "verify-agents-md-advisory",
367
- "verify:persisted-planning-narratives": "verify-persisted-planning-narratives",
368
- "verify:eval-health-relocation": "verify-eval-health-relocation",
369
- "verify:eval-triggers-relocation": "verify-eval-triggers-relocation",
370
- "verify:evaluator-surface": "verify-evaluator-surface",
371
- "verify:observable-scope": "verify-observable-scope",
372
- "verify:operator-scope-limit": "verify-operator-scope-limit",
373
- "verify:intent-constraint": "verify-intent-constraint",
374
- "verify:durable-effect-acquisition": "verify-durable-effect-acquisition",
375
- "verify:consumer-test-lane": "verify-consumer-test-lane",
376
- "verify:hooks-installed": "verify-hooks-installed",
377
- "verify:no-task-runtime": "verify-no-task-runtime",
378
- "vbrief:validate": "vbrief-validate",
379
- "vbrief:preflight": "vbrief-preflight",
380
- // xbrief:-prefixed aliases for the canonical post-#2109 lifecycle spelling.
381
- // xbrief:validate closes the #3483 gap: the swarm skill named it at MUST
382
- // level but only vbrief:validate resolved.
383
- "xbrief:validate": "vbrief-validate",
384
- "xbrief:activate": "vbrief-activate",
385
- "xbrief:preflight": "vbrief-preflight",
386
- "xbrief:create": "xbrief-create",
387
- "xbrief:verify": "xbrief-verify",
388
- "xbrief:adopt-stored-plan-id": "xbrief-adopt-stored-plan-id",
389
- "vbrief:activate": "vbrief-activate",
390
- "verify:story-ready": "verify-story-ready",
391
- "verify:review-monitor": "verify-review-monitor",
392
- "verify:cohort-review-monitors": "verify-cohort-review-monitors",
393
- "verify:l4-owner": "verify-l4-owner",
394
- "verify:subagent-alive": "verify-subagent-alive",
395
- "verify:subagent-steer": "verify-subagent-steer",
396
- "subagent:pre-cancel": "subagent-pre-cancel",
397
- "subagent:steer": "subagent-steer-write",
398
- "design-critique:spend-resolve": "design-critique-spend-resolve",
399
- "agent:monitor": "subagent-monitor",
400
- "review-monitor:register": "review-monitor-register",
401
- "review-monitor:release": "review-monitor-release",
402
- "verify:tools": "verify-tools",
403
- "verify:investigation": "verify-investigation",
404
- "verify:judgment-gates": "verify-judgment-gates",
405
- "verify:stubs": "verify-stubs",
406
- "verify:links": "validate-links",
407
- "validate:links": "validate-links",
408
- "verify:rule-ownership": "rule-ownership-lint",
409
- "rule:ownership-lint": "rule-ownership-lint",
410
- "verify:rule-to-verb-parity": "rule-to-verb-parity",
411
- "verify:biome-config": "verify-biome-config",
412
- "verify:contained-writes": "verify-contained-writes",
413
- "verify:telemetry-coverage": "verify-telemetry-coverage",
414
- "verify:content-manifest": "verify-content-manifest",
415
- "verify:deposit-closure": "verify-deposit-closure",
416
- "verify:consumer-hard-stops": "verify-consumer-hard-stops",
417
- "verify:closing-keywords": "verify-closing-keywords",
418
- "verify:docs-impact": "docs-impact",
419
- "verify:skill-external-fetch-gate": "verify-skill-external-fetch-gate",
420
- "verify:semantic-single-source": "verify-semantic-single-source",
421
- "verify:contract-drift": "verify-contract-drift",
422
- "verify:cursor-tier1": "verify-cursor-tier1",
423
- "verify:openclaw-tier1": "verify-openclaw-tier1",
424
- "verify:go-freeze": "verify-go-freeze",
425
- "verify:bridge-drift": "verify-bridge-drift",
426
- "verify:scm-boundary": "verify-scm-boundary",
427
- "verify:xbrief-drift": "verify-xbrief-drift",
428
- "verify:spec-prd-fresh": "verify-spec-prd-fresh",
429
- "verify:spec-drift": "verify-spec-drift",
430
- "verify:capacity": "verify-capacity",
431
- "verify:session-ritual": "verify-session-ritual",
432
- "verify:plan-sequence": "verify-plan-sequence",
433
- ...PLAN_SEQUENCE_COLON_ALIASES,
434
- ...TUTORIAL_COLON_ALIASES,
435
- "verify-strategy-output": "validate-strategy-output",
436
- "validate:strategy-output": "validate-strategy-output",
437
- "verify:codebase-map-fresh": "codebase-map-fresh",
438
- "codebase:map": "codebase-map",
439
- "triage:welcome": "triage-welcome",
440
- "triage:bootstrap": "triage-bootstrap",
441
- "triage:summary": "triage-summary",
442
- "triage:queue": "triage-queue",
443
- "triage:evaluate": "triage-evaluate",
444
- "triage:scope": "triage-scope",
445
- ...TRIAGE_ACTION_COLON_ALIASES,
446
- ...POLICY_ACTION_COLON_ALIASES,
447
- ...AUTHZ_ACTION_COLON_ALIASES,
448
- ...ESCALATION_ACTION_COLON_ALIASES,
449
- ...PRODUCT_SIGNAL_COLON_ALIASES,
450
- "agents:refresh": "agents-refresh",
451
- "migrate:preflight": "migrate-preflight",
452
- "migrate:xbrief": "migrate-xbrief",
453
- "migrate:category-b": "migrate-category-b",
454
- "migrate:clause-ids": "migrate-clause-ids",
455
- "migrate:confidence": "migrate-confidence",
456
- "framework:check-updates": "framework-check-updates",
457
- "umbrella:current-shape": "umbrella-current-shape",
458
- "issue:sync-from-xbrief": "issue-sync-from-xbrief",
459
- upgrade: "install-upgrade",
460
- "session:start": "session-start",
461
- "session:ready": "session-ready",
462
- "occupancy:steal": "occupancy-steal",
463
- "occupancy:release": "occupancy-release",
464
- "occupancy:heartbeat": "occupancy-heartbeat",
465
- "occupancy:grant": "occupancy-grant",
466
- "session:end": "occupancy-release",
467
- ...FRESHNESS_COLON_ALIASES,
468
- "lifecycle:event": "lifecycle-event",
469
- "lifecycle:stats": "lifecycle-stats",
470
- "toolchain:check": "toolchain-check",
471
- "ts:check-lane": "ts-check-lane",
472
- "spec:validate": "spec-validate",
473
- "spec:render": "spec-render",
474
- "spec:reconstruct": "spec-reconstruct",
475
- "prd:render": "prd-render",
476
- "project:render": "project-render",
477
- "project:write-narratives": "project-write-narratives",
478
- "docs:rule-map": "rule-map",
479
- "project:export-spec": "export-spec",
480
- "pr:watch": "pr-watch",
481
- "pr:finish-loop": "pr-finish-loop",
482
- "directive:finish-loop": "directive-finish-loop",
483
- doctor: "doctor",
484
- "ownership:doctor": "ownership-doctor",
485
- "ownership:fix": "ownership-fix",
486
- "verify:ownership": "verify-ownership",
487
- "deft:ownership:doctor": "ownership-doctor",
488
- "deft:ownership:fix": "ownership-fix",
489
- "deft:verify:ownership": "verify-ownership",
490
- "eval:health": "eval-health",
491
- "feedback:file": "feedback-file",
492
- "value:show": "value-readback",
493
- "triage:metrics": "value-readback",
494
- "decision:write": "decision-write",
495
- "decision:list": "decision-list",
496
- "eval:run": "eval-run",
497
- "eval:triggers": "eval-triggers",
498
- "eval:report": "eval-report",
499
- build: "framework-commands",
500
- "setup:ghx": "setup-ghx",
501
- "setup:seed": "setup-seed",
502
- "scm:status": "scm-readiness",
503
- "scm:readiness": "scm-readiness",
504
- "scm:sync-default": "scm-sync-default",
505
- "release:wait-npm": "release-wait-npm",
506
- };
507
- /** CLI modules living under verify-source-cli/ or content-validate-cli/ subdirs. */
508
- const SUBDIR_CLI_STEMS = {
509
- "verify-stubs": "verify-source-cli/verify-stubs",
510
- "rule-ownership-lint": "verify-source-cli/rule-ownership-lint",
511
- "verify-biome-config": "verify-source-cli/verify-biome-config",
512
- "verify-contained-writes": "verify-source-cli/verify-contained-writes",
513
- "verify-telemetry-coverage": "verify-source-cli/verify-telemetry-coverage",
514
- "verify-content-manifest": "verify-source-cli/verify-content-manifest",
515
- "verify-deposit-closure": "verify-source-cli/verify-deposit-closure",
516
- "verify-consumer-hard-stops": "verify-source-cli/verify-consumer-hard-stops",
517
- "verify-closing-keywords": "verify-source-cli/verify-closing-keywords",
518
- "verify-skill-external-fetch-gate": "verify-source-cli/verify-skill-external-fetch-gate",
519
- "verify-semantic-single-source": "verify-source-cli/verify-semantic-single-source",
520
- "verify-contract-drift": "verify-source-cli/verify-contract-drift",
521
- "verify-cursor-tier1": "verify-source-cli/verify-cursor-tier1",
522
- "verify-openclaw-tier1": "verify-source-cli/verify-openclaw-tier1",
523
- "verify-scm-boundary": "verify-source-cli/verify-scm-boundary",
524
- "verify-xbrief-drift": "verify-source-cli/verify-xbrief-drift",
525
- "verify-spec-prd-fresh": "verify-source-cli/verify-spec-prd-fresh",
526
- "verify-spec-drift": "verify-source-cli/verify-spec-drift",
527
- "verify-go-freeze": "gates-cli/verify-go-freeze",
528
- "verify-bridge-drift": "gates-cli/verify-bridge-drift",
529
- "validate-links": "content-validate-cli/validate-links",
530
- "verify-capacity": "content-validate-cli/verify-capacity",
531
- "validate-strategy-output": "content-validate-cli/validate-strategy-output",
532
- };
533
- const WRAPPER_CLI_STEMS = new Set([
534
- "capacity-backfill",
535
- "capacity-show",
536
- "codebase-default-extractor",
537
- "codebase-map",
538
- "codebase-map-fresh",
539
- "codebase-projection-registry",
540
- "codebase-provider",
541
- "vbrief-activate",
542
- "vbrief-build",
543
- "vbrief-reconcile",
544
- "vbrief-validate",
545
- "vbrief-validation",
546
- "xbrief-create",
547
- "xbrief-verify",
548
- "xbrief-adopt-stored-plan-id",
549
- ]);
550
- function emitCliResult(result, io) {
551
- if (result.stdout)
552
- io.writeOut(result.stdout);
553
- if (result.stderr)
554
- io.writeErr(result.stderr);
555
- return result.exitCode;
556
- }
557
- function resolveHandler(mod) {
558
- for (const key of HANDLER_KEYS) {
559
- const fn = mod[key];
560
- if (typeof fn === "function") {
561
- return fn;
562
- }
563
- }
564
- return null;
565
- }
566
- async function loadWrapperCliHandler(stem, io) {
567
- switch (stem) {
568
- case "capacity-backfill": {
569
- const { runCapacityBackfillCli } = await import("@deftai/directive-core/capacity");
570
- return async (argv) => emitCliResult(await runCapacityBackfillCli(argv), io);
571
- }
572
- case "capacity-show": {
573
- const { runCapacityShowCli } = await import("@deftai/directive-core/capacity");
574
- return (argv) => emitCliResult(runCapacityShowCli(argv), io);
575
- }
576
- case "codebase-default-extractor": {
577
- const { runDefaultExtractorCli } = await import("@deftai/directive-core/codebase");
578
- return (argv) => emitCliResult(runDefaultExtractorCli(argv), io);
579
- }
580
- case "codebase-map": {
581
- const { runCodebaseMapCli } = await import("@deftai/directive-core/codebase");
582
- return (argv) => emitCliResult(runCodebaseMapCli(argv), io);
583
- }
584
- case "codebase-map-fresh": {
585
- const { runCodebaseMapFreshCli } = await import("@deftai/directive-core/codebase");
586
- return (argv) => emitCliResult(runCodebaseMapFreshCli(argv), io);
587
- }
588
- case "codebase-projection-registry": {
589
- const { runProjectionRegistryCli } = await import("@deftai/directive-core/codebase");
590
- return (argv) => emitCliResult(runProjectionRegistryCli(argv), io);
591
- }
592
- case "codebase-provider": {
593
- const { runProviderCli } = await import("@deftai/directive-core/codebase");
594
- return (argv) => emitCliResult(runProviderCli(argv), io);
595
- }
596
- case "vbrief-activate": {
597
- const { run } = await import("@deftai/directive-core/vbrief-activate");
598
- return run;
599
- }
600
- case "vbrief-build": {
601
- const { cmdVbriefBuild } = await import("@deftai/directive-core/vbrief-build");
602
- return cmdVbriefBuild;
603
- }
604
- case "vbrief-reconcile": {
605
- const { cmdVbriefReconcile } = await import("@deftai/directive-core/vbrief-reconcile");
606
- return cmdVbriefReconcile;
607
- }
608
- case "vbrief-validate": {
609
- const { cmdVbriefValidate } = await import("@deftai/directive-core/vbrief-validate");
610
- return cmdVbriefValidate;
611
- }
612
- case "vbrief-validation": {
613
- const { cmdVbriefValidation } = await import("@deftai/directive-core/vbrief-validation");
614
- return cmdVbriefValidation;
615
- }
616
- case "xbrief-create": {
617
- const { runXbriefCreateCli } = await import("@deftai/directive-core/xbrief");
618
- return (argv) => emitCliResult(runXbriefCreateCli(argv), io);
619
- }
620
- case "xbrief-verify": {
621
- const { runXbriefVerifyCli } = await import("@deftai/directive-core/xbrief");
622
- return (argv) => emitCliResult(runXbriefVerifyCli(argv), io);
623
- }
624
- case "xbrief-adopt-stored-plan-id": {
625
- const { runAdoptStoredPlanIdCli } = await import("@deftai/directive-core/xbrief");
626
- return (argv) => emitCliResult(runAdoptStoredPlanIdCli(argv), io);
627
- }
628
- default:
629
- throw new Error(`no wrapper handler for ${stem}`);
630
- }
631
- }
632
- async function loadCliModuleHandler(stem, io) {
633
- if (WRAPPER_CLI_STEMS.has(stem)) {
634
- return loadWrapperCliHandler(stem, io);
635
- }
636
- const subdir = SUBDIR_CLI_STEMS[stem];
637
- const modulePath = subdir !== undefined ? `./${subdir}.js` : `./${stem}.js`;
638
- const mod = (await import(modulePath));
639
- const handler = resolveHandler(mod);
640
- if (handler === null) {
641
- throw new Error(`module ${stem} has no command handler export`);
642
- }
643
- return handler;
644
- }
645
- function resolveDeftRoot() {
646
- if (process.env.DEFT_ROOT !== undefined && process.env.DEFT_ROOT.length > 0) {
647
- return resolve(process.env.DEFT_ROOT);
648
- }
649
- return resolve(import.meta.dirname, "..", "..", "..");
650
- }
651
- function parseCodeStructureArgs(argv) {
652
- let projectRoot = ".";
653
- const paths = [];
654
- let json = false;
655
- let strict = false;
656
- let enforce = false;
657
- for (let i = 0; i < argv.length; i += 1) {
658
- const arg = argv[i];
659
- if (arg === "--project-root") {
660
- const v = argv[i + 1];
661
- if (v === undefined)
662
- return {
663
- projectRoot,
664
- paths,
665
- json,
666
- strict,
667
- enforce,
668
- error: "missing --project-root value",
669
- };
670
- projectRoot = v;
671
- i += 1;
672
- }
673
- else if (arg?.startsWith("--project-root=")) {
674
- projectRoot = arg.slice("--project-root=".length);
675
- }
676
- else if (arg === "--path") {
677
- const v = argv[i + 1];
678
- if (v === undefined)
679
- return { projectRoot, paths, json, strict, enforce, error: "missing --path value" };
680
- paths.push(v);
681
- i += 1;
682
- }
683
- else if (arg?.startsWith("--path=")) {
684
- paths.push(arg.slice("--path=".length));
685
- }
686
- else if (arg === "--json") {
687
- json = true;
688
- }
689
- else if (arg === "--strict") {
690
- strict = true;
691
- }
692
- else if (arg === "--enforce") {
693
- enforce = true;
694
- }
695
- else if (arg !== undefined && !arg.startsWith("-")) {
696
- paths.push(arg);
697
- }
698
- else {
699
- return {
700
- projectRoot,
701
- paths,
702
- json,
703
- strict,
704
- enforce,
705
- error: `unrecognized argument: ${arg}`,
706
- };
707
- }
708
- }
709
- return { projectRoot, paths, json, strict, enforce };
710
- }
711
- // ===========================================================================
712
- // Native pack-migrate handlers (#2022 Phase 1).
713
- //
714
- // Port of scripts/pack_migrate_{skills,rules,strategies,patterns,swarm_spec}.py
715
- // to native TypeScript so the pack-render surface no longer shells into bundled
716
- // Python. Output parity with the Python scripts is exact, including the
717
- // json.dumps(..., indent=2, ensure_ascii=True) + "\n" serialization, document
718
- // scanning order, and per-entry field ordering.
719
- // ===========================================================================
720
- const PACK_VERSION = "0.1";
721
- const DEFAULT_SKILL_VERSION = "0.1";
722
- const SHOULD_NOT_GLYPH = "\u2249";
723
- const MUST_NOT_GLYPH = "\u2297";
724
- /** Serialize like Python json.dumps(value, indent=2, ensure_ascii=True) + "\n". */
725
- function dumpsAsciiJson(value) {
726
- const base = JSON.stringify(value, null, 2);
727
- let out = "";
728
- for (let i = 0; i < base.length; i += 1) {
729
- const code = base.charCodeAt(i);
730
- // ensure_ascii escapes every code unit outside the printable ASCII range
731
- // (0x20-0x7e). JSON.stringify has already escaped control chars (< 0x20)
732
- // and the structural quote/backslash, so only chars > 0x7e remain literal.
733
- if (code > 0x7e) {
734
- out += `\\u${code.toString(16).padStart(4, "0")}`;
735
- }
736
- else {
737
- out += base.charAt(i);
738
- }
739
- }
740
- return `${out}\n`;
741
- }
742
- /** Strip leading/trailing chars in `chars` (Python str.strip(chars)); whitespace when omitted. */
743
- function pyStrip(value, chars) {
744
- if (chars === undefined) {
745
- return value.replace(/^\s+/, "").replace(/\s+$/, "");
746
- }
747
- let start = 0;
748
- let end = value.length;
749
- while (start < end && chars.includes(value.charAt(start)))
750
- start += 1;
751
- while (end > start && chars.includes(value.charAt(end - 1)))
752
- end -= 1;
753
- return value.slice(start, end);
754
- }
755
- // Python str.splitlines() universal newlines: \n \r \r\n \v \f \x1c \x1d \x1e \x85 \u2028 \u2029.
756
- // Built from code points (as \uXXXX escape text) so no literal control characters land in the source.
757
- const LINE_BOUNDARY_CLASS = [0x0a, 0x0d, 0x0b, 0x0c, 0x1c, 0x1d, 0x1e, 0x85, 0x2028, 0x2029]
758
- .map((code) => `\\u${code.toString(16).padStart(4, "0")}`)
759
- .join("");
760
- const LINE_BOUNDARY_RE = new RegExp(`\\r\\n|[${LINE_BOUNDARY_CLASS}]`);
761
- /** Mirror Python str.splitlines(): split on universal line boundaries, dropping one terminal break. */
762
- function splitLines(text) {
763
- if (text === "")
764
- return [];
765
- const parts = text.split(LINE_BOUNDARY_RE);
766
- if (parts.length > 0 && parts[parts.length - 1] === "")
767
- parts.pop();
768
- return parts;
769
- }
770
- /** Repo-relative POSIX path of `to` measured from `from`. */
771
- function relPosix(from, to) {
772
- return relative(from, to).split(/[\\/]/).join("/");
773
- }
774
- /** Python Path.stem -- filename minus its final suffix. */
775
- function stemOf(filePath) {
776
- const base = basename(filePath);
777
- const dot = base.lastIndexOf(".");
778
- return dot > 0 ? base.slice(0, dot) : base;
779
- }
780
- /** Slugify a doc stem: lowercase, runs of non-alnum -> '-', trimmed of '-'. */
781
- function slugify(stem) {
782
- return pyStrip(stem.toLowerCase().replace(/[^a-z0-9]+/g, "-"), "-");
783
- }
784
- function isFileSafe(path) {
785
- try {
786
- return statSync(path).isFile();
787
- }
788
- catch {
789
- return false;
790
- }
791
- }
792
- function isDirSafe(path) {
793
- try {
794
- return statSync(path).isDirectory();
795
- }
796
- catch {
797
- return false;
798
- }
799
- }
800
- /** Sorted SKILL.md paths one directory below skillsDir (Python skills_dir glob of the SKILL.md docs). */
801
- function globSkillMd(skillsDir) {
802
- const out = [];
803
- let names;
804
- try {
805
- names = readdirSync(skillsDir);
806
- }
807
- catch {
808
- return out;
809
- }
810
- for (const name of names) {
811
- const dir = join(skillsDir, name);
812
- if (!isDirSafe(dir))
813
- continue;
814
- const candidate = join(dir, "SKILL.md");
815
- if (isFileSafe(candidate))
816
- out.push(candidate);
817
- }
818
- out.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
819
- return out;
820
- }
821
- /** Sorted full paths of `<dir>/*.md` (Python dir.glob("*.md")). */
822
- function globMd(dir) {
823
- const out = [];
824
- let names;
825
- try {
826
- names = readdirSync(dir);
827
- }
828
- catch {
829
- return out;
830
- }
831
- for (const name of names) {
832
- if (!name.endsWith(".md"))
833
- continue;
834
- const candidate = join(dir, name);
835
- if (isFileSafe(candidate))
836
- out.push(candidate);
837
- }
838
- out.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
839
- return out;
840
- }
841
- const H1_RE = /^#\s+(.+?)\s*$/;
842
- const CHROME_PREFIXES = [
843
- "legend ",
844
- "legend(",
845
- "**legend",
846
- `**${"\u26a0\ufe0f"}`,
847
- "**see also",
848
- "<!--",
849
- ];
850
- function isChrome(line) {
851
- const low = line.replace(/^\s+/, "").toLowerCase();
852
- if (CHROME_PREFIXES.some((prefix) => low.startsWith(prefix)))
853
- return true;
854
- const stripped = line.trim();
855
- return stripped.length > 0 && [...stripped].every((ch) => ch === "-" || ch === "=");
856
- }
857
- /** Index a line array with a defined fallback (`i` is always in range at call sites). */
858
- function lineAt(lines, i) {
859
- return lines[i] ?? "";
860
- }
861
- function extractTitle(md) {
862
- for (const line of splitLines(md)) {
863
- const match = H1_RE.exec(line);
864
- if (match)
865
- return (match[1] ?? "").trim();
866
- }
867
- return "";
868
- }
869
- function extractDescription(md) {
870
- const lines = splitLines(md);
871
- const n = lines.length;
872
- let i = 0;
873
- while (i < n && !H1_RE.test(lineAt(lines, i)))
874
- i += 1;
875
- if (i < n)
876
- i += 1;
877
- while (i < n && (lineAt(lines, i).trim() === "" || isChrome(lineAt(lines, i))))
878
- i += 1;
879
- const block = [];
880
- while (i < n &&
881
- lineAt(lines, i).trim() !== "" &&
882
- !lineAt(lines, i).replace(/^\s+/, "").startsWith("#")) {
883
- let stripped = lineAt(lines, i).trim();
884
- if (stripped.startsWith(">"))
885
- stripped = stripped.replace(/^>+/, "").trim();
886
- if (stripped)
887
- block.push(stripped);
888
- i += 1;
889
- }
890
- return block.join(" ");
891
- }
892
- const REDIRECT_MARKERS = [
893
- "legacy alias",
894
- "superseded",
895
- "has been renamed",
896
- "has moved",
897
- "deprecated",
898
- ];
899
- function isRedirectStub(md) {
900
- const lines = splitLines(md);
901
- const n = lines.length;
902
- let i = 0;
903
- while (i < n && !H1_RE.test(lineAt(lines, i)))
904
- i += 1;
905
- if (i < n)
906
- i += 1;
907
- while (i < n && (lineAt(lines, i).trim() === "" || isChrome(lineAt(lines, i))))
908
- i += 1;
909
- if (i >= n || !lineAt(lines, i).replace(/^\s+/, "").startsWith(">"))
910
- return false;
911
- const block = [];
912
- while (i < n && lineAt(lines, i).replace(/^\s+/, "").startsWith(">")) {
913
- block.push(lineAt(lines, i).replace(/^\s+/, "").replace(/^>+/, "").trim());
914
- i += 1;
915
- }
916
- const quote = block.join(" ").toLowerCase();
917
- return REDIRECT_MARKERS.some((marker) => quote.includes(marker));
918
- }
919
- const BANNER_OPEN = "<!-- AUTO-GENERATED by task packs:render";
920
- function stripLeadingBanner(body) {
921
- const lines = body.split("\n");
922
- const n = lines.length;
923
- let i = 0;
924
- while (i < n && lineAt(lines, i).trim() === "")
925
- i += 1;
926
- if (i < n && lineAt(lines, i).startsWith(BANNER_OPEN)) {
927
- while (i < n && lineAt(lines, i).replace(/^\s+/, "").startsWith("<!--"))
928
- i += 1;
929
- while (i < n && lineAt(lines, i).trim() === "")
930
- i += 1;
931
- }
932
- return lines.slice(i).join("\n");
933
- }
934
- const FRONTMATTER_RE = /^---\n([\s\S]*?\n)---\n?([\s\S]*)$/;
935
- function splitFrontmatter(text) {
936
- if (!text.startsWith("---\n"))
937
- return [null, text];
938
- const match = FRONTMATTER_RE.exec(text);
939
- if (!match)
940
- return [null, text];
941
- return [match[1] ?? "", match[2] ?? ""];
942
- }
943
- function foldBlock(blockLines) {
944
- const paragraphs = [];
945
- let current = [];
946
- for (const line of blockLines) {
947
- if (line.trim() === "") {
948
- if (current.length) {
949
- paragraphs.push(current.join(" "));
950
- current = [];
951
- }
952
- }
953
- else {
954
- current.push(line.trim());
955
- }
956
- }
957
- if (current.length)
958
- paragraphs.push(current.join(" "));
959
- return paragraphs.join("\n");
960
- }
961
- const KEY_RE = /^([A-Za-z_][\w-]*):(.*)$/;
962
- const BLOCK_INDICATORS = new Set([">", ">-", ">+", "|", "|-", "|+"]);
963
- function isIndented(line) {
964
- return line.startsWith(" ") || line.startsWith("\t");
965
- }
966
- function parseFrontmatterFields(frontmatter) {
967
- const lines = frontmatter.split("\n");
968
- const fields = {};
969
- const n = lines.length;
970
- let i = 0;
971
- while (i < n) {
972
- const line = lineAt(lines, i);
973
- const match = KEY_RE.exec(line);
974
- if (!match || isIndented(line)) {
975
- i += 1;
976
- continue;
977
- }
978
- const key = match[1] ?? "";
979
- const value = (match[2] ?? "").trim();
980
- if (BLOCK_INDICATORS.has(value)) {
981
- const block = [];
982
- i += 1;
983
- while (i < n) {
984
- const nxt = lineAt(lines, i);
985
- if (nxt.trim() === "") {
986
- block.push("");
987
- i += 1;
988
- continue;
989
- }
990
- if (isIndented(nxt)) {
991
- block.push(nxt);
992
- i += 1;
993
- continue;
994
- }
995
- break;
996
- }
997
- fields[key] = foldBlock(block);
998
- continue;
999
- }
1000
- if (value === "" || value.startsWith("- ")) {
1001
- i += 1;
1002
- while (i < n &&
1003
- (lineAt(lines, i).replace(/^\s+/, "").startsWith("- ") || isIndented(lineAt(lines, i)))) {
1004
- i += 1;
1005
- }
1006
- if (!(key in fields))
1007
- fields[key] = "";
1008
- continue;
1009
- }
1010
- fields[key] = pyStrip(pyStrip(value, '"'), "'");
1011
- i += 1;
1012
- }
1013
- return fields;
1014
- }
1015
- function extractExtraFrontmatter(frontmatter) {
1016
- const lines = frontmatter.split("\n");
1017
- const extra = [];
1018
- const n = lines.length;
1019
- let i = 0;
1020
- while (i < n) {
1021
- const line = lineAt(lines, i);
1022
- const match = KEY_RE.exec(line);
1023
- if (!match || isIndented(line)) {
1024
- i += 1;
1025
- continue;
1026
- }
1027
- const key = match[1] ?? "";
1028
- const value = (match[2] ?? "").trim();
1029
- const block = [line];
1030
- i += 1;
1031
- if (BLOCK_INDICATORS.has(value)) {
1032
- while (i < n && (lineAt(lines, i).trim() === "" || isIndented(lineAt(lines, i)))) {
1033
- block.push(lineAt(lines, i));
1034
- i += 1;
1035
- }
1036
- }
1037
- else if (value === "" || value.startsWith("- ")) {
1038
- while (i < n &&
1039
- (lineAt(lines, i).replace(/^\s+/, "").startsWith("- ") || isIndented(lineAt(lines, i)))) {
1040
- block.push(lineAt(lines, i));
1041
- i += 1;
1042
- }
1043
- }
1044
- if (key !== "name" && key !== "description")
1045
- extra.push(...block);
1046
- }
1047
- while (extra.length && (extra[extra.length - 1] ?? "").trim() === "")
1048
- extra.pop();
1049
- return extra.length ? extra.join("\n") : null;
1050
- }
1051
- // Skill trigger keywords are sourced from durable, post-#838 surfaces rather
1052
- // than the removed AGENTS.md "## Skill Routing" table (#838 / #2152). Priority:
1053
- // 1. each SKILL.md frontmatter `triggers:` list (the skill's own contract);
1054
- // 2. the REFERENCES.md "Skills Index" table (the #838 single source of truth).
1055
- // This decouples the skills pack from AGENTS.md, so adding a skill no longer
1056
- // requires editing the always-loaded policy file and the trigger map stays
1057
- // non-empty after #838 removed the heading parseRouting used to read.
1058
- const SKILLS_INDEX_HEADING_RE = /Skills Index/i;
1059
- const HEADING_LINE_RE = /^#{1,6}\s/;
1060
- const SKILL_LINK_RE = /\(([^)]*skills\/[^)]+\/SKILL\.md)\)/;
1061
- const BACKTICK_TOKEN_RE = /`([^`]+)`/g;
1062
- /** Normalize a REFERENCES.md skill link path to the `skills/<name>/SKILL.md` key. */
1063
- function normalizeSkillIndexPath(linkPath) {
1064
- let p = linkPath.trim();
1065
- if (p.startsWith("./"))
1066
- p = p.slice(2);
1067
- const marker = p.indexOf("skills/");
1068
- return marker >= 0 ? p.slice(marker) : p;
1069
- }
1070
- /**
1071
- * Parse the REFERENCES.md "Skills Index" table into a `skills/<name>/SKILL.md`
1072
- * -> triggers map. Skill rows are identified by their SKILL.md link (so the
1073
- * header and separator rows are skipped); the trigger cell is the last
1074
- * pipe-delimited column and its keywords are the backtick-quoted tokens.
1075
- */
1076
- function parseSkillsIndexTriggers(referencesMd) {
1077
- const mapping = new Map();
1078
- let inSection = false;
1079
- for (const raw of splitLines(referencesMd)) {
1080
- const line = raw.trim();
1081
- if (HEADING_LINE_RE.test(line)) {
1082
- inSection = SKILLS_INDEX_HEADING_RE.test(line);
1083
- continue;
1084
- }
1085
- if (!inSection || !line.startsWith("|"))
1086
- continue;
1087
- const linkMatch = SKILL_LINK_RE.exec(line);
1088
- if (!linkMatch)
1089
- continue;
1090
- const path = normalizeSkillIndexPath(linkMatch[1] ?? "");
1091
- const cells = line
1092
- .split("|")
1093
- .map((cell) => cell.trim())
1094
- .filter((cell) => cell.length > 0);
1095
- const triggerCell = cells[cells.length - 1] ?? "";
1096
- const bucket = mapping.get(path) ?? [];
1097
- for (const match of triggerCell.matchAll(BACKTICK_TOKEN_RE)) {
1098
- const keyword = (match[1] ?? "").trim();
1099
- if (keyword && !bucket.includes(keyword))
1100
- bucket.push(keyword);
1101
- }
1102
- if (bucket.length > 0)
1103
- mapping.set(path, bucket);
1104
- }
1105
- return mapping;
1106
- }
1107
- // Split on quoted phrases (single or double) or bare comma-delimited runs, so a
1108
- // quoted trigger containing a comma (`["what's next, please", other]`) is not
1109
- // mis-tokenised. All shipped skills use the block-list form today; this keeps
1110
- // the inline flow-list form correct for future skills.
1111
- const FLOW_LIST_TOKEN_RE = /(?:"([^"]*)")|(?:'([^']*)')|([^,]+)/g;
1112
- /** Split an inline YAML flow list (`[a, "b"]`) into trimmed, unquoted tokens. */
1113
- function parseFlowListTokens(value) {
1114
- const inner = value.replace(/^\[/, "").replace(/\]$/, "");
1115
- const out = [];
1116
- for (const match of inner.matchAll(FLOW_LIST_TOKEN_RE)) {
1117
- const token = (match[1] ?? match[2] ?? match[3] ?? "").trim();
1118
- if (token)
1119
- out.push(token);
1120
- }
1121
- return out;
1122
- }
1123
- /** Extract a `triggers:` list (block or inline flow form) from SKILL.md frontmatter. */
1124
- function parseFrontmatterTriggers(frontmatter) {
1125
- const lines = frontmatter.split("\n");
1126
- const n = lines.length;
1127
- for (let i = 0; i < n; i += 1) {
1128
- const line = lineAt(lines, i);
1129
- if (isIndented(line))
1130
- continue;
1131
- const match = KEY_RE.exec(line);
1132
- if (!match || (match[1] ?? "") !== "triggers")
1133
- continue;
1134
- const value = (match[2] ?? "").trim();
1135
- if (value.startsWith("["))
1136
- return parseFlowListTokens(value);
1137
- const out = [];
1138
- let j = i + 1;
1139
- while (j < n) {
1140
- const nxt = lineAt(lines, j);
1141
- if (nxt.trim() === "") {
1142
- j += 1;
1143
- continue;
1144
- }
1145
- if (!isIndented(nxt))
1146
- break;
1147
- const item = nxt.trim();
1148
- if (item.startsWith("- ")) {
1149
- const token = pyStrip(pyStrip(item.slice(2).trim(), '"'), "'");
1150
- if (token)
1151
- out.push(token);
1152
- }
1153
- j += 1;
1154
- }
1155
- return out;
1156
- }
1157
- return [];
1158
- }
1159
- function buildSkillEntry(skillMd, skillsDir, indexTriggers, captureBody) {
1160
- const text = readFileSync(skillMd, "utf8");
1161
- const [frontmatter, body] = splitFrontmatter(text);
1162
- if (frontmatter === null)
1163
- return null;
1164
- const fields = parseFrontmatterFields(frontmatter);
1165
- const name = (fields.name ?? "").trim();
1166
- if (!name)
1167
- return null;
1168
- const relPath = relPosix(dirname(resolve(skillsDir)), resolve(skillMd));
1169
- // Prefer the skill's own frontmatter `triggers:` contract; fall back to the
1170
- // REFERENCES.md Skills Index (#838 single source of truth) so shipped skills
1171
- // that carry no frontmatter triggers still get a non-empty trigger list.
1172
- const frontmatterTriggers = parseFrontmatterTriggers(frontmatter);
1173
- const triggers = frontmatterTriggers.length > 0 ? frontmatterTriggers : (indexTriggers.get(relPath) ?? []);
1174
- const version = (fields.version ?? "").trim() || DEFAULT_SKILL_VERSION;
1175
- return {
1176
- id: name,
1177
- description: (fields.description ?? "").trim(),
1178
- triggers,
1179
- path: relPath,
1180
- version,
1181
- body: captureBody ? stripLeadingBanner(body) : null,
1182
- frontmatter_extra: extractExtraFrontmatter(frontmatter),
1183
- };
1184
- }
1185
- function buildSkillsPack(skillsDir, referencesMd, proofSkill) {
1186
- const indexTriggers = parseSkillsIndexTriggers(readFileSync(referencesMd, "utf8"));
1187
- const captureAll = proofSkill === null;
1188
- const proofPath = proofSkill !== null ? `skills/${proofSkill}/SKILL.md` : null;
1189
- const base = dirname(resolve(skillsDir));
1190
- const skills = [];
1191
- for (const skillMd of globSkillMd(skillsDir)) {
1192
- const relPath = relPosix(base, resolve(skillMd));
1193
- const entry = buildSkillEntry(skillMd, skillsDir, indexTriggers, captureAll || relPath === proofPath);
1194
- if (entry !== null)
1195
- skills.push(entry);
1196
- }
1197
- return {
1198
- pack: "skills-pack-0.1",
1199
- version: PACK_VERSION,
1200
- generated_from: "skills/*/SKILL.md frontmatter triggers + REFERENCES.md (Skills Index)",
1201
- skills,
1202
- };
1203
- }
1204
- const GLYPH_TIER = {
1205
- "!": "MUST",
1206
- "~": "SHOULD",
1207
- [SHOULD_NOT_GLYPH]: "SHOULD_NOT",
1208
- [MUST_NOT_GLYPH]: "MUST_NOT",
1209
- "?": "MAY",
1210
- };
1211
- const MARKER_RE = new RegExp(`^\\s*(?:-\\s+)?([!~?${SHOULD_NOT_GLYPH}${MUST_NOT_GLYPH}])\\s+(\\S.*)$`);
1212
- const PROSE_TIERS = [
1213
- ["MUST NOT", "MUST_NOT"],
1214
- ["SHOULD NOT", "SHOULD_NOT"],
1215
- ["MUST", "MUST"],
1216
- ["SHOULD", "SHOULD"],
1217
- ["MAY", "MAY"],
1218
- ];
1219
- function proseTier(text) {
1220
- for (const [keyword, tier] of PROSE_TIERS) {
1221
- const pattern = new RegExp(`\\b${keyword.replace(/ /g, "[ ]")}\\b`);
1222
- if (pattern.test(text))
1223
- return tier;
1224
- }
1225
- return null;
1226
- }
1227
- function parseRules(md, domain) {
1228
- const rules = [];
1229
- let seq = 0;
1230
- for (const raw of splitLines(md)) {
1231
- const line = raw.replace(/\s+$/, "");
1232
- let tier = null;
1233
- let text = "";
1234
- const marker = MARKER_RE.exec(line);
1235
- if (marker) {
1236
- tier = GLYPH_TIER[marker[1] ?? ""] ?? null;
1237
- text = (marker[2] ?? "").trim();
1238
- }
1239
- else {
1240
- const stripped = line.trim();
1241
- if (!stripped.startsWith("- "))
1242
- continue;
1243
- text = stripped.slice(2).trim();
1244
- tier = text ? proseTier(text) : null;
1245
- }
1246
- if (tier === null || text === "")
1247
- continue;
1248
- seq += 1;
1249
- rules.push({ id: `${domain}-${String(seq).padStart(3, "0")}`, tier, domain, text });
1250
- }
1251
- return rules;
1252
- }
1253
- const MANAGED_SECTION_RE = /<!--\s*deft:managed-section[\s\S]*?<!--\s*\/deft:managed-section\s*-->/g;
1254
- function stripManagedSection(md) {
1255
- return md.replace(MANAGED_SECTION_RE, "");
1256
- }
1257
- function buildRulesPack(codingDir, extraSources) {
1258
- const base = dirname(resolve(codingDir));
1259
- const rules = [];
1260
- for (const md of globMd(codingDir)) {
1261
- const relPath = relPosix(base, resolve(md));
1262
- const domain = slugify(stemOf(md));
1263
- const text = readFileSync(md, "utf8");
1264
- const docRules = parseRules(text, domain);
1265
- docRules.forEach((rule, idx) => {
1266
- rule.path = relPath;
1267
- rule.body = idx === 0 ? stripLeadingBanner(text) : null;
1268
- rules.push(rule);
1269
- });
1270
- }
1271
- for (const src of extraSources) {
1272
- if (!isFileSafe(src))
1273
- continue;
1274
- const candidate = relPosix(base, resolve(src));
1275
- const relPath = candidate.startsWith("..") || isAbsolute(candidate) ? basename(src) : candidate;
1276
- const domain = slugify(stemOf(src));
1277
- let text = readFileSync(src, "utf8");
1278
- if (basename(src) === "AGENTS.md")
1279
- text = stripManagedSection(text);
1280
- for (const rule of parseRules(text, domain)) {
1281
- rule.path = relPath;
1282
- rule.body = null;
1283
- rules.push(rule);
1284
- }
1285
- }
1286
- return {
1287
- pack: "rules-pack-0.1",
1288
- version: PACK_VERSION,
1289
- generated_from: "coding/*.md + AGENTS.md + main.md (marker-prefixed RFC2119 directives; " +
1290
- "AGENTS.md managed-section excluded; coding bodies rendered, " +
1291
- "AGENTS.md/main.md metadata-only)",
1292
- rules,
1293
- };
1294
- }
1295
- function buildMdEntry(md, dir, captureBody) {
1296
- const relPath = relPosix(dirname(resolve(dir)), resolve(md));
1297
- const stemSlug = slugify(stemOf(md));
1298
- const text = readFileSync(md, "utf8");
1299
- return {
1300
- id: stemSlug,
1301
- title: extractTitle(text),
1302
- description: extractDescription(text),
1303
- triggers: stemSlug ? [stemSlug] : [],
1304
- path: relPath,
1305
- body: captureBody ? stripLeadingBanner(text) : null,
1306
- };
1307
- }
1308
- function buildStrategiesPack(strategiesDir, proofStrategy) {
1309
- const base = dirname(resolve(strategiesDir));
1310
- const captureAll = proofStrategy === null;
1311
- const strategies = [];
1312
- for (const md of globMd(strategiesDir)) {
1313
- const relPath = relPosix(base, resolve(md));
1314
- const captureBody = captureAll
1315
- ? !isRedirectStub(readFileSync(md, "utf8"))
1316
- : relPath === proofStrategy;
1317
- strategies.push(buildMdEntry(md, strategiesDir, captureBody));
1318
- }
1319
- return {
1320
- pack: "strategies-pack-0.1",
1321
- version: PACK_VERSION,
1322
- generated_from: "strategies/*.md",
1323
- strategies,
1324
- };
1325
- }
1326
- function buildPatternsPack(patternsDir, proofPattern) {
1327
- const base = dirname(resolve(patternsDir));
1328
- const patterns = [];
1329
- for (const md of globMd(patternsDir)) {
1330
- const relPath = relPosix(base, resolve(md));
1331
- patterns.push(buildMdEntry(md, patternsDir, relPath === proofPattern));
1332
- }
1333
- return {
1334
- pack: "patterns-pack-0.1",
1335
- version: PACK_VERSION,
1336
- generated_from: "patterns/*.md",
1337
- patterns,
1338
- };
1339
- }
1340
- function buildSwarmSpecPack(swarmDir, proofEntry) {
1341
- const base = dirname(resolve(swarmDir));
1342
- const entries = [];
1343
- for (const md of globMd(swarmDir)) {
1344
- const relPath = relPosix(base, resolve(md));
1345
- entries.push(buildMdEntry(md, swarmDir, relPath === proofEntry));
1346
- }
1347
- return {
1348
- pack: "swarm-spec-pack-0.1",
1349
- version: PACK_VERSION,
1350
- generated_from: "swarm/*.md",
1351
- entries,
1352
- };
1353
- }
1354
- function writePack(out, pack) {
1355
- mkdirSync(dirname(out), { recursive: true });
1356
- writeFileSync(out, dumpsAsciiJson(pack), "utf8");
1357
- }
1358
- /** Resolve the shippable content root: <root>/content when present, else <root> (#1875). */
1359
- function resolveContentRoot() {
1360
- const root = resolveDeftRoot();
1361
- const candidate = join(root, "content");
1362
- return isDirSafe(candidate) ? candidate : root;
1363
- }
1364
- /**
1365
- * Minimal argparse-compatible option reader supporting `--flag value` and
1366
- * `--flag=value`. `listFlags` accumulate repeats; all flags take a value.
1367
- */
1368
- function parsePackArgs(argv, valueFlags, listFlags = []) {
1369
- const values = {};
1370
- const lists = {};
1371
- const known = new Set([...valueFlags, ...listFlags]);
1372
- for (let i = 0; i < argv.length; i += 1) {
1373
- const arg = argv[i];
1374
- if (arg === undefined)
1375
- continue;
1376
- let flag = arg;
1377
- let inlineValue;
1378
- if (arg.startsWith("--") && arg.includes("=")) {
1379
- const eq = arg.indexOf("=");
1380
- flag = arg.slice(0, eq);
1381
- inlineValue = arg.slice(eq + 1);
1382
- }
1383
- if (!known.has(flag)) {
1384
- return { values, lists, error: `unrecognized argument: ${arg}` };
1385
- }
1386
- let value = inlineValue;
1387
- if (value === undefined) {
1388
- i += 1;
1389
- value = argv[i];
1390
- }
1391
- if (value === undefined) {
1392
- return { values, lists, error: `argument ${flag}: expected one argument` };
1393
- }
1394
- if (listFlags.includes(flag)) {
1395
- const bucket = lists[flag] ?? [];
1396
- bucket.push(value);
1397
- lists[flag] = bucket;
1398
- }
1399
- else {
1400
- values[flag] = value;
1401
- }
1402
- }
1403
- return { values, lists };
1404
- }
1405
- function runPackMigrateSkills(argv, io) {
1406
- const contentRoot = resolveContentRoot();
1407
- const parsed = parsePackArgs(argv, ["--skills-dir", "--references-md", "--proof-skill", "--out"]);
1408
- if (parsed.error !== undefined) {
1409
- io.writeErr(`error: ${parsed.error}\n`);
1410
- return 2;
1411
- }
1412
- const skillsDir = parsed.values["--skills-dir"] ?? join(contentRoot, "skills");
1413
- const referencesMd = parsed.values["--references-md"] ?? join(resolveDeftRoot(), "REFERENCES.md");
1414
- const proofSkill = parsed.values["--proof-skill"] ?? null;
1415
- const out = parsed.values["--out"] ?? join(contentRoot, "packs", "skills", "skills-pack-0.1.json");
1416
- if (!isDirSafe(skillsDir)) {
1417
- io.writeErr(`error: skills directory not found: ${skillsDir}\n`);
1418
- return 1;
1419
- }
1420
- if (!isFileSafe(referencesMd)) {
1421
- io.writeErr(`error: REFERENCES.md not found: ${referencesMd}\n`);
1422
- return 1;
1423
- }
1424
- const pack = buildSkillsPack(skillsDir, referencesMd, proofSkill);
1425
- if (pack.skills.length === 0) {
1426
- io.writeErr(`error: no skills with frontmatter discovered under ${skillsDir}\n`);
1427
- return 1;
1428
- }
1429
- writePack(out, pack);
1430
- const bodied = pack.skills.filter((s) => s.body !== null).length;
1431
- io.writeOut(`Migrated ${pack.skills.length} skills (${bodied} with body) -> ${out}\n`);
1432
- return 0;
1433
- }
1434
- function runPackMigrateRules(argv, io) {
1435
- const contentRoot = resolveContentRoot();
1436
- const deftRoot = resolveDeftRoot();
1437
- const parsed = parsePackArgs(argv, ["--coding-dir", "--out"], ["--extra-source"]);
1438
- if (parsed.error !== undefined) {
1439
- io.writeErr(`error: ${parsed.error}\n`);
1440
- return 2;
1441
- }
1442
- const codingDir = parsed.values["--coding-dir"] ?? join(contentRoot, "coding");
1443
- const extraSources = parsed.lists["--extra-source"] ?? [
1444
- join(deftRoot, "AGENTS.md"),
1445
- join(deftRoot, "main.md"),
1446
- ];
1447
- const out = parsed.values["--out"] ?? join(contentRoot, "packs", "rules", "rules-pack-0.1.json");
1448
- if (!isDirSafe(codingDir)) {
1449
- io.writeErr(`error: coding directory not found: ${codingDir}\n`);
1450
- return 1;
1451
- }
1452
- const pack = buildRulesPack(codingDir, extraSources);
1453
- if (pack.rules.length === 0) {
1454
- io.writeErr(`error: no directives discovered under ${codingDir}\n`);
1455
- return 1;
1456
- }
1457
- writePack(out, pack);
1458
- const bodied = pack.rules.filter((r) => r.body != null).length;
1459
- io.writeOut(`Migrated ${pack.rules.length} rules (${bodied} with body) -> ${out}\n`);
1460
- return 0;
1461
- }
1462
- function runPackMigrateStrategies(argv, io) {
1463
- const contentRoot = resolveContentRoot();
1464
- const parsed = parsePackArgs(argv, ["--strategies-dir", "--proof-strategy", "--out"]);
1465
- if (parsed.error !== undefined) {
1466
- io.writeErr(`error: ${parsed.error}\n`);
1467
- return 2;
1468
- }
1469
- const strategiesDir = parsed.values["--strategies-dir"] ?? join(contentRoot, "strategies");
1470
- const proofStrategy = parsed.values["--proof-strategy"] ?? null;
1471
- const out = parsed.values["--out"] ?? join(contentRoot, "packs", "strategies", "strategies-pack-0.1.json");
1472
- if (!isDirSafe(strategiesDir)) {
1473
- io.writeErr(`error: strategies directory not found: ${strategiesDir}\n`);
1474
- return 1;
1475
- }
1476
- const pack = buildStrategiesPack(strategiesDir, proofStrategy);
1477
- if (pack.strategies.length === 0) {
1478
- io.writeErr(`error: no strategies discovered under ${strategiesDir}\n`);
1479
- return 1;
1480
- }
1481
- writePack(out, pack);
1482
- const bodied = pack.strategies.filter((s) => s.body !== null).length;
1483
- io.writeOut(`Migrated ${pack.strategies.length} strategies (${bodied} with body) -> ${out}\n`);
1484
- return 0;
1485
- }
1486
- function runPackMigratePatterns(argv, io) {
1487
- const contentRoot = resolveContentRoot();
1488
- const parsed = parsePackArgs(argv, ["--patterns-dir", "--proof-pattern", "--out"]);
1489
- if (parsed.error !== undefined) {
1490
- io.writeErr(`error: ${parsed.error}\n`);
1491
- return 2;
1492
- }
1493
- const patternsDir = parsed.values["--patterns-dir"] ?? join(contentRoot, "patterns");
1494
- const proofPattern = parsed.values["--proof-pattern"] ?? "patterns/multi-agent.md";
1495
- const out = parsed.values["--out"] ?? join(contentRoot, "packs", "patterns", "patterns-pack-0.1.json");
1496
- if (!isDirSafe(patternsDir)) {
1497
- io.writeErr(`error: patterns directory not found: ${patternsDir}\n`);
1498
- return 1;
1499
- }
1500
- const pack = buildPatternsPack(patternsDir, proofPattern);
1501
- if (pack.patterns.length === 0) {
1502
- io.writeErr(`error: no patterns discovered under ${patternsDir}\n`);
1503
- return 1;
1504
- }
1505
- writePack(out, pack);
1506
- const bodied = pack.patterns.filter((p) => p.body !== null).length;
1507
- io.writeOut(`Migrated ${pack.patterns.length} patterns (${bodied} with body) -> ${out}\n`);
1508
- return 0;
1509
- }
1510
- function runPackMigrateSwarmSpec(argv, io) {
1511
- const contentRoot = resolveContentRoot();
1512
- const parsed = parsePackArgs(argv, ["--swarm-dir", "--proof-entry", "--out"]);
1513
- if (parsed.error !== undefined) {
1514
- io.writeErr(`error: ${parsed.error}\n`);
1515
- return 2;
1516
- }
1517
- const swarmDir = parsed.values["--swarm-dir"] ?? join(contentRoot, "swarm");
1518
- const proofEntry = parsed.values["--proof-entry"] ?? "swarm/swarm.md";
1519
- const out = parsed.values["--out"] ?? join(contentRoot, "packs", "swarm-spec", "swarm-spec-pack-0.1.json");
1520
- if (!isDirSafe(swarmDir)) {
1521
- io.writeErr(`error: swarm directory not found: ${swarmDir}\n`);
1522
- return 1;
1523
- }
1524
- const pack = buildSwarmSpecPack(swarmDir, proofEntry);
1525
- if (pack.entries.length === 0) {
1526
- io.writeErr(`error: no swarm-spec docs discovered under ${swarmDir}\n`);
1527
- return 1;
1528
- }
1529
- writePack(out, pack);
1530
- const bodied = pack.entries.filter((e) => e.body !== null).length;
1531
- io.writeOut(`Migrated ${pack.entries.length} swarm-spec entries (${bodied} with body) -> ${out}\n`);
1532
- return 0;
1533
- }
1534
- // ===========================================================================
1535
- // Native setup:ghx handler (#2022 Phase 1; #2178 download-verify-execute).
1536
- //
1537
- // Port of scripts/setup_ghx.py to native TypeScript: consent-gated ghx proxy
1538
- // installer with three-state exit (0 ok / 1 install failure / 2 config error).
1539
- //
1540
- // #2178: the installer no longer pipes remote bytes straight into a shell
1541
- // (`curl | bash` / `irm | iex`). Socket Security's AI-malware heuristic flags
1542
- // exactly that live-pipe-with-no-integrity-check pattern and blocks every
1543
- // consumer PR that bumps @deftai/directive (Socket scored the package ~65%
1544
- // likely malicious, severity 0.78 -- seen on deftai/evolution#1046 / #1047).
1545
- // Instead: download the installer script to memory, verify it against a
1546
- // SHA-256 vendored below, write it to a private local temp file, and only
1547
- // then execute that local file directly. The fetch URL also pins to the
1548
- // immutable commit SHA that GHX_VERSION resolved to at vendor time (not the
1549
- // mutable tag name), so a future tag force-move on the upstream repo cannot
1550
- // swap the fetched bytes out from under the vendored hash without also
1551
- // failing the hash check.
1552
- //
1553
- // Bumping GHX_VERSION (`.github/workflows/ci.yml` env.GHX_VERSION MUST stay
1554
- // in lockstep):
1555
- // 1. Resolve the new tag's commit SHA:
1556
- // gh api repos/brunoborges/ghx/git/refs/tags/<new-version>
1557
- // Use `object.sha`. If `object.type` is "tag" (an annotated tag, not a
1558
- // lightweight one), resolve one level further:
1559
- // gh api repos/brunoborges/ghx/git/tags/<object.sha>
1560
- // and use THAT response's `object.sha` (the commit, not the tag object).
1561
- // 2. Refetch both installers at the resolved commit and recompute hashes:
1562
- // curl -fsSL https://raw.githubusercontent.com/brunoborges/ghx/<sha>/install.sh | sha256sum
1563
- // curl -fsSL https://raw.githubusercontent.com/brunoborges/ghx/<sha>/install.ps1 | sha256sum
1564
- // 3. Update GHX_VERSION, GHX_COMMIT_SHA, GHX_INSTALL_SH_SHA256, and
1565
- // GHX_INSTALL_PS1_SHA256 below IN THE SAME COMMIT as the matching
1566
- // `.github/workflows/ci.yml` env values -- never let the two drift.
1567
- // ===========================================================================
1568
- /** Pinned ghx version (display only) — keep in lockstep with .github/workflows/ci.yml env.GHX_VERSION. */
1569
- export const GHX_VERSION = "v1.5.1";
1570
- /**
1571
- * Immutable commit SHA the GHX_VERSION tag resolved to at vendor time
1572
- * (2026-07-02, via `gh api repos/brunoborges/ghx/git/refs/tags/v1.5.1`).
1573
- * Fetch URLs pin to this SHA rather than the mutable tag name so a future
1574
- * tag force-move on brunoborges/ghx cannot silently swap the fetched bytes
1575
- * out from under the vendored SHA-256 hashes below (#2178).
1576
- */
1577
- export const GHX_COMMIT_SHA = "aa4a2786660e27392b0d3e8886f140e0a0261a0c";
1578
- export const INSTALL_PS1_URL = `https://raw.githubusercontent.com/brunoborges/ghx/${GHX_COMMIT_SHA}/install.ps1`;
1579
- export const INSTALL_SH_URL = `https://raw.githubusercontent.com/brunoborges/ghx/${GHX_COMMIT_SHA}/install.sh`;
1580
- /**
1581
- * SHA-256 of the installer scripts at GHX_COMMIT_SHA, vendored so the
1582
- * download-verify-execute pipeline below can refuse to run tampered bytes.
1583
- * Matches `.github/workflows/ci.yml` env.GHX_INSTALL_SH_SHA256 /
1584
- * GHX_INSTALL_PS1_SHA256 (#1070 / #1328) — keep both in lockstep (#2178).
1585
- */
1586
- export const GHX_INSTALL_SH_SHA256 = "08c768feb6d2bc485079898f7e76c2b07576cbb1188a356acf99dac0fc55d1cb";
1587
- export const GHX_INSTALL_PS1_SHA256 = "5f67eab68970ecc55bb0fc1b8399ba6f3ce4b2aadeee39255d628e96d187a5ed";
1588
- async function defaultGhxDownload(url) {
1589
- const res = await fetch(url);
1590
- if (!res.ok) {
1591
- throw new Error(`download failed: HTTP ${res.status} ${res.statusText} for ${url}`);
1592
- }
1593
- return Buffer.from(await res.arrayBuffer());
1594
- }
1595
- /** True when `buf`'s SHA-256 (hex) matches `expectedHex`, case- and whitespace-insensitive. */
1596
- export function verifyGhxSha256(buf, expectedHex) {
1597
- const actual = createHash("sha256").update(buf).digest("hex");
1598
- return actual.toLowerCase() === expectedHex.trim().toLowerCase();
1599
- }
1600
- export function resolveGhxInstallerAsset(host) {
1601
- if (host === "windows") {
1602
- return { url: INSTALL_PS1_URL, sha256: GHX_INSTALL_PS1_SHA256, fileExt: "ps1" };
1603
- }
1604
- if (host === "darwin" || host === "linux") {
1605
- return { url: INSTALL_SH_URL, sha256: GHX_INSTALL_SH_SHA256, fileExt: "sh" };
1606
- }
1607
- throw new Error(`no upstream ghx installer available for host '${host}'; ` +
1608
- "see https://github.com/brunoborges/ghx#install for manual options");
1609
- }
1610
- function parseSetupGhxArgs(argv) {
1611
- let yes = false;
1612
- let check = false;
1613
- for (const arg of argv) {
1614
- if (arg === "--yes") {
1615
- yes = true;
1616
- }
1617
- else if (arg === "--check") {
1618
- check = true;
1619
- }
1620
- else {
1621
- return { yes, check, error: `unrecognized argument: ${arg}` };
1622
- }
1623
- }
1624
- return { yes, check };
1625
- }
1626
- export function ghxPresent(whichFn = defaultWhich) {
1627
- return whichFn("ghx") !== null;
1628
- }
1629
- export function detectSetupGhxHost() {
1630
- if (process.platform === "win32")
1631
- return "windows";
1632
- if (process.platform === "darwin")
1633
- return "darwin";
1634
- if (process.platform === "linux")
1635
- return "linux";
1636
- return process.platform;
1637
- }
1638
- function readConsentLineFromStdin() {
1639
- const buf = Buffer.alloc(256);
1640
- try {
1641
- const n = readSync(0, buf, 0, 256, null);
1642
- if (n === null || n <= 0)
1643
- return "";
1644
- return buf.toString("utf8", 0, n);
1645
- }
1646
- catch {
1647
- return "";
1648
- }
1649
- }
1650
- export function promptSetupGhxConsent(io, readLine = readConsentLineFromStdin) {
1651
- io.writeOut("\n[setup_ghx] ghx is the recommended GitHub CLI cache proxy for deft " +
1652
- "maintainers (prevents rate-limiting in multi-agent swarms; speeds up " +
1653
- "scm:* calls). Consumer projects only require gh.\n");
1654
- io.writeOut(`[setup_ghx] Upstream: https://github.com/brunoborges/ghx (${GHX_VERSION})\n`);
1655
- io.writeOut("[setup_ghx] Install ghx via the upstream installer? [y/N]: ");
1656
- const answer = readLine().trim().toLowerCase();
1657
- return answer === "y" || answer === "yes";
1658
- }
1659
- function ghxTempFileName(fileExt) {
1660
- return `ghx-install-${GHX_VERSION}.${fileExt}`;
1661
- }
1662
- /**
1663
- * Downloads `asset`, verifies it against its vendored SHA-256, and writes it
1664
- * to a private local temp file. Returns the local path, ready for direct
1665
- * local-file execution (never piped into a shell). Throws -- without
1666
- * writing or executing anything -- on a hash mismatch (#2178). Split out
1667
- * from `fetchAndVerifyGhxInstaller` so tests can exercise the download ->
1668
- * verify -> write pipeline against a synthetic asset/hash without depending
1669
- * on the real vendored constants or the network.
1670
- */
1671
- export async function fetchAndVerifyGhxInstallerAsset(asset, downloadFn = defaultGhxDownload) {
1672
- const bytes = await downloadFn(asset.url);
1673
- if (!verifyGhxSha256(bytes, asset.sha256)) {
1674
- const actual = createHash("sha256").update(bytes).digest("hex");
1675
- throw new Error(`ghx installer SHA-256 mismatch for ${asset.url} ` +
1676
- `(expected ${asset.sha256}, got ${actual}); refusing to execute. ` +
1677
- "The pinned commit's bytes may have changed, or the download was tampered with.");
1678
- }
1679
- const dir = mkdtempSync(join(tmpdir(), "deft-ghx-"));
1680
- const installerPath = join(dir, ghxTempFileName(asset.fileExt));
1681
- writeFileSync(installerPath, bytes, { mode: 0o700 });
1682
- return installerPath;
1683
- }
1684
- /**
1685
- * Downloads the pinned installer for `host`, verifies it against the
1686
- * vendored SHA-256, and writes it to a private local temp file. Returns the
1687
- * local path, ready for direct local-file execution (never piped into a
1688
- * shell). Throws -- without writing or executing anything -- on a hash
1689
- * mismatch (#2178).
1690
- */
1691
- export async function fetchAndVerifyGhxInstaller(host, downloadFn = defaultGhxDownload) {
1692
- return fetchAndVerifyGhxInstallerAsset(resolveGhxInstallerAsset(host), downloadFn);
1693
- }
1694
- /**
1695
- * Executes an already-downloaded, hash-verified installer from its local
1696
- * temp path. No live pipe (`curl | bash` / `irm | iex`) and no
1697
- * `-ExecutionPolicy Bypass` -- the file is written by Node, so it never
1698
- * carries a Windows Mark-of-the-Web zone identifier the way a browser or
1699
- * `Invoke-WebRequest` download would; `RemoteSigned` treats it as a local,
1700
- * unsigned-but-trusted script (#2178).
1701
- */
1702
- export function executeVerifiedGhxInstaller(host, installerPath, whichFn = defaultWhich, runner = spawnSync) {
1703
- const cmd = host === "windows"
1704
- ? [
1705
- whichFn("pwsh") ?? whichFn("powershell") ?? "powershell",
1706
- "-NoProfile",
1707
- "-ExecutionPolicy",
1708
- "RemoteSigned",
1709
- "-File",
1710
- installerPath,
1711
- ]
1712
- : ["bash", installerPath];
1713
- const proc = runner(cmd[0] ?? "", cmd.slice(1), {
1714
- env: { ...process.env, GHX_VERSION },
1715
- stdio: "inherit",
1716
- });
1717
- return proc.status ?? 1;
1718
- }
1719
- /**
1720
- * Downloads, hash-verifies, and executes `asset` for `host`. Cleans up the
1721
- * temp file (and its containing directory) regardless of outcome. Split out
1722
- * from `installSetupGhx` so tests can exercise the full download -> verify
1723
- * -> execute -> cleanup pipeline against a synthetic asset without depending
1724
- * on the real vendored constants or the network (#2178).
1725
- */
1726
- export async function installVerifiedGhxAsset(asset, host, whichFn = defaultWhich, runner = spawnSync, downloadFn = defaultGhxDownload) {
1727
- const installerPath = await fetchAndVerifyGhxInstallerAsset(asset, downloadFn);
1728
- try {
1729
- return executeVerifiedGhxInstaller(host, installerPath, whichFn, runner);
1730
- }
1731
- finally {
1732
- try {
1733
- rmSync(dirname(installerPath), { recursive: true, force: true });
1734
- }
1735
- catch {
1736
- // Best-effort cleanup; a leftover temp file is not fatal.
1737
- }
1738
- }
1739
- }
1740
- /**
1741
- * Downloads, hash-verifies, and executes the ghx installer for `host`.
1742
- * Cleans up the temp file (and its containing directory) regardless of
1743
- * outcome (#2178).
1744
- */
1745
- export async function installSetupGhx(host, whichFn = defaultWhich, runner = spawnSync, downloadFn = defaultGhxDownload) {
1746
- return installVerifiedGhxAsset(resolveGhxInstallerAsset(host), host, whichFn, runner, downloadFn);
1747
- }
1748
- /** Native `setup:ghx` handler (replaces scripts/setup_ghx.py shell-out, #2022 Phase 1). */
1749
- export async function runSetupGhx(argv, io, deps = {}) {
1750
- const args = parseSetupGhxArgs(argv);
1751
- if (args.error !== undefined) {
1752
- io.writeErr(`setup-ghx: ${args.error}\n`);
1753
- return 2;
1754
- }
1755
- if (args.yes && args.check) {
1756
- io.writeErr("[setup_ghx] error: --yes and --check are mutually exclusive.\n");
1757
- return 2;
1758
- }
1759
- const whichFn = deps.whichFn ?? defaultWhich;
1760
- if (ghxPresent(whichFn)) {
1761
- io.writeOut("[setup_ghx] ghx already on PATH -- skipping install.\n");
1762
- return 0;
1763
- }
1764
- if (args.check) {
1765
- if (whichFn("gh") !== null) {
1766
- io.writeOut("[setup_ghx] gh is on PATH but ghx is not; ghx is the recommended GitHub CLI " +
1767
- "cache proxy. Install with `directive setup:ghx` (or `task setup:ghx`). Refs #884.\n");
1768
- }
1769
- else {
1770
- io.writeOut("[setup_ghx] ghx not on PATH; recommended for speed -- run `directive setup:ghx` " +
1771
- "to opt in. Consumer projects only require gh. Refs #884.\n");
1772
- }
1773
- return 0;
1774
- }
1775
- let consent;
1776
- if (args.yes) {
1777
- consent = true;
1778
- io.writeOut("[setup_ghx] --yes provided; skipping interactive consent prompt.\n");
1779
- }
1780
- else {
1781
- const skip = (process.env.DEFT_SETUP_GHX_SKIP ?? "").trim();
1782
- if (skip === "1" || skip.toLowerCase() === "true" || skip.toLowerCase() === "yes") {
1783
- io.writeOut("[setup_ghx] DEFT_SETUP_GHX_SKIP set; skipping ghx install. Refs #884.\n");
1784
- return 0;
1785
- }
1786
- const readLine = deps.readConsentLine ?? readConsentLineFromStdin;
1787
- consent = promptSetupGhxConsent(io, readLine);
1788
- }
1789
- if (!consent) {
1790
- io.writeOut("[setup_ghx] Skipping ghx install. ghx is recommended for speed for maintainers and " +
1791
- "swarm runs; consumer projects only require gh " +
1792
- "(see https://github.com/brunoborges/ghx, #884).\n");
1793
- return 0;
1794
- }
1795
- const host = detectSetupGhxHost();
1796
- const runInstall = deps.runInstall ??
1797
- ((h) => installSetupGhx(h, whichFn, deps.runner, deps.downloadFn));
1798
- try {
1799
- const rc = await runInstall(host);
1800
- if (rc !== 0) {
1801
- io.writeErr(`[setup_ghx] error: upstream installer exited ${rc}. ` +
1802
- "See https://github.com/brunoborges/ghx#install for manual options.\n");
1803
- return 1;
1804
- }
1805
- io.writeOut("[setup_ghx] ghx installed. Open a fresh shell so the updated PATH takes effect, " +
1806
- "then re-run `task setup` to verify.\n");
1807
- return 0;
1808
- }
1809
- catch (err) {
1810
- const message = err instanceof Error ? err.message : String(err);
1811
- io.writeErr(`[setup_ghx] error: ${message}\n`);
1812
- return 1;
1813
- }
1814
- }
1815
- // ===========================================================================
1816
- // Native directive bootstrap launcher (#2022 Phase 4).
1817
- //
1818
- // Thin operator entry: deposit-if-absent, carry phase intent + deliberate
1819
- // re-entry signal, then hand off to deft-directive-setup (agent-driven).
1820
- // ===========================================================================
1821
- export const SETUP_SKILL_REL_PATH = ".deft/core/skills/deft-directive-setup/SKILL.md";
1822
- function bootstrapPhaseLabel(phase) {
1823
- if (phase === 1)
1824
- return "user";
1825
- if (phase === 2)
1826
- return "project";
1827
- return "spec";
1828
- }
1829
- /**
1830
- * Bootstrap USER.md path resolver (#2271). Delegates to the shared first-hit-
1831
- * wins resolver so the CLI bootstrap, session-start, and doctor share one
1832
- * source of truth. DEFT_USER_PATH precedence is preserved by the shared
1833
- * resolver (rung 1). `projectRoot` scopes the workspace-local rung so a bridged
1834
- * `<projectRoot>/.deft/USER.md` resolves without a manual DEFT_USER_PATH.
1835
- */
1836
- function resolveBootstrapUserMdPath(projectRoot) {
1837
- return userConfig.resolveUserMdPath(projectRoot !== undefined ? { projectRoot } : {}).path;
1838
- }
1839
- function defaultBootstrapDeps() {
1840
- return {
1841
- deftCorePresent: (projectRoot) => isDirSafe(join(resolve(projectRoot), ".deft", "core")),
1842
- userMdPresent: (projectRoot) => isFileSafe(resolveBootstrapUserMdPath(projectRoot)),
1843
- projectDefPresent: (projectRoot) => isFileSafe(projectDefinitionPath(projectRoot)),
1844
- runInitDeposit: async (projectRoot, io) => {
1845
- const initArgs = parseInitArgv(["--yes", "--repo-root", projectRoot, "--json"], []);
1846
- return runInitDepositCli({
1847
- ...initArgs,
1848
- writeOut: io.writeOut,
1849
- writeErr: io.writeErr,
1850
- });
1851
- },
1852
- };
1853
- }
1854
- /** Parse `directive bootstrap` argv (#2022 Phase 4). */
1855
- export function parseDirectiveBootstrapArgs(argv) {
1856
- let projectRoot = ".";
1857
- let jumpProject = false;
1858
- let strategy = null;
1859
- let reconfigure = false;
1860
- let force = false;
1861
- let json = false;
1862
- for (let i = 0; i < argv.length; i += 1) {
1863
- const arg = argv[i];
1864
- if (arg === "--project") {
1865
- jumpProject = true;
1866
- }
1867
- else if (arg === "--reconfigure") {
1868
- reconfigure = true;
1869
- }
1870
- else if (arg === "--force") {
1871
- force = true;
1872
- }
1873
- else if (arg === "--json") {
1874
- json = true;
1875
- }
1876
- else if (arg === "--project-root") {
1877
- const value = argv[i + 1];
1878
- if (value === undefined) {
1879
- return {
1880
- projectRoot,
1881
- jumpProject,
1882
- strategy,
1883
- reconfigure,
1884
- force,
1885
- json,
1886
- error: "argument --project-root: expected one argument",
1887
- };
1888
- }
1889
- projectRoot = value;
1890
- i += 1;
1891
- }
1892
- else if (arg?.startsWith("--project-root=")) {
1893
- projectRoot = arg.slice("--project-root=".length);
1894
- }
1895
- else if (arg === "--strategy") {
1896
- const value = argv[i + 1];
1897
- if (value === undefined) {
1898
- return {
1899
- projectRoot,
1900
- jumpProject,
1901
- strategy,
1902
- reconfigure,
1903
- force,
1904
- json,
1905
- error: "argument --strategy: expected one argument",
1906
- };
1907
- }
1908
- strategy = value;
1909
- i += 1;
1910
- }
1911
- else if (arg?.startsWith("--strategy=")) {
1912
- strategy = arg.slice("--strategy=".length);
1913
- }
1914
- else if (arg === "--help" || arg === "-h") {
1915
- return {
1916
- projectRoot,
1917
- jumpProject,
1918
- strategy,
1919
- reconfigure,
1920
- force,
1921
- json,
1922
- error: "__help__",
1923
- };
1924
- }
1925
- else {
1926
- return {
1927
- projectRoot,
1928
- jumpProject,
1929
- strategy,
1930
- reconfigure,
1931
- force,
1932
- json,
1933
- error: `unrecognized argument: ${arg}`,
1934
- };
1935
- }
1936
- }
1937
- return {
1938
- projectRoot: resolve(projectRoot),
1939
- jumpProject,
1940
- strategy,
1941
- reconfigure,
1942
- force,
1943
- json,
1944
- };
1945
- }
1946
- /** Resolve phase intent and deliberate re-entry signal from parsed args + on-disk state. */
1947
- export function resolveDirectiveBootstrapPlan(args, deps) {
1948
- let phase;
1949
- if (args.jumpProject) {
1950
- phase = 2;
1951
- }
1952
- else if (args.strategy !== null) {
1953
- phase = 3;
1954
- }
1955
- else if (!deps.userMdPresent(args.projectRoot)) {
1956
- phase = 1;
1957
- }
1958
- else if (!deps.projectDefPresent(args.projectRoot)) {
1959
- phase = 2;
1960
- }
1961
- else {
1962
- phase = 3;
1963
- }
1964
- let reEntry = "none";
1965
- if (args.force) {
1966
- reEntry = "force";
1967
- }
1968
- else if (args.reconfigure) {
1969
- reEntry = "reconfigure";
1970
- }
1971
- else {
1972
- const artifactExists = phase === 1
1973
- ? deps.userMdPresent(args.projectRoot)
1974
- : phase === 2
1975
- ? deps.projectDefPresent(args.projectRoot)
1976
- : deps.projectDefPresent(args.projectRoot);
1977
- if (artifactExists) {
1978
- reEntry = "prompt";
1979
- }
1980
- }
1981
- return {
1982
- phase,
1983
- phaseLabel: bootstrapPhaseLabel(phase),
1984
- reEntry,
1985
- strategy: args.strategy,
1986
- };
1987
- }
1988
- function printDirectiveBootstrapHelp(io) {
1989
- io.writeOut("Usage: directive bootstrap [--project-root <path>] [--project] [--strategy <name>] [--reconfigure] [--force] [--json]\n\n" +
1990
- "Deposit the framework when absent, then hand off to deft-directive-setup.\n\n" +
1991
- " --project Jump to Phase 2 (project configuration)\n" +
1992
- " --strategy Jump to Phase 3 (scope vBRIEF / spec interview)\n" +
1993
- " --reconfigure Deliberate re-entry — agent should reconfigure existing artifacts\n" +
1994
- " --force Skip reconfigure-or-keep prompt; overwrite allowed\n" +
1995
- " --json Emit structured handoff JSON on stdout\n");
1996
- }
1997
- function emitBootstrapHandoff(handoff, io, json) {
1998
- if (json) {
1999
- io.writeOut(`${JSON.stringify(handoff, null, 2)}\n`);
2000
- return;
2001
- }
2002
- io.writeOut("[directive bootstrap] Setup launcher — hand off to deft-directive-setup\n\n");
2003
- io.writeOut(`project_root: ${handoff.project_root}\n`);
2004
- io.writeOut(`deposited: ${handoff.deposited}\n`);
2005
- io.writeOut(`phase: ${handoff.phase} (${handoff.phase_label})\n`);
2006
- io.writeOut(`re_entry: ${handoff.re_entry}\n`);
2007
- if (handoff.strategy !== null) {
2008
- io.writeOut(`strategy: ${handoff.strategy}\n`);
2009
- }
2010
- io.writeOut(`\nNext: Read and follow ${handoff.skill_path}\n`);
2011
- }
2012
- /** Native `directive bootstrap` handler (#2022 Phase 4). */
2013
- export async function runDirectiveBootstrap(argv, io, deps = {}) {
2014
- const merged = { ...defaultBootstrapDeps(), ...deps };
2015
- const args = parseDirectiveBootstrapArgs(argv);
2016
- if (args.error === "__help__") {
2017
- printDirectiveBootstrapHelp(io);
2018
- return 0;
2019
- }
2020
- if (args.error !== undefined) {
2021
- io.writeErr(`directive bootstrap: ${args.error}\n`);
2022
- return 2;
2023
- }
2024
- const plan = resolveDirectiveBootstrapPlan(args, merged);
2025
- let deposited = false;
2026
- if (!merged.deftCorePresent(args.projectRoot)) {
2027
- const initCode = await merged.runInitDeposit(args.projectRoot, io);
2028
- if (initCode !== 0) {
2029
- return initCode;
2030
- }
2031
- deposited = true;
2032
- }
2033
- const handoff = {
2034
- handoff: "deft-directive-setup",
2035
- skill_path: SETUP_SKILL_REL_PATH,
2036
- project_root: args.projectRoot,
2037
- deposited,
2038
- phase: plan.phase,
2039
- phase_label: plan.phaseLabel,
2040
- re_entry: plan.reEntry,
2041
- strategy: plan.strategy,
2042
- };
2043
- emitBootstrapHandoff(handoff, io, args.json);
2044
- return 0;
2045
- }
2046
- // ===========================================================================
2047
- // Native policy-set handler (#2022 Phase 1).
2048
- //
2049
- // Port of scripts/policy_set.py to native TypeScript so the typed-policy write
2050
- // path (enforce-branches / allow-direct-commits / wip-cap / subagent-backend)
2051
- // and the subagent-backends probe surface no longer shell into bundled Python.
2052
- // Behaviour parity with the Python script is preserved: the audit row appended
2053
- // to meta/policy-changes.log, the json.dumps(..., indent=2, ensure_ascii=False)
2054
- // + "\n" serialization, the disclosure text, and the exit codes
2055
- // (0 success / 1 refusal / 2 config-or-parse error).
2056
- // ===========================================================================
2057
- const POLICY_CAPABILITY_COST_DISCLOSURE = "\u26a0 Capability-cost disclosure -- enabling direct commits to the default " +
2058
- "branch turns OFF the deft branch-protection policy.\n" +
2059
- " \u2022 Pre-commit + pre-push hooks will no longer block default-branch " +
2060
- "commits.\n" +
2061
- " \u2022 verify:branch will pass on the default branch.\n" +
2062
- " \u2022 The CI sanity check (head_ref != base_ref) is still independent and " +
2063
- "will continue to flag master->master PRs.\n" +
2064
- " \u2022 This change is reversible: run `" +
2065
- policyColonInvocation("enforce-branches") +
2066
- "` to " +
2067
- "re-enable the gate.\n" +
2068
- " \u2022 The change is recorded to meta/policy-changes.log for auditability.";
2069
- const POLICY_WIP_CAP_DISCLOSURE = "\u26a0 Capability-cost disclosure -- changing plan.policy.wipCap " +
2070
- "alters the refusal threshold on task scope:promote (#1124 / D4 of #1119).\n" +
2071
- " \u2022 Raising the cap lets more vBRIEFs sit in pending/+active/ " +
2072
- "before promotion is refused.\n" +
2073
- " \u2022 Lowering the cap may put the project over cap immediately; " +
2074
- "use `task scope:demote` / `task scope:demote --batch --older-than-days 30` " +
2075
- "to drain.\n" +
2076
- " \u2022 cap=0 freezes promotion entirely (useful for code-freeze " +
2077
- "windows; restore by setting a positive value).\n" +
2078
- " \u2022 This change is reversible and recorded to " +
2079
- "meta/policy-changes.log for auditability.";
2080
- const POLICY_SET_COMMANDS = [
2081
- "enforce-branches",
2082
- "allow-direct-commits",
2083
- "wip-cap",
2084
- "subagent-backend",
2085
- "subagent-backends",
2086
- ];
2087
- /** Flags each subcommand accepts (mirrors the policy_set.py argparse subparsers). */
2088
- const POLICY_SET_ALLOWED_FLAGS = {
2089
- "enforce-branches": new Set(["--actor", "--note", "--project-root"]),
2090
- "allow-direct-commits": new Set(["--confirm", "--actor", "--note", "--project-root"]),
2091
- "wip-cap": new Set(["--set", "--confirm", "--actor", "--note", "--project-root"]),
2092
- "subagent-backend": new Set(["--set", "--actor", "--note", "--project-root"]),
2093
- "subagent-backends": new Set(["--format", "--project-root"]),
2094
- };
2095
- /** Custom error so write helpers can distinguish a missing file from a config fault. */
2096
- class PolicySetError extends Error {
2097
- kind;
2098
- constructor(message, kind) {
2099
- super(message);
2100
- this.name = "PolicySetError";
2101
- this.kind = kind;
2102
- }
2103
- }
2104
- /** Python repr() for the audit-trail `previous=` field (None / 'str' / int / bool). */
2105
- function pyRepr(value) {
2106
- if (value === undefined || value === null)
2107
- return "None";
2108
- if (typeof value === "string")
2109
- return `'${value}'`;
2110
- if (typeof value === "boolean")
2111
- return value ? "True" : "False";
2112
- return String(value);
2113
- }
2114
- /** Strip newlines so an audit note stays a single log line (mirrors policy_set.py). */
2115
- function sanitizeNote(note) {
2116
- return note.replace(/\n/g, " ").replace(/\r/g, " ");
2117
- }
2118
- function defaultPolicySetActor(cmd) {
2119
- switch (cmd) {
2120
- case "enforce-branches":
2121
- return policyColonInvocation("enforce-branches");
2122
- case "allow-direct-commits":
2123
- return policyColonInvocation("allow-direct-commits");
2124
- case "wip-cap":
2125
- return policySetInvocation("wip-cap");
2126
- case "subagent-backend":
2127
- return policySetInvocation("subagent-backend");
2128
- case "subagent-backends":
2129
- return policySetInvocation("subagent-backends");
2130
- }
2131
- }
2132
- /** Mirror Python `Path(...).expanduser()` for a leading `~` / `~/` segment. */
2133
- function expandUser(p) {
2134
- if (p === "~")
2135
- return homedir();
2136
- if (p.startsWith("~/") || p.startsWith("~\\")) {
2137
- return join(homedir(), p.slice(2));
2138
- }
2139
- return p;
2140
- }
2141
- function policySetError(message) {
2142
- return {
2143
- cmd: "enforce-branches",
2144
- confirm: false,
2145
- actor: "",
2146
- note: "",
2147
- projectRoot: ".",
2148
- format: "text",
2149
- error: message,
2150
- };
2151
- }
2152
- /** Parse `policy-set <cmd> [flags]` (mirrors the policy_set.py argparse surface). */
2153
- function parsePolicySetArgs(argv) {
2154
- const cmd = argv[0];
2155
- if (cmd === undefined) {
2156
- return policySetError("the following arguments are required: cmd");
2157
- }
2158
- if (!POLICY_SET_COMMANDS.includes(cmd)) {
2159
- return policySetError(`argument cmd: invalid choice: '${cmd}'`);
2160
- }
2161
- const command = cmd;
2162
- const allowed = POLICY_SET_ALLOWED_FLAGS[command];
2163
- const args = {
2164
- cmd: command,
2165
- confirm: false,
2166
- actor: defaultPolicySetActor(command),
2167
- note: "",
2168
- projectRoot: ".",
2169
- format: "text",
2170
- };
2171
- const rest = argv.slice(1);
2172
- for (let i = 0; i < rest.length; i += 1) {
2173
- const token = rest[i];
2174
- if (token === undefined)
2175
- continue;
2176
- let flag = token;
2177
- let inlineValue;
2178
- if (token.startsWith("--") && token.includes("=")) {
2179
- const eq = token.indexOf("=");
2180
- flag = token.slice(0, eq);
2181
- inlineValue = token.slice(eq + 1);
2182
- }
2183
- if (!allowed.has(flag)) {
2184
- return policySetError(`unrecognized arguments: ${token}`);
2185
- }
2186
- const takeValue = () => {
2187
- if (inlineValue !== undefined)
2188
- return inlineValue;
2189
- i += 1;
2190
- return rest[i];
2191
- };
2192
- if (flag === "--confirm") {
2193
- args.confirm = true;
2194
- continue;
2195
- }
2196
- const value = takeValue();
2197
- if (value === undefined) {
2198
- return policySetError(`argument ${flag}: expected one argument`);
2199
- }
2200
- if (flag === "--actor") {
2201
- args.actor = value;
2202
- }
2203
- else if (flag === "--note") {
2204
- args.note = value;
2205
- }
2206
- else if (flag === "--project-root") {
2207
- args.projectRoot = expandUser(value);
2208
- }
2209
- else if (flag === "--format") {
2210
- if (value !== "text" && value !== "json") {
2211
- return policySetError(`argument --format: invalid choice: '${value}'`);
2212
- }
2213
- args.format = value;
2214
- }
2215
- else if (flag === "--set") {
2216
- if (command === "wip-cap") {
2217
- // Python int() strips surrounding whitespace, so "--set ' 5'" parsed
2218
- // cleanly; trim before the integer check to preserve that contract.
2219
- const capText = value.trim();
2220
- if (!/^[+-]?\d+$/.test(capText)) {
2221
- return policySetError(`argument --set: invalid int value: '${value}'`);
2222
- }
2223
- args.cap = Number.parseInt(capText, 10);
2224
- }
2225
- else {
2226
- // subagent-backend --set <choice>
2227
- if (!KNOWN_SUBAGENT_BACKEND_IDS.has(value)) {
2228
- const choices = [...KNOWN_SUBAGENT_BACKEND_IDS].sort().join(", ");
2229
- return policySetError(`argument --set: invalid choice: '${value}' (choose from ${choices})`);
2230
- }
2231
- args.backendId = value;
2232
- }
2233
- }
2234
- }
2235
- if (command === "wip-cap" && args.cap === undefined) {
2236
- return policySetError("the following arguments are required: --set");
2237
- }
2238
- if (command === "subagent-backend" && args.backendId === undefined) {
2239
- return policySetError("the following arguments are required: --set");
2240
- }
2241
- return args;
2242
- }
2243
- /**
2244
- * Load PROJECT-DEFINITION for an in-place typed-field write (mirrors the
2245
- * .setdefault chain). Reads the artifact the mutation lock captured (#3796) so
2246
- * these CLI writers can no longer resolve, read, and write a PROJECT-DEFINITION
2247
- * outside the shared mutation lock.
2248
- */
2249
- function loadProjectDefinitionForWrite(mutation) {
2250
- const path = mutation.artifactPath;
2251
- if (!existsSync(path)) {
2252
- throw new PolicySetError(`PROJECT-DEFINITION not found at ${mutation.artifactLabel}`, "not-found");
2253
- }
2254
- let data;
2255
- try {
2256
- // The shared loader rejects unreadable, malformed, and non-object top-level
2257
- // payloads; every one of those is a config error for the CLI writers.
2258
- data = mutation.load();
2259
- }
2260
- catch (err) {
2261
- throw new PolicySetError(err instanceof Error ? err.message : String(err), "config");
2262
- }
2263
- let plan = data.plan;
2264
- if (plan === undefined) {
2265
- plan = {};
2266
- data.plan = plan;
2267
- }
2268
- if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
2269
- throw new PolicySetError("PROJECT-DEFINITION 'plan' is not an object", "config");
2270
- }
2271
- const planObj = plan;
2272
- const legacyMigrated = migrateLegacyPolicyKey(planObj);
2273
- let policy = planObj[PLAN_POLICY_KEY];
2274
- if (policy === undefined) {
2275
- policy = {};
2276
- planObj[PLAN_POLICY_KEY] = policy;
2277
- }
2278
- if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
2279
- throw new PolicySetError("plan.policy is not an object", "config");
2280
- }
2281
- return { data, policyBlock: policy, legacyMigrated };
2282
- }
2283
- /** Write plan.policy.wipCap in place + append the audit row (mirrors set_wip_cap). */
2284
- function writeWipCap(projectRoot, cap, actor, note) {
2285
- return withProjectDefinitionMutation(projectRoot, (mutation) => {
2286
- const { data, policyBlock, legacyMigrated } = loadProjectDefinitionForWrite(mutation);
2287
- const previous = policyBlock.wipCap;
2288
- policyBlock.wipCap = cap;
2289
- const changed = previous !== cap || legacyMigrated;
2290
- const parts = [`actor=${actor}`, `wipCap=${cap}`, `previous=${pyRepr(previous)}`];
2291
- if (note)
2292
- parts.push(`note=${sanitizeNote(note)}`);
2293
- const auditEntry = stampChangedToken(parts.join(" "), changed);
2294
- if (changed) {
2295
- mutation.persist(data);
2296
- }
2297
- appendAuditLog(projectRoot, auditEntry, changed);
2298
- return { changed, auditEntry };
2299
- });
2300
- }
2301
- /** Write plan.policy.swarmSubagentBackend in place + append the audit row. */
2302
- function writeSubagentBackend(projectRoot, backendId, actor, note) {
2303
- return withProjectDefinitionMutation(projectRoot, (mutation) => {
2304
- const { data, policyBlock, legacyMigrated } = loadProjectDefinitionForWrite(mutation);
2305
- const previous = policyBlock.swarmSubagentBackend;
2306
- policyBlock.swarmSubagentBackend = backendId;
2307
- const changed = previous !== backendId || legacyMigrated;
2308
- const parts = [
2309
- `actor=${actor}`,
2310
- `swarmSubagentBackend=${backendId}`,
2311
- `previous=${pyRepr(previous)}`,
2312
- ];
2313
- if (note)
2314
- parts.push(`note=${sanitizeNote(note)}`);
2315
- const auditEntry = stampChangedToken(parts.join(" "), changed);
2316
- if (changed) {
2317
- mutation.persist(data);
2318
- }
2319
- appendAuditLog(projectRoot, auditEntry, changed);
2320
- return { changed, auditEntry };
2321
- });
2322
- }
2323
- /** Serialise the probe output for `subagent-backends --format json`. */
2324
- function subagentBackendsToJson(backends) {
2325
- const payload = {
2326
- backends: backends.map((entry) => ({
2327
- id: entry.backend_id,
2328
- display_name: entry.display_name,
2329
- roles: [...entry.roles],
2330
- available: entry.available,
2331
- })),
2332
- };
2333
- return JSON.stringify(payload, null, 2);
2334
- }
2335
- /** Map a write-path error to its fail-closed message + exit code (mirrors the except blocks). */
2336
- function reportPolicyWriteError(err, io) {
2337
- const message = err instanceof Error ? err.message : String(err);
2338
- if (err instanceof PolicySetError && err.kind === "not-found") {
2339
- io.writeErr(`\u274c ${message}\n`);
2340
- io.writeErr(" Recovery: run `task setup` to generate vbrief/PROJECT-DEFINITION.vbrief.json.\n");
2341
- return 2;
2342
- }
2343
- io.writeErr(`\u274c Config error: ${message}\n`);
2344
- return 2;
2345
- }
2346
- function applyBranchPolicy(args, io) {
2347
- let target;
2348
- if (args.cmd === "enforce-branches") {
2349
- target = false;
2350
- }
2351
- else {
2352
- if (!args.confirm) {
2353
- io.writeOut(`${POLICY_CAPABILITY_COST_DISCLOSURE}\n`);
2354
- io.writeOut("\n");
2355
- io.writeOut(`Re-run with --confirm to apply: ${policyColonInvocation("allow-direct-commits", " -- --confirm")}\n`);
2356
- return 1;
2357
- }
2358
- target = true;
2359
- }
2360
- let result;
2361
- try {
2362
- result = setPolicy(args.projectRoot, {
2363
- allowDirectCommits: target,
2364
- actor: args.actor,
2365
- note: args.note,
2366
- });
2367
- }
2368
- catch (err) {
2369
- const message = err instanceof Error ? err.message : String(err);
2370
- if (message.includes("PROJECT-DEFINITION not found")) {
2371
- io.writeErr(`\u274c ${message}\n`);
2372
- io.writeErr(" Recovery: run `task setup` to generate vbrief/PROJECT-DEFINITION.vbrief.json.\n");
2373
- return 2;
2374
- }
2375
- io.writeErr(`\u274c Config error: ${message}\n`);
2376
- return 2;
2377
- }
2378
- const state = target ? "OFF" : "ON";
2379
- io.writeOut(`\u2713 plan.policy.allowDirectCommitsToMaster=${target ? "true" : "false"} ` +
2380
- `(branch-protection ${state}).\n`);
2381
- if (result.changed) {
2382
- io.writeOut(` audit: meta/policy-changes.log :: ${result.auditEntry}\n`);
2383
- }
2384
- else {
2385
- io.writeOut(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
2386
- }
2387
- io.writeOut(`${disclosureLine(resolvePolicy(args.projectRoot))}\n`);
2388
- return 0;
2389
- }
2390
- function applyWipCap(args, io) {
2391
- const cap = args.cap ?? 0;
2392
- if (cap < 0) {
2393
- io.writeErr(`\u274c --set must be >= 0; got ${cap}.\n`);
2394
- return 1;
2395
- }
2396
- if (!args.confirm) {
2397
- io.writeOut(`${POLICY_WIP_CAP_DISCLOSURE}\n`);
2398
- io.writeOut("\n");
2399
- io.writeOut(`Re-run with --confirm to apply: ${policySetInvocation("wip-cap", ` -- --set ${cap} --confirm`)}\n`);
2400
- return 1;
2401
- }
2402
- let res;
2403
- try {
2404
- res = writeWipCap(args.projectRoot, cap, args.actor, args.note);
2405
- }
2406
- catch (err) {
2407
- return reportPolicyWriteError(err, io);
2408
- }
2409
- io.writeOut(`\u2713 plan.policy.wipCap=${cap}.\n`);
2410
- if (res.changed) {
2411
- io.writeOut(` audit: meta/policy-changes.log :: ${res.auditEntry}\n`);
2412
- }
2413
- else {
2414
- io.writeOut(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
2415
- }
2416
- const result = resolveWipCap(args.projectRoot);
2417
- io.writeOut(`[deft policy] plan.policy.wipCap=${result.cap} (source: ${result.source}).\n`);
2418
- return 0;
2419
- }
2420
- function applySubagentBackend(args, io) {
2421
- const backendId = args.backendId ?? "";
2422
- let res;
2423
- try {
2424
- res = writeSubagentBackend(args.projectRoot, backendId, args.actor, args.note);
2425
- }
2426
- catch (err) {
2427
- return reportPolicyWriteError(err, io);
2428
- }
2429
- io.writeOut(`\u2713 plan.policy.swarmSubagentBackend=${backendId}.\n`);
2430
- if (res.changed) {
2431
- io.writeOut(` audit: meta/policy-changes.log :: ${res.auditEntry}\n`);
2432
- }
2433
- else {
2434
- io.writeOut(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
2435
- }
2436
- const result = resolveSwarmSubagentBackend(args.projectRoot);
2437
- io.writeOut(`[deft policy] plan.policy.swarmSubagentBackend=${pyRepr(result.backend_id)} ` +
2438
- `(source: ${result.source}).\n`);
2439
- return 0;
2440
- }
2441
- function applySubagentBackends(args, io) {
2442
- const entries = probeSubagentBackends();
2443
- if (args.format === "json") {
2444
- io.writeOut(`${subagentBackendsToJson(entries)}\n`);
2445
- return 0;
2446
- }
2447
- for (const entry of entries) {
2448
- const roles = entry.roles.join(", ");
2449
- const avail = entry.available ? "available" : "unavailable";
2450
- io.writeOut(`${entry.backend_id}\t${entry.display_name}\troles=[${roles}]\t${avail}\n`);
2451
- }
2452
- return 0;
2453
- }
2454
- /** Native `policy-set` dispatcher (replaces the policy_set.py shell-out, #2022 Phase 1). */
2455
- export function runPolicySet(argv, io) {
2456
- const args = parsePolicySetArgs(argv);
2457
- if (args.error !== undefined) {
2458
- io.writeErr(`policy-set: ${args.error}\n`);
2459
- return 2;
2460
- }
2461
- switch (args.cmd) {
2462
- case "enforce-branches":
2463
- case "allow-direct-commits":
2464
- return applyBranchPolicy(args, io);
2465
- case "wip-cap":
2466
- return applyWipCap(args, io);
2467
- case "subagent-backend":
2468
- return applySubagentBackend(args, io);
2469
- case "subagent-backends":
2470
- return applySubagentBackends(args, io);
2471
- }
2472
- }
2473
- async function loadCoreModuleHandler(verb, io) {
2474
- switch (verb) {
2475
- case "scm": {
2476
- const { main } = await import("@deftai/directive-core/dist/scm/main.js");
2477
- return (argv) => main(argv);
2478
- }
2479
- case "scm-readiness": {
2480
- const { mainEntry } = await import("@deftai/directive-core/dist/scm/readiness-cli.js");
2481
- return mainEntry;
2482
- }
2483
- case "github-auth-modes": {
2484
- const { mainEntry } = await import("@deftai/directive-core/dist/intake/github-auth-modes-cli.js");
2485
- return mainEntry;
2486
- }
2487
- case "github-body": {
2488
- const { mainEntry } = await import("@deftai/directive-core/dist/intake/github-body-cli.js");
2489
- return mainEntry;
2490
- }
2491
- case "rule-to-verb-parity": {
2492
- const { mainEntry } = await import("@deftai/directive-core/dist/check/rule-to-verb-parity.js");
2493
- return mainEntry;
2494
- }
2495
- case "issue-emit": {
2496
- const { mainEntry } = await import("@deftai/directive-core/dist/intake/issue-emit-cli.js");
2497
- return mainEntry;
2498
- }
2499
- case "issue-ingest": {
2500
- const { mainEntry } = await import("@deftai/directive-core/dist/intake/issue-ingest-cli.js");
2501
- return mainEntry;
2502
- }
2503
- case "issue-sync-from-xbrief": {
2504
- const { mainEntry } = await import("@deftai/directive-core/dist/issue-sync/sync-from-xbrief-cli.js");
2505
- return mainEntry;
2506
- }
2507
- case "reconcile-issues": {
2508
- const { mainEntry } = await import("@deftai/directive-core/dist/intake/reconcile-issues-cli.js");
2509
- return mainEntry;
2510
- }
2511
- case "swarm-launch": {
2512
- const { launchMain } = await import("@deftai/directive-core/dist/swarm/launch-cli.js");
2513
- return launchMain;
2514
- }
2515
- case "swarm-complete-cohort": {
2516
- const { completeCohortMain } = await import("@deftai/directive-core/dist/swarm/complete-cohort-cli.js");
2517
- return completeCohortMain;
2518
- }
2519
- case "swarm-finalize-cohort": {
2520
- const { finalizeCohortMain } = await import("@deftai/directive-core/dist/swarm/finalize-cohort-cli.js");
2521
- return finalizeCohortMain;
2522
- }
2523
- case "swarm-finalize-owed": {
2524
- const { finalizeOwedMain } = await import("@deftai/directive-core/dist/swarm/finalize-owed-cli.js");
2525
- return finalizeOwedMain;
2526
- }
2527
- case "swarm-pre-dispatch": {
2528
- const { preDispatchMain } = await import("@deftai/directive-core/dist/swarm/pre-dispatch-cli.js");
2529
- return preDispatchMain;
2530
- }
2531
- case "swarm-readiness": {
2532
- const { readinessMain } = await import("@deftai/directive-core/dist/swarm/readiness-cli.js");
2533
- return readinessMain;
2534
- }
2535
- case "swarm-routing-verify": {
2536
- const { routingVerifyMain } = await import("@deftai/directive-core/dist/swarm/routing-verify-cli.js");
2537
- return routingVerifyMain;
2538
- }
2539
- case "swarm-routing-set": {
2540
- const { routingSetMain } = await import("@deftai/directive-core/dist/swarm/routing-set-cli.js");
2541
- return routingSetMain;
2542
- }
2543
- case "swarm-verify-review-clean": {
2544
- const { verifyReviewCleanMain } = await import("@deftai/directive-core/dist/swarm/verify-review-clean-cli.js");
2545
- return verifyReviewCleanMain;
2546
- }
2547
- case "swarm-worktrees": {
2548
- const { worktreesMain } = await import("@deftai/directive-core/dist/swarm/worktrees-cli.js");
2549
- return worktreesMain;
2550
- }
2551
- case "framework-commands": {
2552
- const { frameworkCommandsMain } = await import("@deftai/directive-core/render");
2553
- return (argv) => frameworkCommandsMain(argv);
2554
- }
2555
- case "pack-render": {
2556
- const { main } = await import("@deftai/directive-core/dist/packs/pack-render.js");
2557
- return (argv) => main([...argv]);
2558
- }
2559
- case "packs-slice": {
2560
- const { main } = await import("@deftai/directive-core/dist/packs/packs-slice.js");
2561
- return (argv) => main([...argv]);
2562
- }
2563
- case "roadmap-render": {
2564
- const { main } = await import("@deftai/directive-core/dist/render/roadmap-render.js");
2565
- return (argv) => main(argv);
2566
- }
2567
- case "spec-validate": {
2568
- const { runSpecValidateCli } = await import("./render-cli/spec-validate-cli.js");
2569
- return (argv) => runSpecValidateCli(argv);
2570
- }
2571
- case "spec-reconstruct": {
2572
- const { runSpecReconstructCliEntry } = await import("./render-cli/spec-reconstruct-cli.js");
2573
- return (argv) => runSpecReconstructCliEntry(argv);
2574
- }
2575
- case "spec-render": {
2576
- const { runSpecRenderCli } = await import("./render-cli/spec-render-cli.js");
2577
- return (argv) => runSpecRenderCli(argv);
2578
- }
2579
- case "prd-render": {
2580
- const { runPrdRenderCli } = await import("./render-cli/prd-render-cli.js");
2581
- return (argv) => runPrdRenderCli(argv);
2582
- }
2583
- case "project-render": {
2584
- const { runProjectRenderCli } = await import("./render-cli/project-render-cli.js");
2585
- return (argv) => runProjectRenderCli(argv);
2586
- }
2587
- case "project-write-narratives": {
2588
- const { writePhase2NarrativesMain } = await import("@deftai/directive-core/dist/render/phase2-narratives-cli.js");
2589
- return (argv) => writePhase2NarrativesMain(argv);
2590
- }
2591
- case "setup-seed": {
2592
- const { seedFromDesignMain } = await import("@deftai/directive-core/dist/setup/seed-from-design.js");
2593
- return (argv) => seedFromDesignMain(argv);
2594
- }
2595
- case "rule-map": {
2596
- const { runRuleMapCli } = await import("./render-cli/rule-map-cli.js");
2597
- return (argv) => runRuleMapCli(argv);
2598
- }
2599
- case "export-spec": {
2600
- const { runExportSpecCli } = await import("./render-cli/export-spec-cli.js");
2601
- return (argv) => runExportSpecCli(argv);
2602
- }
2603
- case "code-structure-validate": {
2604
- const { evaluateCodeStructure } = await import("@deftai/directive-core/verify-source");
2605
- return (argv) => {
2606
- const parsed = parseCodeStructureArgs(argv);
2607
- if (parsed.error !== undefined) {
2608
- io.writeErr(`code_structure_validate: ${parsed.error}\n`);
2609
- return 2;
2610
- }
2611
- const result = evaluateCodeStructure(parsed.projectRoot, {
2612
- paths: parsed.paths.length > 0 ? parsed.paths : undefined,
2613
- json: parsed.json,
2614
- strict: parsed.strict,
2615
- enforce: parsed.enforce,
2616
- });
2617
- if (result.stdout)
2618
- io.writeOut(result.stdout);
2619
- if (result.stderr)
2620
- io.writeErr(result.stderr);
2621
- return result.code;
2622
- };
2623
- }
2624
- case "pack-migrate-skills":
2625
- return (argv) => runPackMigrateSkills(argv, io);
2626
- case "pack-migrate-rules":
2627
- return (argv) => runPackMigrateRules(argv, io);
2628
- case "pack-migrate-strategies":
2629
- return (argv) => runPackMigrateStrategies(argv, io);
2630
- case "pack-migrate-patterns":
2631
- return (argv) => runPackMigratePatterns(argv, io);
2632
- case "pack-migrate-swarm-spec":
2633
- return (argv) => runPackMigrateSwarmSpec(argv, io);
2634
- case "policy-set":
2635
- return (argv) => runPolicySet(argv, io);
2636
- case "setup-ghx":
2637
- return (argv) => runSetupGhx(argv, io);
2638
- case "scope-undo": {
2639
- const { undoMain } = await import("@deftai/directive-core/dist/scope/main.js");
2640
- return undoMain;
2641
- }
2642
- case "scope-demote": {
2643
- const { demoteMain } = await import("@deftai/directive-core/dist/scope/main.js");
2644
- return demoteMain;
2645
- }
2646
- case "scope-decompose": {
2647
- const { decomposeMain } = await import("@deftai/directive-core/dist/scope/decompose.js");
2648
- return decomposeMain;
2649
- }
2650
- case "changelog-resolve-unreleased": {
2651
- const { changelogResolveUnreleasedMain } = await import("@deftai/directive-core/dist/platform/changelog-cli.js");
2652
- return changelogResolveUnreleasedMain;
2653
- }
2654
- case "architecture-preflight-sor": {
2655
- const { architecturePreflightSorMain } = await import("@deftai/directive-core/dist/architecture/sor-preflight.js");
2656
- return architecturePreflightSorMain;
2657
- }
2658
- case "feedback-file": {
2659
- const { mainEntry } = await import("@deftai/directive-core/dist/value/feedback-file.js");
2660
- return mainEntry;
2661
- }
2662
- case "value-readback": {
2663
- const { mainEntry } = await import("@deftai/directive-core/dist/value/readback.js");
2664
- return mainEntry;
2665
- }
2666
- case "product-signal": {
2667
- const { mainEntry } = await import("@deftai/directive-core/dist/product-signal/submit.js");
2668
- return mainEntry;
2669
- }
2670
- case "freshness-report": {
2671
- const { mainEntry } = await import("@deftai/directive-core/dist/freshness/cli.js");
2672
- return mainEntry;
2673
- }
2674
- case "decision-write": {
2675
- const { writeMainEntry } = await import("@deftai/directive-core/dist/decision/index.js");
2676
- return writeMainEntry;
2677
- }
2678
- case "decision-list": {
2679
- const { listMainEntry } = await import("@deftai/directive-core/dist/decision/index.js");
2680
- return listMainEntry;
2681
- }
2682
- case "docs-impact": {
2683
- const { docsImpactMain } = await import("@deftai/directive-core/dist/docs/docs-impact.js");
2684
- return (argv) => docsImpactMain(argv);
2685
- }
2686
- case "tutorial": {
2687
- const { tutorialMain } = await import("@deftai/directive-core/dist/tutorial/cli.js");
2688
- return tutorialMain;
2689
- }
2690
- case "migrate-clause-ids": {
2691
- const { mainEntry } = await import("@deftai/directive-core/dist/xbrief-migrate/clause-ids.js");
2692
- return (argv) => mainEntry(argv);
2693
- }
2694
- case "migrate-confidence": {
2695
- const { mainEntry } = await import("@deftai/directive-core/dist/xbrief-migrate/confidence.js");
2696
- return (argv) => mainEntry(argv);
2697
- }
2698
- default:
2699
- throw new Error(`unknown core verb: ${verb}`);
2700
- }
2701
- }
2702
- const handlerCache = new Map();
2703
- function loadHandler(canonical, io) {
2704
- let pending = handlerCache.get(canonical);
2705
- if (pending === undefined) {
2706
- pending = CLI_MODULE_VERBS.includes(canonical)
2707
- ? loadCliModuleHandler(canonical, io)
2708
- : loadCoreModuleHandler(canonical, io);
2709
- handlerCache.set(canonical, pending);
2710
- }
2711
- return pending;
2712
- }
2713
- function defaultIo() {
2714
- return {
2715
- writeOut: (text) => {
2716
- process.stdout.write(text);
2717
- },
2718
- writeErr: (text) => {
2719
- process.stderr.write(text);
2720
- },
2721
- };
2722
- }
2723
- /** Resolve a user-facing verb to its canonical handler key. */
2724
- export function resolveCanonicalVerb(verb) {
2725
- if (CLI_MODULE_VERBS.includes(verb))
2726
- return verb;
2727
- if (CORE_MODULE_VERBS.includes(verb))
2728
- return verb;
2729
- const alias = VERB_ALIASES[verb];
2730
- if (alias !== undefined)
2731
- return alias;
2732
- return null;
2733
- }
2734
- /** Sorted list of all registered verb names (canonical + aliases). */
2735
- export function registeredVerbs() {
2736
- const names = new Set([
2737
- ...CLI_MODULE_VERBS,
2738
- ...CORE_MODULE_VERBS,
2739
- ...Object.keys(VERB_ALIASES),
2740
- ]);
2741
- return [...names].sort();
2742
- }
2743
- /** Top-level UX commands routed before the flat dispatcher (#1670). */
2744
- const TOP_LEVEL_COMMAND_NAMES = [
2745
- "init",
2746
- "update",
2747
- "migrate",
2748
- "bootstrap",
2749
- "doctor",
2750
- "check",
2751
- ];
2752
- /** Scope lifecycle verbs exposed as scope:<verb> in help (#2172). */
2753
- const SCOPE_COMMAND_NAMES = [
2754
- "scope:promote",
2755
- "scope:activate",
2756
- "scope:complete",
2757
- "scope:demote",
2758
- "scope:undo",
2759
- "scope:record-approved-scope",
2760
- "scope:record-observable-scope",
2761
- "scope:record-intent-constraint",
2762
- ];
2763
- /**
2764
- * Static dual-invoke set for scm:body:* (#5521). Mirror of
2765
- * packages/cli/src/cli-router/route-argv.ts SCM_BODY_COLON_VERBS — kept here
2766
- * so dispatch does not import route-argv (route-argv already imports dispatch).
2767
- */
2768
- const SCM_BODY_DUAL_INVOKE_VERBS = new Set([
2769
- "scm:body:issue:create",
2770
- "scm:body:issue:edit",
2771
- "scm:body:issue:fetch",
2772
- "scm:body:issue:lint",
2773
- "scm:body:comment:create",
2774
- "scm:body:comment:edit",
2775
- "scm:body:pr:edit",
2776
- "scm:body:pr:lint",
2777
- ]);
2778
- /**
2779
- * Deduplicated command names for `directive commands`, preferring colon-style
2780
- * task verbs over dash-style canonical stems when both exist (#2172).
2781
- */
2782
- export function preferredCommandNames() {
2783
- const aliasKeys = Object.keys(VERB_ALIASES);
2784
- const aliasedCanonicals = new Set(Object.values(VERB_ALIASES));
2785
- const unaliasedCanonicals = [...CLI_MODULE_VERBS, ...CORE_MODULE_VERBS].filter((verb) => !aliasedCanonicals.has(verb));
2786
- return [
2787
- ...new Set([
2788
- ...TOP_LEVEL_COMMAND_NAMES,
2789
- ...SCOPE_COMMAND_NAMES,
2790
- ...aliasKeys,
2791
- ...SCM_BODY_DUAL_INVOKE_VERBS,
2792
- ...unaliasedCanonicals,
2793
- ]),
2794
- ].sort();
2795
- }
2796
- /** Major.minor label for the curated help title (#2172). */
2797
- export function helpVersionLabel() {
2798
- const version = engineInfo().version;
2799
- const match = /^(\d+\.\d+)/.exec(version);
2800
- return match ? `v${match[1]}` : `v${version}`;
2801
- }
2802
- const CURATED_HELP_GROUPS = [
2803
- {
2804
- title: "Getting started",
2805
- commands: [
2806
- { name: "init", summary: "Set up Directive in the current project (first-time setup)" },
2807
- { name: "update", summary: "Refresh an existing install and self-heal the engine" },
2808
- { name: "doctor", summary: "Diagnose the install and print the one next step" },
2809
- ],
2810
- },
2811
- {
2812
- title: "Session & ritual",
2813
- commands: [
2814
- {
2815
- name: "session:start",
2816
- summary: "Claim occupancy and record ritual state for one session owner",
2817
- },
2818
- {
2819
- name: "session:ready",
2820
- summary: "One-shot recovery to gated write-ready (session + ritual + cache)",
2821
- },
2822
- {
2823
- name: "occupancy:steal",
2824
- summary: "Replace only the live lease; use session:start --steal to align ritual",
2825
- },
2826
- {
2827
- name: "occupancy:release",
2828
- summary: "Release this worktree occupancy lease (owner live, or expired residue)",
2829
- },
2830
- {
2831
- name: "occupancy:heartbeat",
2832
- summary: "Refresh your own live lease during long quiet stretches (never claims)",
2833
- },
2834
- {
2835
- name: "occupancy:grant",
2836
- summary: "Admit a dispatched child to your lease for writes (--revoke withdraws)",
2837
- },
2838
- {
2839
- name: "session:end",
2840
- summary: "End this mutation session by releasing occupancy (same as occupancy:release)",
2841
- },
2842
- {
2843
- name: "freshness:report",
2844
- summary: "Bound vs live deposit generation (current|stale_soft|stale_hard)",
2845
- },
2846
- {
2847
- name: "freshness:bind",
2848
- summary: "Bind live deposit generation into this session (no host restart)",
2849
- },
2850
- {
2851
- name: "scm:status",
2852
- summary: "Probe gh/ghx + auth readiness in this execution env (#2275)",
2853
- },
2854
- {
2855
- name: "lifecycle:event",
2856
- summary: "Record review-cycle plan:approved approval events",
2857
- },
2858
- {
2859
- name: "lifecycle:stats",
2860
- summary: "Local xBRIEF lifecycle folder counts for process rollups",
2861
- },
2862
- ],
2863
- },
2864
- {
2865
- title: "Quality & gates",
2866
- commands: [{ name: "check", summary: "Run install and lifecycle quality gates" }],
2867
- },
2868
- {
2869
- title: "Work queue & triage",
2870
- commands: [
2871
- { name: "triage:welcome", summary: "Session orientation and triage one-liner" },
2872
- { name: "triage:queue", summary: "Ranked work queue for what to do next" },
2873
- ],
2874
- },
2875
- {
2876
- title: "Scope lifecycle",
2877
- commands: [{ name: "scope:promote", summary: "Promote a scope xBRIEF to pending" }],
2878
- },
2879
- {
2880
- title: "xBRIEF create/verify (not lifecycle)",
2881
- commands: [
2882
- {
2883
- name: "xbrief:create",
2884
- summary: "Write a dense xBRIEF artifact at --out (requires --format); not scope:*",
2885
- },
2886
- {
2887
- name: "xbrief:verify",
2888
- summary: "Verify a dense xBRIEF artifact at --out (requires --format); not scope:*",
2889
- },
2890
- {
2891
- name: "xbrief:adopt-stored-plan-id",
2892
- summary: "Set plan.id from the stored plan-id binding; refuse if another artifact has it",
2893
- },
2894
- ],
2895
- },
2896
- {
2897
- title: "Project artifacts",
2898
- commands: [
2899
- { name: "project:render", summary: "Render PROJECT-DEFINITION projection" },
2900
- {
2901
- name: "project:write-narratives",
2902
- summary: "Store Phase 2 narratives in PROJECT-DEFINITION without policy keys",
2903
- },
2904
- {
2905
- name: "setup:seed",
2906
- summary: "Draft Phase 2 narratives from --from design.md or --answers file (confirm before write)",
2907
- },
2908
- { name: "spec:render", summary: "Render specification projection" },
2909
- ],
2910
- },
2911
- ];
2912
- function formatHelpCommand(command) {
2913
- const padding = " ".repeat(Math.max(1, 22 - command.name.length));
2914
- return ` ${command.name}${padding}${command.summary}\n`;
2915
- }
2916
- /**
2917
- * Print the exhaustive registered-command list for `directive commands` (#2172).
2918
- * Lists deduplicated preferred names (colon-style when available).
2919
- */
2920
- export function printCommandsList(io = defaultIo()) {
2921
- io.writeOut("Registered commands:\n");
2922
- for (const name of preferredCommandNames()) {
2923
- io.writeOut(` ${name}\n`);
2924
- }
2925
- }
2926
- /**
2927
- * Print curated top-level help: title/version, usage, common options, grouped
2928
- * common commands, and a pointer to `directive commands` for the full list
2929
- * (#2172). Preserves the init/update/doctor first-run guidance from #2273.
2930
- */
2931
- export function printHelp(io = defaultIo()) {
2932
- io.writeOut(`Directive ${helpVersionLabel()}\n`);
2933
- io.writeOut("AI development framework CLI for project lifecycle, scope, and quality gates.\n\n");
2934
- io.writeOut("Usage:\n");
2935
- io.writeOut(" directive <command> [options]\n");
2936
- io.writeOut(" directive help\n");
2937
- io.writeOut(" directive commands List every registered command\n\n");
2938
- io.writeOut("Options:\n");
2939
- io.writeOut(" -h, --help Show this help\n");
2940
- io.writeOut(" -V, --version Print version information\n\n");
2941
- for (const group of CURATED_HELP_GROUPS) {
2942
- io.writeOut(`${group.title}:\n`);
2943
- for (const command of group.commands) {
2944
- io.writeOut(formatHelpCommand(command));
2945
- }
2946
- io.writeOut("\n");
2947
- }
2948
- io.writeOut("Commands use colon style (e.g. triage:queue); dash-style aliases remain supported.\n" +
2949
- "Run `directive commands` for the full registered-command list.\n\n" +
2950
- "First run? From the project root:\n" +
2951
- " 1. npm i -g @deftai/directive (Node >= 20)\n" +
2952
- " (pnpm: pnpm add -g @deftai/directive -- ensure PNPM_HOME is on PATH, run `pnpm setup` if needed)\n" +
2953
- " 2. directive init\n" +
2954
- " 3. directive doctor\n" +
2955
- " 4. directive toolchain:check --consumer\n" +
2956
- "New clone where `directive` will not run? Read the Cold-start bootstrap block at the top of README.md.\n");
2957
- }
2958
- async function invokeHandler(handler, argv) {
2959
- const code = await handler(argv);
2960
- return typeof code === "number" ? code : 0;
2961
- }
2962
- const PLAN_SEQUENCE_VERBS = "set|current|clear|advance";
2963
- /**
2964
- * Unknown-colon-verb remediation (#3439 / #2652 / #5521).
2965
- * Never re-suggest the failing `deft <verb>` spelling. Prefer Taskfile
2966
- * dual-invoke forms when known; demote hyphen stems; no `task --list` shell-out.
2967
- */
2968
- export function unknownColonVerbHint(verb, context = "unknown") {
2969
- if (verb.startsWith("plan-sequence:") && !(verb in PLAN_SEQUENCE_ALIAS_SUBCOMMANDS)) {
2970
- return (`hint: plan-sequence verbs are ${PLAN_SEQUENCE_VERBS} ` +
2971
- `(e.g. \`deft plan-sequence:current\`). '${verb}' is not a verb. ` +
2972
- `Do not run bare \`task ${verb}\` -- consumer include-only Taskfiles cannot resolve it.\n`);
2973
- }
2974
- const hyphen = verb.replaceAll(":", "-");
2975
- const hyphenNote = `Hyphen stem \`${hyphen}\` is not primary recovery.`;
2976
- const listing = "List registered CLI verbs with `deft commands`.";
2977
- if (SCM_BODY_DUAL_INVOKE_VERBS.has(verb)) {
2978
- if (context === "include-only") {
2979
- return (`hint: try \`task deft:${verb}\` (consumer include Taskfile). ` +
2980
- `Bare \`task ${verb}\` is framework-only. ${hyphenNote} ${listing}\n`);
2981
- }
2982
- if (context === "no-taskfile") {
2983
- return (`hint: Taskfile recovery is unavailable here. ` +
2984
- `On a framework checkout with go-task: \`task ${verb}\`; ` +
2985
- `on an include-only deposit: \`task deft:${verb}\`. ${hyphenNote} ${listing}\n`);
2986
- }
2987
- if (context === "framework") {
2988
- return (`hint: try \`task ${verb}\` (framework Taskfile) or \`task deft:${verb}\` (consumer include). ` +
2989
- `${hyphenNote} ${listing}\n`);
2990
- }
2991
- return (`hint: if go-task is available, try \`task ${verb}\` (framework) or ` +
2992
- `\`task deft:${verb}\` (consumer include) — Taskfile recovery is conditional. ` +
2993
- `${hyphenNote} ${listing}\n`);
2994
- }
2995
- if (context === "include-only") {
2996
- return (`hint: do not retry the same \`deft ${verb}\` spelling. ` +
2997
- `If a Taskfile task exists, try \`task deft:${verb}\` (include-only). ` +
2998
- `${hyphenNote} ${listing}\n`);
2999
- }
3000
- if (context === "no-taskfile") {
3001
- return (`hint: do not retry the same \`deft ${verb}\` spelling. ` +
3002
- `Taskfile recovery is unavailable here. ${hyphenNote} ${listing}\n`);
3003
- }
3004
- if (context === "framework") {
3005
- return (`hint: do not retry the same \`deft ${verb}\` spelling. ` +
3006
- `If a Taskfile task exists, try \`task ${verb}\` (framework) or \`task deft:${verb}\` (include). ` +
3007
- `${hyphenNote} ${listing}\n`);
3008
- }
3009
- return (`hint: do not retry the same \`deft ${verb}\` spelling. ` +
3010
- `If a Taskfile task exists, try \`task ${verb}\` (framework) or \`task deft:${verb}\` (include) — ` +
3011
- `Taskfile recovery is conditional. ${hyphenNote} ${listing}\n`);
3012
- }
3013
- function versionBanner() {
3014
- return `${identityBanner()}\n`;
3015
- }
3016
- /** Dispatch argv to a registered verb; returns the handler exit code. */
3017
- export async function dispatch(argv, io = defaultIo()) {
3018
- if (argv[0] === "--version" || argv[0] === "-V") {
3019
- io.writeOut(versionBanner());
3020
- return 0;
3021
- }
3022
- if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
3023
- printHelp(io);
3024
- return 0;
3025
- }
3026
- const [verb, ...rest] = argv;
3027
- if (verb === "commands") {
3028
- printCommandsList(io);
3029
- return 0;
3030
- }
3031
- const canonical = resolveCanonicalVerb(verb ?? "");
3032
- if (canonical === null) {
3033
- io.writeErr(`directive: unknown verb '${verb}'\n`);
3034
- if (verb?.includes(":")) {
3035
- io.writeErr(unknownColonVerbHint(verb));
3036
- }
3037
- return 1;
3038
- }
3039
- try {
3040
- const triageSubcommand = verb !== undefined ? TRIAGE_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
3041
- const policySubcommand = verb !== undefined ? POLICY_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
3042
- const authzSubcommand = verb !== undefined ? AUTHZ_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
3043
- const escalationSubcommand = verb !== undefined ? ESCALATION_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
3044
- const planSequenceSubcommand = verb !== undefined ? PLAN_SEQUENCE_ALIAS_SUBCOMMANDS[verb] : undefined;
3045
- const tutorialSubcommand = verb !== undefined ? TUTORIAL_ALIAS_SUBCOMMANDS[verb] : undefined;
3046
- const productSignalSubcommand = verb !== undefined ? PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS[verb] : undefined;
3047
- const freshnessSubcommand = verb !== undefined ? FRESHNESS_ALIAS_SUBCOMMANDS[verb] : undefined;
3048
- const vbriefValidateSubcommand = verb === "verify:vbrief-conformance" && canonical === "vbrief-validate"
3049
- ? "conformance"
3050
- : undefined;
3051
- const handlerArgv = canonical === "framework-commands" && verb !== undefined && verb !== canonical
3052
- ? [verb, ...rest]
3053
- : triageSubcommand !== undefined && canonical === "triage-actions"
3054
- ? [triageSubcommand, ...rest]
3055
- : policySubcommand !== undefined && canonical === "policy"
3056
- ? [policySubcommand, ...rest]
3057
- : authzSubcommand !== undefined && canonical === "authz"
3058
- ? [authzSubcommand, ...rest]
3059
- : escalationSubcommand !== undefined && canonical === "escalation-cli"
3060
- ? [escalationSubcommand, ...rest]
3061
- : planSequenceSubcommand !== undefined && canonical === "plan-sequence"
3062
- ? [planSequenceSubcommand, ...rest]
3063
- : tutorialSubcommand !== undefined && canonical === "tutorial"
3064
- ? [tutorialSubcommand, ...rest]
3065
- : productSignalSubcommand !== undefined && canonical === "product-signal"
3066
- ? [productSignalSubcommand, ...rest]
3067
- : freshnessSubcommand !== undefined && canonical === "freshness-report"
3068
- ? [freshnessSubcommand, ...rest]
3069
- : vbriefValidateSubcommand !== undefined
3070
- ? [vbriefValidateSubcommand, ...rest]
3071
- : rest;
3072
- const helpRc = interceptHelp(canonical.replaceAll("-", "_"), handlerArgv, {
3073
- write: io.writeOut,
3074
- });
3075
- if (helpRc !== null) {
3076
- return helpRc;
3077
- }
3078
- const handler = await loadHandler(canonical, io);
3079
- return await invokeHandler(handler, handlerArgv);
3080
- }
3081
- catch (err) {
3082
- const message = err instanceof Error ? err.message : String(err);
3083
- io.writeErr(`directive: ${message}\n`);
3084
- return 2;
3085
- }
3086
- }
3087
- /** Test seam: reset lazy handler cache between cases. */
3088
- export function resetHandlerCacheForTests() {
3089
- handlerCache.clear();
3090
- }
3091
- //# sourceMappingURL=dispatch.js.map