@khoavu882/doflow 2.0.0-beta.4 → 2.0.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 (497) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -3
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  9. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  10. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  11. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  12. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  13. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  14. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  15. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  16. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  17. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  18. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  19. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  20. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  21. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  22. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  23. package/core/harnesses/claude/settings/settings.json +18 -0
  24. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  25. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  26. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  27. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  28. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  29. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  30. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  31. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  32. package/core/harnesses/codex/hooks/lib.sh +44 -25
  33. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  34. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  35. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  36. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  37. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  38. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  39. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  40. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  41. package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
  42. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  43. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  44. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  45. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  46. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  47. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  48. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  49. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  50. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  51. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  52. package/core/harnesses/kiro/agents/doflow.json +1 -1
  53. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  54. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
  55. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
  56. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
  57. package/core/harnesses/kiro/hooks/session-start.sh +12 -5
  58. package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
  59. package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
  60. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
  61. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  62. package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
  63. package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
  64. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  65. package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
  66. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  67. package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
  68. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  69. package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
  70. package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
  71. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  72. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  73. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  74. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  75. package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
  76. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
  77. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
  78. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
  79. package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
  80. package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
  81. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
  82. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  83. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
  84. package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
  85. package/core/harnesses/shared/locator/doflow-run +2 -3
  86. package/core/registry/advice.json +3 -3
  87. package/core/registry/assets.json +72 -80
  88. package/core/registry/capabilities.json +12 -0
  89. package/core/registry/contracts.json +60 -33
  90. package/core/registry/contribution-points.json +32 -0
  91. package/core/registry/gates.json +32 -0
  92. package/core/registry/guidance-caps.json +40 -0
  93. package/core/registry/harnesses.json +1542 -277
  94. package/core/registry/lifecycle.json +2837 -37
  95. package/core/registry/mcp.json +5 -1
  96. package/core/registry/models.json +7 -1
  97. package/core/registry/package-templates/README.md +19 -0
  98. package/core/registry/package-templates/agent/files/agent.md +9 -0
  99. package/core/registry/package-templates/agent/template.json +4 -0
  100. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  101. package/core/registry/package-templates/capability-provider/template.json +11 -0
  102. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  103. package/core/registry/package-templates/debate-preset/template.json +4 -0
  104. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  105. package/core/registry/package-templates/review-preset/template.json +4 -0
  106. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  107. package/core/registry/package-templates/skill/template.json +4 -0
  108. package/core/registry/package-templates/worker/files/worker.json +92 -0
  109. package/core/registry/package-templates/worker/template.json +11 -0
  110. package/core/registry/packages/doflow/doflow-package.json +15 -0
  111. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  112. package/core/registry/protocols.json +61 -0
  113. package/core/registry/routes.json +17 -0
  114. package/core/registry/rules.json +431 -0
  115. package/core/registry/settings.json +276 -0
  116. package/core/registry/storage.json +701 -30
  117. package/core/registry/telemetry.json +236 -0
  118. package/core/registry/transcripts.json +142 -0
  119. package/core/registry/verification.json +76 -1
  120. package/core/registry/workflows.json +55 -0
  121. package/core/shared/agent-specs/core-implementer.md +3 -0
  122. package/core/shared/agent-specs/delivery-lead.md +68 -0
  123. package/core/shared/agent-specs/quality-guardian.md +5 -1
  124. package/core/shared/agent-specs/research-writer.md +10 -3
  125. package/core/shared/agent-specs/reviewer.md +53 -0
  126. package/core/shared/agent-specs/spec-analyst.md +5 -6
  127. package/core/shared/agent-specs/system-architect.md +5 -1
  128. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  129. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  130. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  131. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  132. package/core/shared/guidance/pointers/codex.md +1 -4
  133. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  134. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  135. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  136. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  137. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  138. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  139. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  140. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  141. package/core/shared/guidance/rules/RULES.md +45 -0
  142. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  143. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  144. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  145. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  146. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  147. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  148. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  149. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  150. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  151. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  152. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  153. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  154. package/core/shared/scripts/doflow/bin/doflow-run +154 -35
  155. package/core/shared/skills/do/SKILL.md +12 -10
  156. package/core/shared/skills/do/references/advice_calls.md +26 -0
  157. package/core/shared/skills/do/references/pm_routing.md +1 -2
  158. package/core/shared/skills/do/references/program_mode.md +31 -0
  159. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  160. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  161. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  162. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  163. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  164. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  165. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  166. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  167. package/core/shared/skills/do-design/SKILL.md +24 -23
  168. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  169. package/core/shared/skills/do-document/SKILL.md +11 -1
  170. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  171. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  172. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  173. package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
  174. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  175. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  176. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
  177. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  178. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  179. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  180. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  181. package/core/shared/skills/do-flow/SKILL.md +8 -9
  182. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  183. package/core/shared/skills/do-git/SKILL.md +4 -0
  184. package/core/shared/skills/do-implement/SKILL.md +7 -9
  185. package/core/shared/skills/do-plan/SKILL.md +29 -19
  186. package/core/shared/skills/do-test/SKILL.md +7 -4
  187. package/core/shared/templates/doflow/constitution-template.md +9 -3
  188. package/core/shared/templates/doflow/data-model-template.md +1 -1
  189. package/core/shared/templates/doflow/design-template.md +113 -16
  190. package/core/shared/templates/doflow/plan-template.md +11 -10
  191. package/core/shared/templates/doflow/question-log-template.md +14 -2
  192. package/core/shared/templates/doflow/requirement-template.md +1 -1
  193. package/core/worker-evidence/antigravity/checks.json +14 -0
  194. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  195. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  196. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  197. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  198. package/core/worker-evidence/codex/checks.json +38 -0
  199. package/package.json +10 -6
  200. package/src/adapters/antigravity/index.js +226 -113
  201. package/src/adapters/claude/index.js +39 -5
  202. package/src/adapters/codex/agents.js +25 -15
  203. package/src/adapters/codex/hooks.js +40 -5
  204. package/src/adapters/codex/index.js +100 -22
  205. package/src/adapters/codex/native-projection.js +6 -2
  206. package/src/adapters/copilot/index.js +58 -23
  207. package/src/adapters/copy-tree.js +294 -48
  208. package/src/adapters/guidance-manifest.js +71 -0
  209. package/src/adapters/guidance-pointer.js +19 -0
  210. package/src/adapters/hook-commands.js +6 -10
  211. package/src/adapters/index.js +1 -0
  212. package/src/adapters/kiro/index.js +38 -13
  213. package/src/adapters/opencode/index.js +8 -8
  214. package/src/adapters/pi/hooks.js +120 -0
  215. package/src/adapters/pi/index.js +48 -16
  216. package/src/adapters/resolve-source.js +26 -2
  217. package/src/cli/commands/install.js +3 -2
  218. package/src/cli/commands/reconcile.js +53 -9
  219. package/src/cli/commands/remove.js +2 -2
  220. package/src/cli/commands/rollback.js +6 -3
  221. package/src/cli/commands/self-update.js +18 -9
  222. package/src/cli/commands/status.js +21 -2
  223. package/src/cli/commands/ui.js +99 -0
  224. package/src/cli/commands/update.js +3 -2
  225. package/src/cli/index.js +338 -33
  226. package/src/cli/runtime-commands.js +243 -52
  227. package/src/cli/shared.js +0 -2
  228. package/src/helper/git.js +1 -1
  229. package/src/helper/guardrails.js +1 -1
  230. package/src/helper/marker-merge.js +31 -13
  231. package/src/helper/package-ids.js +37 -0
  232. package/src/helper/semver.js +94 -0
  233. package/src/install/backup.js +105 -15
  234. package/src/install/context.js +1 -1
  235. package/src/install/targets.js +31 -16
  236. package/src/lifecycle/coverage.js +74 -0
  237. package/src/lifecycle/hook-step.js +61 -0
  238. package/src/lifecycle/hook-tier.js +160 -0
  239. package/src/lifecycle/index.js +434 -86
  240. package/src/lifecycle/retired-harnesses.js +318 -0
  241. package/src/lifecycle/view.js +35 -15
  242. package/src/registry/index.js +686 -32
  243. package/src/registry/worker-ids.js +45 -0
  244. package/src/release/build-package.js +0 -3
  245. package/src/release/docs-blocks.js +492 -0
  246. package/src/release/preflight.js +425 -0
  247. package/src/release/rehearse.js +401 -0
  248. package/src/release/release-workflow.js +733 -0
  249. package/src/release/sync-plugin-versions.js +43 -7
  250. package/src/runtime/advice/bench-gate.js +25 -0
  251. package/src/runtime/advice/cli.js +59 -16
  252. package/src/runtime/advice/client.js +32 -9
  253. package/src/runtime/advice/config.js +98 -133
  254. package/src/runtime/advice/doctor.js +1 -1
  255. package/src/runtime/advice/elevation.js +97 -0
  256. package/src/runtime/advice/endpoint-guard.js +9 -3
  257. package/src/runtime/advice/mode.js +37 -0
  258. package/src/runtime/advice/record.js +18 -5
  259. package/src/runtime/advice/registry.js +4 -1
  260. package/src/runtime/advice/shadow-config.js +229 -0
  261. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  262. package/src/runtime/brief-rules.js +81 -0
  263. package/src/runtime/brief-tools.js +111 -34
  264. package/src/runtime/candidate.js +514 -0
  265. package/src/runtime/capabilities/executor.js +450 -0
  266. package/src/runtime/capabilities/loopback.js +41 -0
  267. package/src/runtime/capabilities/sanitizer.js +88 -0
  268. package/src/runtime/capability-router.js +135 -3
  269. package/src/runtime/checkouts.js +73 -11
  270. package/src/runtime/claims.js +27 -12
  271. package/src/runtime/cli-result.js +11 -4
  272. package/src/runtime/cli.js +221 -277
  273. package/src/runtime/config-home.js +42 -0
  274. package/src/runtime/context/arbitrate.js +767 -0
  275. package/src/runtime/context/budget.js +203 -0
  276. package/src/runtime/context/compose.js +1367 -0
  277. package/src/runtime/context/delivery-store.js +100 -0
  278. package/src/runtime/context/emission-rules.js +118 -0
  279. package/src/runtime/context/identity.js +227 -0
  280. package/src/runtime/context/mandatory.js +594 -0
  281. package/src/runtime/context/pointer.js +142 -0
  282. package/src/runtime/context/primary-command.js +117 -0
  283. package/src/runtime/context/primary.js +344 -0
  284. package/src/runtime/context/summary-store.js +655 -0
  285. package/src/runtime/context-pack.js +1106 -245
  286. package/src/runtime/decision-register.js +13 -4
  287. package/src/runtime/dispatch/admission.js +392 -0
  288. package/src/runtime/dispatch/attempt-end.js +274 -0
  289. package/src/runtime/dispatch/command.js +628 -0
  290. package/src/runtime/dispatch/controls.js +904 -0
  291. package/src/runtime/dispatch/envelope.js +276 -0
  292. package/src/runtime/dispatch/grants.js +310 -0
  293. package/src/runtime/dispatch/integrate.js +617 -0
  294. package/src/runtime/dispatch/loop.js +1835 -0
  295. package/src/runtime/dispatch/store.js +352 -0
  296. package/src/runtime/dispatch/verify-run.js +201 -0
  297. package/src/runtime/evidence-fields.js +39 -0
  298. package/src/runtime/evidence-ledger.js +41 -7
  299. package/src/runtime/failure/capture.js +3 -1
  300. package/src/runtime/failure/cli.js +25 -9
  301. package/src/runtime/failure/home.js +57 -30
  302. package/src/runtime/failure/store.js +7 -2
  303. package/src/runtime/feature-pointer.js +174 -0
  304. package/src/runtime/feature-resolve.js +13 -2
  305. package/src/runtime/fleet/command.js +77 -0
  306. package/src/runtime/fleet/projects.js +178 -0
  307. package/src/runtime/fleet/tree.js +383 -0
  308. package/src/runtime/freshness.js +77 -10
  309. package/src/runtime/git-memo.js +46 -0
  310. package/src/runtime/guidance/ablation.js +427 -0
  311. package/src/runtime/health.js +565 -17
  312. package/src/runtime/history-compactor.js +13 -3
  313. package/src/runtime/implementation-gate.js +277 -22
  314. package/src/runtime/inventory/coverage.js +1 -1
  315. package/src/runtime/inventory/index.js +1 -1
  316. package/src/runtime/inventory/precedence.js +2 -2
  317. package/src/runtime/inventory/siblings.js +6 -4
  318. package/src/runtime/knowledge/code-corpus.js +277 -0
  319. package/src/runtime/knowledge/code-lexical.js +130 -0
  320. package/src/runtime/knowledge/code-retrieval.js +307 -0
  321. package/src/runtime/knowledge/code-validation.js +106 -0
  322. package/src/runtime/knowledge/gp3-gate.js +74 -0
  323. package/src/runtime/knowledge/graph-adapter.js +288 -0
  324. package/src/runtime/knowledge/graph-fallback.js +394 -0
  325. package/src/runtime/knowledge/hybrid.js +136 -8
  326. package/src/runtime/knowledge/retrieval.js +34 -1
  327. package/src/runtime/knowledge/rtk-compress.js +77 -0
  328. package/src/runtime/knowledge/semble-bridge.py +106 -0
  329. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  330. package/src/runtime/knowledge/semble-provider.js +365 -0
  331. package/src/runtime/lifecycle/cli.js +16 -6
  332. package/src/runtime/lifecycle/event-store.js +8 -6
  333. package/src/runtime/lifecycle/followup.js +6 -1
  334. package/src/runtime/lifecycle/overview.js +24 -1
  335. package/src/runtime/lifecycle/report-store.js +8 -10
  336. package/src/runtime/lifecycle/retention.js +95 -31
  337. package/src/runtime/lifecycle/root.js +9 -1
  338. package/src/runtime/lifecycle/status.js +34 -17
  339. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  340. package/src/runtime/mask.js +134 -7
  341. package/src/runtime/mcp-consultations.js +40 -6
  342. package/src/runtime/model-router.js +79 -7
  343. package/src/runtime/models/catalogue.js +191 -0
  344. package/src/runtime/models/command.js +153 -0
  345. package/src/runtime/off-words.js +12 -0
  346. package/src/runtime/outcome.js +3 -3
  347. package/src/runtime/packages/command.js +1009 -0
  348. package/src/runtime/packages/contributions.js +116 -0
  349. package/src/runtime/packages/digest.js +132 -0
  350. package/src/runtime/packages/fetch.js +182 -0
  351. package/src/runtime/packages/grants.js +192 -0
  352. package/src/runtime/packages/index-file.js +134 -0
  353. package/src/runtime/packages/instruction.js +90 -0
  354. package/src/runtime/packages/manifest.js +331 -0
  355. package/src/runtime/packages/points.js +477 -0
  356. package/src/runtime/packages/programs.js +216 -0
  357. package/src/runtime/packages/scaffold.js +277 -0
  358. package/src/runtime/packages/source.js +90 -0
  359. package/src/runtime/packages/state.js +164 -0
  360. package/src/runtime/packages/store.js +449 -0
  361. package/src/runtime/packages/tar.js +106 -0
  362. package/src/runtime/packages/view.js +406 -0
  363. package/src/runtime/plan-check.js +59 -0
  364. package/src/runtime/plan-content.js +324 -0
  365. package/src/runtime/policy-init.js +118 -0
  366. package/src/runtime/profiles/doctor.js +96 -0
  367. package/src/runtime/profiles/resolve.js +244 -0
  368. package/src/runtime/profiles/rollback.js +254 -0
  369. package/src/runtime/profiles/sampler.js +41 -0
  370. package/src/runtime/readiness-record.js +16 -11
  371. package/src/runtime/readiness.js +127 -16
  372. package/src/runtime/research-request.js +50 -25
  373. package/src/runtime/retrieval-plan.js +402 -73
  374. package/src/runtime/retrieval-record.js +293 -0
  375. package/src/runtime/review/command.js +246 -0
  376. package/src/runtime/review/dominance.js +68 -0
  377. package/src/runtime/review/findings.js +434 -0
  378. package/src/runtime/review/identity.js +255 -0
  379. package/src/runtime/review/protocol-command.js +109 -0
  380. package/src/runtime/review/protocols.js +405 -0
  381. package/src/runtime/review/resolve-protocol.js +302 -0
  382. package/src/runtime/review/start.js +530 -0
  383. package/src/runtime/roadmap/facts.js +479 -0
  384. package/src/runtime/roadmap/gates.js +270 -0
  385. package/src/runtime/roadmap/loop.js +1636 -0
  386. package/src/runtime/roadmap/merge.js +381 -0
  387. package/src/runtime/roadmap/rows.js +1113 -0
  388. package/src/runtime/roadmap/seed.js +951 -0
  389. package/src/runtime/rules-report.js +336 -0
  390. package/src/runtime/scaffold/artifacts.js +63 -9
  391. package/src/runtime/scaffold/generate.js +21 -9
  392. package/src/runtime/settings/autopilot.js +106 -0
  393. package/src/runtime/settings/cli.js +280 -0
  394. package/src/runtime/settings/config-write.js +314 -0
  395. package/src/runtime/settings/files.js +271 -0
  396. package/src/runtime/settings/legacy.js +154 -0
  397. package/src/runtime/settings/lexical.js +56 -0
  398. package/src/runtime/settings/prepared-run.js +269 -0
  399. package/src/runtime/settings/project-layers.js +118 -0
  400. package/src/runtime/settings/refs.js +288 -0
  401. package/src/runtime/settings/registry.js +553 -0
  402. package/src/runtime/settings/resolve.js +713 -0
  403. package/src/runtime/settings/shared.js +37 -0
  404. package/src/runtime/settings/worker-billing.js +107 -0
  405. package/src/runtime/settings/write.js +269 -0
  406. package/src/runtime/slot-command.js +78 -0
  407. package/src/runtime/slots.js +671 -0
  408. package/src/runtime/source-version.js +290 -0
  409. package/src/runtime/state-root.js +217 -0
  410. package/src/runtime/storage/cli.js +12 -5
  411. package/src/runtime/storage/engine.js +9 -2
  412. package/src/runtime/storage/registry.js +14 -1
  413. package/src/runtime/storage/roots.js +66 -16
  414. package/src/runtime/storage/rules.js +82 -10
  415. package/src/runtime/storage/upkeep.js +10 -2
  416. package/src/runtime/storage/work-status.js +245 -26
  417. package/src/runtime/task-attempts.js +589 -69
  418. package/src/runtime/task-classifier.js +47 -11
  419. package/src/runtime/task-graph.js +373 -0
  420. package/src/runtime/task-run.js +778 -0
  421. package/src/runtime/task-scope.js +202 -56
  422. package/src/runtime/task-sources.js +147 -0
  423. package/src/runtime/task-state.js +440 -65
  424. package/src/runtime/team/admission.js +301 -0
  425. package/src/runtime/team/envelope.js +324 -0
  426. package/src/runtime/team/review-guard.js +156 -0
  427. package/src/runtime/trace/analyze.js +427 -0
  428. package/src/runtime/trace/baseline.js +770 -0
  429. package/src/runtime/trace/ledger.js +63 -140
  430. package/src/runtime/trace/otlp-export.js +568 -0
  431. package/src/runtime/trace/otlp-map.js +481 -0
  432. package/src/runtime/trace/spans.js +745 -0
  433. package/src/runtime/trace/telemetry.js +225 -0
  434. package/src/runtime/trace/views.js +1 -0
  435. package/src/runtime/transcripts/command.js +98 -0
  436. package/src/runtime/transcripts/cursor.js +326 -0
  437. package/src/runtime/transcripts/read.js +126 -0
  438. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  439. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  440. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  441. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  442. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  443. package/src/runtime/transcripts/registry.js +161 -0
  444. package/src/runtime/transcripts/stats.js +161 -0
  445. package/src/runtime/verification/check-key.js +628 -0
  446. package/src/runtime/verification/contract-runner.js +546 -11
  447. package/src/runtime/verification/engine.js +499 -9
  448. package/src/runtime/verification/impact.js +617 -0
  449. package/src/runtime/verification/inflight.js +205 -0
  450. package/src/runtime/verification/registry.js +176 -0
  451. package/src/runtime/verification/reuse.js +184 -0
  452. package/src/runtime/verification/telemetry-emit.js +254 -0
  453. package/src/runtime/verification/test-selection.js +67 -7
  454. package/src/runtime/worker/active.js +90 -0
  455. package/src/runtime/worker/cancel.js +222 -0
  456. package/src/runtime/worker/catalogue.js +122 -0
  457. package/src/runtime/worker/command.js +677 -0
  458. package/src/runtime/worker/evidence.js +288 -0
  459. package/src/runtime/worker/launch.js +288 -0
  460. package/src/runtime/worker/models.js +137 -0
  461. package/src/runtime/worker/parsers.js +218 -0
  462. package/src/runtime/worker/probes/index.js +706 -0
  463. package/src/runtime/worker/process-tree.js +415 -0
  464. package/src/runtime/worker/quota.js +43 -0
  465. package/src/runtime/worker/readiness.js +713 -0
  466. package/src/runtime/worker/run-store.js +166 -0
  467. package/src/runtime/worker/supervisor.js +541 -0
  468. package/src/runtime/worker/worktree-check.js +252 -0
  469. package/src/runtime/workflow-engine.js +5 -1
  470. package/src/runtime/workflow-orchestrator.js +578 -63
  471. package/src/runtime/worktree.js +149 -22
  472. package/src/state/index.js +1 -1
  473. package/src/state/lockfile.js +64 -10
  474. package/src/state/run-lock.js +1 -1
  475. package/types/node-commonjs.d.ts +106 -7
  476. package/core/.antigravity-plugin/hooks.json +0 -39
  477. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  478. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  479. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
  480. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  481. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
  482. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  483. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
  484. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  485. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  486. package/core/harnesses/gemini/hooks/session-start.sh +0 -5
  487. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  488. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  489. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  490. package/core/harnesses/shared/hooks/hooks.json +0 -47
  491. package/core/shared/guidance/FLAGS.md +0 -25
  492. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  493. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  494. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  495. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  496. package/src/adapters/gemini/hooks.js +0 -210
  497. package/src/adapters/gemini/index.js +0 -390
@@ -0,0 +1,216 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * IC-018 program declarations: check executable contributions on open points at supported versions,
5
+ * their declared programs, and each declaration's launch shape. This module never spawns a process.
6
+ * `launchSpec` builds the shell-free spec that the D.3 launcher uses, with arguments, cwd, and
7
+ * environment supplied by core.
8
+ */
9
+
10
+ const fs = require('node:fs');
11
+ const path = require('node:path');
12
+ const { isLocalId } = require('../../helper/package-ids.js');
13
+ const { SEGMENT, SEGMENT_GRAMMAR } = require('./digest.js');
14
+
15
+ const ARGV_SHAPES = Object.freeze({
16
+ worker: Object.freeze(['worker-recipe']),
17
+ 'capability-provider': Object.freeze(['capability-cli']),
18
+ });
19
+ const BINARY = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
20
+ const PROGRAM_KEYS = ['id', 'binary', 'path', 'argv'];
21
+ const platform = require('node:process').platform;
22
+
23
+ function isObject(value ) {
24
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
25
+ }
26
+
27
+ /** Validate IC-018 program declarations and each executable contribution's program references. */
28
+ function programFindings(
29
+ manifest ,
30
+ { root = null, pointsRegistry, fsImpl = fs, packageId = null } ,
31
+ ) {
32
+ const findings = [];
33
+ const add = (field , code , message , allowed ) =>
34
+ findings.push({ package: packageId, field, code, severity: 'error', message, allowed });
35
+ const contributions = Array.isArray(manifest?.contributions) ? manifest.contributions : [];
36
+ const executable = contributions
37
+ .map((entry , index ) => ({ entry, index, point: pointsRegistry.points.find((item ) => item.id === entry?.point) }))
38
+ .filter((item ) => item.point?.tier === 'executable'
39
+ && item.point.status === 'open'
40
+ && item.point.versions.includes(item.entry?.version));
41
+
42
+ if (executable.length === 0) return findings;
43
+
44
+ function isCliCapability(entry , point ) {
45
+ if (point?.id !== 'capability-provider') return false;
46
+ if (Array.isArray(entry?.programs) && entry.programs.length > 0) return true;
47
+ if (!root || !entry?.file || typeof entry.file !== 'string') return false;
48
+ try {
49
+ const real = path.join(root, entry.file);
50
+ const text = fsImpl.readFileSync(real, 'utf8');
51
+ const parsed = JSON.parse(text);
52
+ return parsed?.kind === 'cli';
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+
58
+ const needsPrograms = executable.some(({ entry, point } ) => {
59
+ if (point?.id === 'capability-provider') {
60
+ return isCliCapability(entry, point);
61
+ }
62
+ return true;
63
+ });
64
+
65
+ const rawPrograms = manifest?.programs;
66
+ if (rawPrograms === undefined) {
67
+ if (needsPrograms) {
68
+ add('programs', 'program-invalid', 'programs must be an array of at most 16 declarations', 'an array of 0 to 16 program declarations');
69
+ }
70
+ } else if (!Array.isArray(rawPrograms) || rawPrograms.length > 16) {
71
+ add('programs', 'program-invalid', 'programs must be an array of at most 16 declarations', 'an array of 0 to 16 program declarations');
72
+ }
73
+
74
+ const declarations = Array.isArray(rawPrograms) ? rawPrograms : [];
75
+ const declaredIds = new Set ();
76
+ const seenIds = new Set ();
77
+ declarations.forEach((program , index ) => {
78
+ const field = `programs[${index}]`;
79
+ if (!isObject(program)) {
80
+ add(field, 'program-invalid', 'a program declaration must be an object', 'an object with id, binary or path, and argv');
81
+ return;
82
+ }
83
+ for (const key of Object.keys(program)) {
84
+ if (!PROGRAM_KEYS.includes(key)) add(`${field}.${key}`, 'program-invalid', `'${key}' is not a program declaration field`, PROGRAM_KEYS);
85
+ }
86
+
87
+ const validId = isLocalId(program.id);
88
+ if (!validId) {
89
+ add(`${field}.id`, 'program-invalid', 'program id must be a local id', 'a local id');
90
+ } else if (seenIds.has(program.id)) {
91
+ add(`${field}.id`, 'program-invalid', `program id '${program.id}' is declared more than once`, 'a unique local id');
92
+ } else {
93
+ seenIds.add(program.id);
94
+ declaredIds.add(program.id);
95
+ }
96
+
97
+ const hasBinary = Object.hasOwn(program, 'binary');
98
+ const hasPath = Object.hasOwn(program, 'path');
99
+ if (hasBinary === hasPath) {
100
+ add(field, 'program-invalid', 'exactly one of binary or path is required', ['binary', 'path']);
101
+ } else if (hasBinary) {
102
+ if (typeof program.binary !== 'string' || program.binary.length > 64 || !BINARY.test(program.binary)) {
103
+ add(`${field}.binary`, 'program-invalid', 'binary must be a bare executable name of at most 64 characters', '^[A-Za-z0-9][A-Za-z0-9._-]*$ (at most 64 characters)');
104
+ }
105
+ } else {
106
+ validateProgramPath(program.path, `${field}.path`);
107
+ }
108
+ });
109
+
110
+ for (const { entry, index, point } of executable) {
111
+ const field = `contributions[${index}].programs`;
112
+ const refs = entry?.programs;
113
+ if (!Array.isArray(refs) || refs.length === 0) {
114
+ if (point?.id === 'capability-provider' && !isCliCapability(entry, point)) {
115
+ continue;
116
+ }
117
+ add(field, 'program-undeclared', 'an executable contribution must name every program it launches', [...declaredIds]);
118
+ continue;
119
+ }
120
+ refs.forEach((id ) => {
121
+ if (typeof id !== 'string' || !declaredIds.has(id)) {
122
+ add(field, 'program-undeclared', `program '${String(id)}' is not declared`, [...declaredIds]);
123
+ }
124
+ });
125
+ }
126
+
127
+ declarations.forEach((program , index ) => {
128
+ if (!isObject(program)) return;
129
+ const field = `programs[${index}].argv`;
130
+ const usingPoints = executable.filter(({ entry } ) =>
131
+ Array.isArray(entry?.programs) && entry.programs.includes(program.id));
132
+ let allowed ;
133
+ if (usingPoints.length > 0) {
134
+ const shapes = usingPoints.map(({ point } ) => ARGV_SHAPES[point.id] ?? []);
135
+ allowed = shapes.length === 0 ? [] : [...shapes[0]].filter((shape) => shapes.every((list ) => list.includes(shape)));
136
+ } else {
137
+ allowed = [...new Set (executable.flatMap(({ point } ) => [...(ARGV_SHAPES[point.id] ?? [])]))];
138
+ }
139
+ if (typeof program.argv !== 'string' || !allowed.includes(program.argv)) {
140
+ add(field, 'program-invalid', 'argv must name a shape defined by the executable point using this program', allowed);
141
+ }
142
+ });
143
+
144
+ return findings;
145
+
146
+ function validateProgramPath(rel , field ) {
147
+ const limits = pointsRegistry.limits;
148
+ if (typeof rel !== 'string' || rel.length === 0) {
149
+ add(field, 'program-invalid', 'path must be a non-empty POSIX package-relative path', 'a package-relative path to an executable regular file');
150
+ return;
151
+ }
152
+ if (rel.startsWith('/') || rel.split('/').includes('..')) {
153
+ add(field, 'path-escapes-root', `${rel} leaves the package root`, 'a path inside the package root');
154
+ return;
155
+ }
156
+ if (rel.length > limits.pathLength || rel.split('/').some((segment ) => !SEGMENT.test(segment))) {
157
+ add(field, 'program-invalid', `${rel} breaks the package path rules`, `${SEGMENT_GRAMMAR} per segment, at most ${limits.pathLength} characters`);
158
+ return;
159
+ }
160
+ if (!root) return;
161
+
162
+ let rootReal ;
163
+ let real ;
164
+ try {
165
+ rootReal = fsImpl.realpathSync(root);
166
+ real = fsImpl.realpathSync(path.join(root, rel));
167
+ } catch (error ) {
168
+ add(field, 'file-missing', `${rel} does not exist: ${error?.message || String(error)}`, 'an existing regular file');
169
+ return;
170
+ }
171
+ if (real !== rootReal && !real.startsWith(rootReal + path.sep)) {
172
+ add(field, 'path-escapes-root', `${rel} resolves outside the package root`, 'a path inside the package root');
173
+ return;
174
+ }
175
+ let stat ;
176
+ try {
177
+ stat = fsImpl.statSync(real);
178
+ } catch (error ) {
179
+ add(field, 'file-missing', `${rel} cannot be read: ${error?.message || String(error)}`, 'an existing regular file');
180
+ return;
181
+ }
182
+ if (!stat.isFile()) {
183
+ add(field, 'program-invalid', `${rel} is not a regular file`, 'an executable regular file');
184
+ return;
185
+ }
186
+ if (platform !== 'win32' && (stat.mode & 0o111) === 0) {
187
+ add(field, 'program-invalid', `${rel} has no execute bit`, 'a file with at least one execute bit');
188
+ }
189
+ }
190
+ }
191
+
192
+ /** Build a shell-free launch description using only the declared executable and core-provided arguments. */
193
+ function launchSpec({ program, storeEntry, recipeArgv, cwd, env } ) {
194
+ if (!Array.isArray(recipeArgv) || recipeArgv.some((arg ) => typeof arg !== 'string')) {
195
+ throw new TypeError('recipeArgv must be an array of strings');
196
+ }
197
+ let file ;
198
+ if (typeof program?.binary === 'string') {
199
+ file = program.binary;
200
+ } else if (typeof program?.path === 'string') {
201
+ const root = path.resolve(storeEntry);
202
+ file = path.resolve(root, program.path);
203
+ if (file !== root && !file.startsWith(root + path.sep)) {
204
+ throw new Error('program path leaves the store entry');
205
+ }
206
+ } else {
207
+ throw new TypeError('program must declare a binary or path');
208
+ }
209
+ return {
210
+ file,
211
+ args: [...recipeArgv],
212
+ options: { shell: false, cwd, env, windowsHide: true },
213
+ };
214
+ }
215
+
216
+ module.exports = { ARGV_SHAPES, programFindings, launchSpec };
@@ -0,0 +1,277 @@
1
+ 'use strict';
2
+
3
+ // IC-025: authored source lives under package/, never in an install ledger or package store.
4
+ // .staging- siblings older than 24 hours are removed by the scaffold itself.
5
+ const fs = require('node:fs');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+ const { randomBytes } = require('node:crypto');
9
+ const { spawnSync } = require('node:child_process');
10
+ const { REPO_ROOT } = require('../../helper/repo-root.js');
11
+ const { isPackageId, isReservedPackageId, isLocalId, qualifiedId, projectedName } = require('../../helper/package-ids.js');
12
+ const { parseSemVer } = require('../../helper/semver.js');
13
+ const { loadPoints, templateFindings, POINTS_FILE } = require('./points.js');
14
+ const { checkPackage, MANIFEST_FILE } = require('./manifest.js');
15
+ const { detectedProjectRoot } = require('./grants.js');
16
+ const { hostVersion } = require('./view.js');
17
+ const { packagePaths } = require('./store.js');
18
+ const { configHome } = require('../config-home.js');
19
+ const { readLock, lockPackages } = require('../../state/lockfile.js');
20
+ const { SEGMENT } = require('./digest.js');
21
+
22
+ const TIERS = ['inert', 'instruction', 'executable'];
23
+ const STAGING = /^\.staging-[a-z0-9-]+-[0-9a-f]{8}$/;
24
+ const DAY = 24 * 3600000;
25
+
26
+ function stat(file , fsImpl ) {
27
+ try { return fsImpl.lstatSync(file); }
28
+ catch (error ) { if (error.code === 'ENOENT') return null; throw error; }
29
+ }
30
+
31
+ /** Resolve existing ancestors, retaining the not-yet-created suffix. A dangling link refuses resolution. */
32
+ function realTarget(file , fsImpl ) {
33
+ const absolute = path.resolve(file);
34
+ if (stat(absolute, fsImpl)) return fsImpl.realpathSync(absolute);
35
+ const parent = path.dirname(absolute);
36
+ if (parent === absolute) throw new Error(`cannot resolve ${absolute}`);
37
+ return path.join(realTarget(parent, fsImpl), path.basename(absolute));
38
+ }
39
+
40
+ function within(file , root ) {
41
+ const relative = path.relative(root, file);
42
+ return relative === '' || (!path.isAbsolute(relative) && relative !== '..' && !relative.startsWith(`..${path.sep}`));
43
+ }
44
+
45
+ const commandArg = (value ) =>
46
+ /^[A-Za-z0-9_./-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
47
+
48
+ function nextSteps(id , dir , tierMax , grantsPath ) {
49
+ const target = commandArg(dir);
50
+ const next = [
51
+ { step: 'edit', command: null, note: `write the content of the listed stub files and the README's "What and why"; edit only files under ${dir}; from /do, this is /do-implement standalone with task id package-${id}` },
52
+ { step: 'check', command: `doflow package --action check --path ${target}`, note: 'repeat until clean' },
53
+ { step: 'add', command: `doflow package --action add --source ${target}`, note: 'writes a plan only; shows the inventory, the projection bytes and the grant lines' },
54
+ { step: 'apply', command: 'doflow package --action add --plan <planId>', note: "installing a package is on the person's ask-first list; ask before running it, and in a headless run stop here and print this line" },
55
+ { step: 'enable', command: `doflow package --action enable --id ${id}`, note: 'an inert package is then active' },
56
+ ];
57
+ if (tierMax !== 'inert') {
58
+ next.push({
59
+ step: 'trust', command: `doflow package --action trust --id ${id}`,
60
+ note: grantsPath === null
61
+ ? 'there is no config home, so no grant can exist; never append a trust line from a model session'
62
+ : `prints the line a person appends to ${grantsPath}; never append it from a model session`,
63
+ });
64
+ }
65
+ next.push({
66
+ step: 'iterate', command: null,
67
+ note: `edit, check, update --id ${id}, update --plan <planId>; above inert, every applied change of content needs a new trust line; DoFlow does not follow the folder`,
68
+ });
69
+ return next;
70
+ }
71
+
72
+ function ignoredByGit(target , fsImpl , env ) {
73
+ const parent = path.dirname(target);
74
+ let found = false;
75
+ for (let dir = parent; ; dir = path.dirname(dir)) {
76
+ try { if (stat(path.join(dir, '.git'), fsImpl)) { found = true; break; } } catch { return null; }
77
+ if (path.dirname(dir) === dir) break;
78
+ }
79
+ if (!found) return null;
80
+ try {
81
+ const result = spawnSync('git', ['check-ignore', '-q', target], {
82
+ shell: false, cwd: parent, env, encoding: 'utf8', timeout: 4000,
83
+ });
84
+ return result.status === 0 ? true : result.status === 1 ? false : null;
85
+ } catch { return null; }
86
+ }
87
+
88
+ /** Create only the authored tree and its staging sibling; rendering and telemetry belong to command.ts. */
89
+ function scaffoldPackage({
90
+ id, points, dir, cwd = process.cwd(), homeDir = os.homedir(), env = process.env,
91
+ repoRoot = REPO_ROOT, now = Date.now(), fsImpl = fs, grantsPath = null,
92
+ } = {}) {
93
+ const finding = (field , code , message , allowed , severity = 'error') =>
94
+ ({ package: typeof id === 'string' ? id : null, field, code, severity, message, allowed });
95
+ const refuse = (code , field , message , allowed , findings = []) =>
96
+ ({ ok: false, error: code, findings: [finding(field, code, message, allowed), ...findings] });
97
+
98
+ let staging = null;
99
+ try {
100
+ const loaded = loadPoints({ repoRoot, fsImpl });
101
+ if (!loaded.ok) {
102
+ // The loader requires template keys. Preserve new's more specific refusal for an absent key.
103
+ try {
104
+ const raw = JSON.parse(fsImpl.readFileSync(path.join(repoRoot, POINTS_FILE), 'utf8'));
105
+ const requested = typeof points === 'string' ? points.split(',').map((s ) => s.split(':')[0]) : [];
106
+ const missing = raw.points?.find((p ) => requested.includes(p.id) && p.status === 'open' && !p.template);
107
+ if (missing) return refuse('template-missing', 'points', `point '${missing.id}' names no template`, 'an open point with a template');
108
+ } catch { /* report the registry errors below */ }
109
+ return refuse('state-unreadable', 'points', loaded.errors.join('; '), 'a valid point registry');
110
+ }
111
+ const registry = loaded.registry;
112
+ const open = registry.points.filter((p ) => p.status === 'open');
113
+ const available = open.filter((p ) => p.template).map((p ) => p.id);
114
+ const usage = (message ) => ({
115
+ ...refuse('usage', 'arguments', `${message}; open points with templates: ${available.join(', ')}`, available),
116
+ usage: true,
117
+ });
118
+ if (typeof id !== 'string' || id.length === 0) return usage('new requires --id <id>');
119
+ if (typeof points !== 'string' || points.length === 0) return usage('new requires --points <point[:local],...>');
120
+ if (!isPackageId(id)) return refuse('bad-id', 'id', `package id '${id}' breaks the grammar`, 'an IC-003 package id');
121
+ if (isReservedPackageId(id)) return refuse('reserved-id', 'id', `package id '${id}' is reserved`, 'an unreserved package id');
122
+ if (dir !== undefined && (typeof dir !== 'string' || dir.length === 0)) return usage('--dir takes a non-empty path');
123
+
124
+ const entries = points.split(',');
125
+ if (entries.length > 64) return refuse('bad-type', 'points', '--points accepts at most 64 entries', '1 to 64 entries');
126
+ const selected = [];
127
+ const seen = new Set ();
128
+ for (const entry of entries) {
129
+ const parts = entry.split(':');
130
+ const point = registry.points.find((p ) => p.id === parts[0]);
131
+ if (!point) return refuse('undeclared-point', 'points', `point '${parts[0]}' is not declared; open points: ${open.map((p ) => p.id).join(', ')}`, open.map((p ) => p.id));
132
+ if (point.status !== 'open') return refuse('point-reserved', 'points', `point '${point.id}' is reserved and opens with ${point.opensWith}`, available);
133
+ const local = parts.length === 1 ? (isLocalId(point.id) ? point.id : 'provider') : parts[1];
134
+ if (parts.length > 2 || !isLocalId(local) || !qualifiedId(id, local)) return refuse('bad-id', 'points', `invalid local id in '${entry}'`, 'point[:local] with an IC-003 local id');
135
+ const key = `${point.id}:${local}`;
136
+ if (seen.has(key)) return refuse('bad-id', 'points', `duplicate contribution '${key}'`, 'unique (point, local id) pairs');
137
+ seen.add(key);
138
+ if (!point.template) return refuse('template-missing', 'points', `point '${point.id}' names no template`, available);
139
+ selected.push({ point, local, version: Math.max(...point.versions) });
140
+ }
141
+
142
+ const projectRoot = detectedProjectRoot({ cwd, homeDir, fsImpl });
143
+ if (dir === undefined && projectRoot === null) return usage('no project root was detected; supply --dir <path>');
144
+ const target = dir === undefined ? path.join(projectRoot, '.doflow', 'package', id) : path.resolve(cwd, dir);
145
+ let real ;
146
+ const forbidden = () => {
147
+ real = realTarget(target, fsImpl);
148
+ const config = configHome(env).dir;
149
+ const roots = [packagePaths(homeDir).base, repoRoot, REPO_ROOT, ...(config === null ? [] : [config])];
150
+ return roots.some((root ) => within(real, realTarget(root, fsImpl)))
151
+ || `${real}${path.sep}`.includes(`${path.sep}.doflow${path.sep}state${path.sep}`);
152
+ };
153
+ try {
154
+ if (forbidden()) return refuse('target-forbidden', 'dir', `target is inside protected storage: ${target}`, 'an authored source directory outside protected storage');
155
+ } catch (error ) {
156
+ return refuse('target-forbidden', 'dir', `cannot resolve target safely: ${error.message}`, 'a resolvable target');
157
+ }
158
+ const originalReal = real ;
159
+ const nonempty = () => {
160
+ const info = stat(target, fsImpl);
161
+ return !!info && (!info.isDirectory() || info.isSymbolicLink() || fsImpl.readdirSync(target).length !== 0);
162
+ };
163
+ if (nonempty()) return refuse('target-not-empty', 'dir', `target is not an empty directory: ${target}`, 'a missing or empty directory');
164
+
165
+ const defects = templateFindings({
166
+ registry: { ...registry, points: selected.map((s ) => s.point) }, repoRoot, fsImpl,
167
+ });
168
+ if (defects.length > 0) return refuse('template-invalid', 'templates', 'the selected templates are invalid', 'valid core templates', defects);
169
+ const host = hostVersion(repoRoot);
170
+ const parsed = typeof host === 'string' ? parseSemVer(host) : null;
171
+ if (!parsed) return refuse('host-incompatible', 'doflow', 'the running DoFlow version cannot be read', 'a running semver version');
172
+
173
+ const warnings = [];
174
+ const rows = lockPackages(readLock({ scope: 'global', homeDir }, { fsImpl }));
175
+ if (rows.some((row ) => row.id === id)) warnings.push(finding('id', 'collision-package', `package '${id}' is already recorded`, 'use update to apply edited source', 'warning'));
176
+
177
+ const tierMax = selected.reduce((top , s ) => TIERS.indexOf(s.point.tier) > TIERS.indexOf(top) ? s.point.tier : top, 'inert');
178
+ const next = nextSteps(id, target, tierMax, grantsPath);
179
+ const substitutions = (local ) => ({
180
+ package: id, local, qualified: qualifiedId(id, local) ?? '', projected: projectedName(qualifiedId(id, local) ?? '') ?? '',
181
+ version: '0.1.0', host,
182
+ });
183
+ const replace = (text , values ) => {
184
+ const result = text.replace(/\{\{([^{}]*)\}\}/g, (match , name ) => {
185
+ if (!Object.hasOwn(values, name)) throw new Error(`unknown template placeholder ${match}`);
186
+ return values[name];
187
+ });
188
+ if (result.includes('{{')) throw new Error('unclosed template placeholder');
189
+ return result;
190
+ };
191
+ const files = new Map ();
192
+ const put = (relative , text , mode = 0o644) => {
193
+ if (relative.includes('\\') || relative.split('/').some((part ) => !SEGMENT.test(part))
194
+ || relative.length > registry.limits.pathLength) throw new Error(`invalid template output path ${relative}`);
195
+ if (files.has(relative)) throw new Error(`templates write the same file: ${relative}`);
196
+ files.set(relative, { text, mode });
197
+ };
198
+ const programs = [];
199
+ const contributions = [];
200
+ const inventory = [];
201
+ for (const { point, local, version } of selected) {
202
+ const base = path.join(repoRoot, 'core', 'registry', point.template);
203
+ const values = substitutions(local);
204
+ const template = JSON.parse(replace(fsImpl.readFileSync(path.join(base, 'template.json'), 'utf8'), values));
205
+ const contribution = { point: point.id, version, id: local, [point.entry]: template.path };
206
+ if (point.tier === 'executable') {
207
+ programs.push(...template.programs);
208
+ contribution.programs = template.programs.map((p ) => p.id);
209
+ }
210
+ contributions.push(contribution);
211
+ inventory.push({ point: point.id, version, qualifiedId: qualifiedId(id, local), tier: point.tier, [point.entry]: template.path });
212
+ const copy = (source , relative = '') => {
213
+ for (const name of fsImpl.readdirSync(source).sort()) {
214
+ const abs = path.join(source, name);
215
+ const info = fsImpl.lstatSync(abs);
216
+ const rel = relative ? `${relative}/${name}` : name;
217
+ if (info.isSymbolicLink()) throw new Error(`template symlink: ${abs}`);
218
+ if (info.isDirectory()) copy(abs, rel);
219
+ else if (info.isFile()) put(
220
+ point.entry === 'file' ? template.path : `${template.path}/${replace(rel, values)}`,
221
+ replace(fsImpl.readFileSync(abs, 'utf8'), values), info.mode & 0o111 ? 0o755 : 0o644,
222
+ );
223
+ else throw new Error(`template special file: ${abs}`);
224
+ }
225
+ };
226
+ copy(path.join(base, 'files'));
227
+ }
228
+ const manifest = {
229
+ manifest: 1, id, version: '0.1.0', doflow: `>=${host} <${BigInt(parsed.major) + 1n}.0.0`,
230
+ ...(programs.length ? { programs } : {}), contributions,
231
+ };
232
+ put(MANIFEST_FILE, `${JSON.stringify(manifest, null, 2)}\n`);
233
+ put('README.md', replace(fsImpl.readFileSync(path.join(repoRoot, 'core', 'registry', 'package-templates', 'README.md'), 'utf8'), {
234
+ ...substitutions(id),
235
+ contributions: inventory.map((c ) => `- ${c.point} ${c.qualifiedId} ${c.tier} ${c.file ?? c.dir}`).join('\n'),
236
+ next: next.map((n ) => `${n.step}: ${n.command ? `${n.command}; ` : ''}${n.note}`).join('\n'),
237
+ }));
238
+
239
+ const parent = path.dirname(target);
240
+ fsImpl.mkdirSync(parent, { recursive: true });
241
+ for (const name of fsImpl.readdirSync(parent)) {
242
+ const sibling = path.join(parent, name);
243
+ if (!STAGING.test(name) || sibling === target) continue;
244
+ const info = fsImpl.lstatSync(sibling);
245
+ if (info.isDirectory() && !info.isSymbolicLink() && now - info.mtimeMs > DAY) fsImpl.rmSync(sibling, { recursive: true, force: true });
246
+ }
247
+ staging = path.join(parent, `.staging-${id}-${randomBytes(4).toString('hex')}`);
248
+ fsImpl.mkdirSync(staging);
249
+ for (const [relative, file] of files) {
250
+ const output = path.join(staging, ...relative.split('/'));
251
+ fsImpl.mkdirSync(path.dirname(output), { recursive: true });
252
+ fsImpl.writeFileSync(output, file.text, { flag: 'wx', mode: file.mode });
253
+ }
254
+ const checked = checkPackage(staging, { repoRoot, pointsRegistry: registry, hostVersion: host, bundled: [], fsImpl });
255
+ if (!checked.ok) return refuse('template-invalid', 'templates', 'the staged scaffold failed package check', 'a clean staged package', checked.findings);
256
+ if (forbidden() || real !== originalReal) return refuse('target-forbidden', 'dir', 'target ancestry changed during scaffolding', 'the original safe target');
257
+ if (nonempty()) return refuse('target-not-empty', 'dir', `target became non-empty: ${target}`, 'a missing or empty directory');
258
+ if (stat(target, fsImpl)) fsImpl.rmdirSync(target);
259
+ try { fsImpl.renameSync(staging, target); }
260
+ catch (error ) {
261
+ if (['EEXIST', 'ENOTEMPTY'].includes(error.code)) return refuse('target-not-empty', 'dir', `target became non-empty: ${target}`, 'a missing or empty directory');
262
+ throw error;
263
+ }
264
+ staging = null;
265
+ return {
266
+ ok: true, id, dir: target, files: [...files.keys()].sort(), contributions: inventory, tierMax,
267
+ contentDigest: checked.contentDigest, capabilityDigest: checked.capabilityDigest,
268
+ ignoredByGit: ignoredByGit(target, fsImpl, env), findings: [...warnings, ...checked.findings], next,
269
+ };
270
+ } catch (error ) {
271
+ return refuse('template-invalid', 'templates', error.message || String(error), 'a readable, valid scaffold template and writable target');
272
+ } finally {
273
+ if (staging !== null) fsImpl.rmSync(staging, { recursive: true, force: true });
274
+ }
275
+ }
276
+
277
+ module.exports = { scaffoldPackage };
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Parse package source specs for IC-022: local paths, git URLs and npm names.
5
+ * Unsupported schemes and URL credentials are refused as `bad-type`. This module
6
+ * dispatches git and pinned npm fetches through `fetch.ts`; the caller supplies the staging directory.
7
+ */
8
+
9
+ const { pinGit, fetchGit, pinNpm, fetchNpm } = require('./fetch.js');
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ const ALLOWED = 'a local path, git:https://<host>/<repo>[#<subdir>]@<ref>, or npm:<name>@<version or range>';
18
+
19
+ function badType(message ) {
20
+ return {
21
+ ok: false,
22
+ finding: { package: null, field: 'source', code: 'bad-type', severity: 'error', message, allowed: ALLOWED },
23
+ };
24
+ }
25
+
26
+ const hasControl = (value ) => /[\u0000-\u001f\u007f]/u.test(value);
27
+ const hasSpaceOrControl = (value ) => /[\s\u0000-\u001f\u007f]/u.test(value);
28
+
29
+ function parseSource(spec ) {
30
+ if (spec.startsWith('git:')) {
31
+ const body = spec.slice(4);
32
+ const refAt = body.lastIndexOf('@');
33
+ if (refAt < 0) return badType('git source needs a non-empty ref after the final @');
34
+ const target = body.slice(0, refAt);
35
+ const ref = body.slice(refAt + 1);
36
+ const hashAt = target.indexOf('#');
37
+ const url = hashAt < 0 ? target : target.slice(0, hashAt);
38
+ const subdir = hashAt < 0 ? undefined : target.slice(hashAt + 1);
39
+ if (!url || url.length > 512 || hasSpaceOrControl(url)) return badType('git URL must be non-empty, at most 512 characters, and contain no whitespace or control characters');
40
+ if (!ref || ref.length > 512 || hasSpaceOrControl(ref) || ref.startsWith('-')) return badType('git ref must be non-empty, at most 512 characters, and contain no whitespace or control characters');
41
+ let parsed ;
42
+ try { parsed = new URL(url); } catch { return badType('git URL must be a valid https URL'); }
43
+ if (parsed.protocol !== 'https:' || !parsed.host) return badType('git URL must use https and include a host');
44
+ if (parsed.username || parsed.password) return badType('git URL must not contain credentials');
45
+ if (subdir !== undefined && (!subdir || subdir.startsWith('/') || subdir.startsWith('-') || subdir.includes('\\') || subdir.split('/').includes('..'))) {
46
+ return badType('git subdir must be non-empty, relative, and contain no .. segment or backslash');
47
+ }
48
+ return { ok: true, source: { type: 'git', url, ref, ...(subdir === undefined ? {} : { path: subdir }) } };
49
+ }
50
+ if (spec.startsWith('npm:')) {
51
+ const body = spec.slice(4);
52
+ const versionAt = body.lastIndexOf('@');
53
+ if (versionAt <= 0) return badType('npm source needs a package name and non-empty version or range');
54
+ const name = body.slice(0, versionAt);
55
+ const version = body.slice(versionAt + 1);
56
+ if (name.startsWith('-') || name.length > 214 || !/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name)) {
57
+ return badType('npm package name does not match the allowed package name grammar');
58
+ }
59
+ if (!version || version.length > 512 || hasControl(version)) return badType('npm version or range must be non-empty, at most 512 characters, and contain no control characters');
60
+ return { ok: true, source: { type: 'npm', name, version } };
61
+ }
62
+ if (/^[A-Za-z][A-Za-z0-9+.-]+:/.test(spec)) return badType('source scheme is not supported');
63
+ return { ok: true, source: { type: 'local', path: spec } };
64
+ }
65
+
66
+ function fetchGrantLine(source , at ) {
67
+ return JSON.stringify({ grant: 'fetch', source, at });
68
+ }
69
+
70
+ function pinSource(source , opts ) {
71
+ if (source?.type === 'git') return pinGit(source, opts);
72
+ if (source?.type === 'npm') return pinNpm(source, opts);
73
+ const message = 'source cannot be pinned';
74
+ return {
75
+ ok: false,
76
+ finding: { package: null, field: 'source', code: 'bad-type', severity: 'error', message, allowed: ALLOWED },
77
+ };
78
+ }
79
+
80
+ function fetchSource(pinned , opts ) {
81
+ if (pinned?.type === 'git') return fetchGit(pinned, opts);
82
+ if (pinned?.type === 'npm') return fetchNpm(pinned, opts);
83
+ const message = 'source cannot be fetched';
84
+ return {
85
+ ok: false,
86
+ findings: [{ package: null, field: 'source', code: 'bad-type', severity: 'error', message, allowed: ALLOWED }],
87
+ };
88
+ }
89
+
90
+ module.exports = { parseSource, fetchGrantLine, pinSource, fetchSource };