devrites 3.2.28 → 4.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 (897) hide show
  1. package/CHANGELOG.md +16 -144
  2. package/NOTICE.md +6 -5
  3. package/README.md +187 -95
  4. package/SECURITY.md +103 -79
  5. package/bin/devrites.mjs +140 -24
  6. package/docs/adr/0003-gate-model-hitl-pause.md +1 -1
  7. package/docs/adr/0013-native-codex-agent-orchestration.md +74 -0
  8. package/docs/adr/0014-native-host-hook-boundary.md +64 -0
  9. package/docs/adr/0015-read-only-root-native-orchestration.md +96 -0
  10. package/docs/adr/0016-codex-writer-fail-closed.md +53 -0
  11. package/docs/adr/0017-native-codex-writer-agent.md +63 -0
  12. package/docs/adr/0018-native-sandbox-instruction-writer-boundary.md +66 -0
  13. package/docs/adr/0019-native-boundary-with-deterministic-gates.md +48 -0
  14. package/docs/adr/0020-thin-engine-native-orchestration-boundary.md +54 -0
  15. package/docs/adr/0021-observable-workspace-compatibility.md +49 -0
  16. package/docs/adr/0022-native-orchestration-thin-engine.md +90 -0
  17. package/docs/adr/0023-native-workspace-reads-line-output.md +71 -0
  18. package/docs/adr/0024-native-policy-offline-installer-boundary.md +111 -0
  19. package/docs/adr/0025-evidence-gated-workspace-upgrades.md +64 -0
  20. package/docs/adr/0026-content-bound-proof-and-bounded-inputs.md +169 -0
  21. package/docs/adr/0027-content-bound-build-readiness.md +77 -0
  22. package/docs/adr/README.md +26 -11
  23. package/docs/agents/domain.md +2 -1
  24. package/docs/agents/issue-driven-rites.md +1 -1
  25. package/docs/architecture.md +191 -103
  26. package/docs/candidate-integrity.md +138 -0
  27. package/docs/capability-surface-selection.md +15 -5
  28. package/docs/cli.md +139 -92
  29. package/docs/command-map.md +88 -63
  30. package/docs/engine/commands.md +115 -507
  31. package/docs/engine/state-schema.md +94 -113
  32. package/docs/engine/workspace-schema.md +169 -229
  33. package/docs/flow.md +113 -68
  34. package/docs/harness-compliance.md +29 -56
  35. package/docs/markdown-instruction-upgrade-2026-08-02.md +302 -0
  36. package/docs/orchestration.md +136 -149
  37. package/docs/porting-to-a-new-harness.md +22 -7
  38. package/docs/quick-reference.md +21 -8
  39. package/docs/release.md +47 -4
  40. package/docs/skills.md +111 -72
  41. package/docs/templates/CRITIC_REVIEW_PACKET_TEMPLATE.md +2 -0
  42. package/docs/upstream-workflow-benchmark-2026-08-01.md +655 -0
  43. package/docs/usage.md +99 -65
  44. package/engine/Makefile +1 -2
  45. package/engine/cmd/releasepack/main.go +45 -25
  46. package/engine/cmd/releasepack/main_test.go +69 -0
  47. package/engine/commands.go +8 -258
  48. package/engine/harnessmatrix_test.go +28 -41
  49. package/engine/internal/devritespaths/paths.go +12 -28
  50. package/engine/internal/fsutil/copy.go +8 -0
  51. package/engine/internal/gate/gate.go +104 -105
  52. package/engine/internal/gate/gate_test.go +94 -84
  53. package/engine/internal/gate/readiness_binding.go +203 -0
  54. package/engine/internal/gate/readiness_binding_test.go +271 -0
  55. package/engine/internal/gitenv/env.go +46 -0
  56. package/engine/internal/gitenv/env_test.go +48 -0
  57. package/engine/internal/hostpack/hostpack.go +16 -20
  58. package/engine/internal/hostpack/hostpack_test.go +6 -6
  59. package/engine/internal/install/install.go +289 -511
  60. package/engine/internal/install/install_test.go +218 -306
  61. package/engine/internal/lib/candidate.go +475 -0
  62. package/engine/internal/lib/candidate_test.go +503 -0
  63. package/engine/internal/lib/closeout.go +17 -21
  64. package/engine/internal/lib/closeout_test.go +23 -0
  65. package/engine/internal/lib/cursor_compat_test.go +12 -75
  66. package/engine/internal/lib/evidencefresh.go +58 -47
  67. package/engine/internal/lib/evidencefresh_test.go +120 -0
  68. package/engine/internal/lib/resolve.go +13 -35
  69. package/engine/internal/lib/resolve_remediation_test.go +5 -17
  70. package/engine/internal/lib/secretscan.go +432 -0
  71. package/engine/internal/lib/secretscan_test.go +555 -0
  72. package/engine/internal/lib/util.go +24 -65
  73. package/engine/internal/lib/util_test.go +44 -0
  74. package/engine/internal/markdowntext/markdowntext.go +108 -0
  75. package/engine/internal/markdowntext/markdowntext_test.go +58 -0
  76. package/engine/internal/markdowntext/testdata/structural.json +27 -0
  77. package/engine/internal/reason/reason.go +2 -123
  78. package/engine/internal/reason/reason_test.go +4 -2
  79. package/engine/internal/rootfacts/facts.go +6 -8
  80. package/engine/internal/rootfacts/facts_test.go +20 -0
  81. package/engine/internal/state/cmd/workflowmanifest/main.go +10 -12
  82. package/engine/internal/state/cursor.go +53 -14
  83. package/engine/internal/state/cursor_test.go +57 -0
  84. package/engine/internal/state/feature.go +64 -154
  85. package/engine/internal/state/schema.go +24 -50
  86. package/engine/internal/state/state_test.go +151 -140
  87. package/engine/internal/state/status.go +4 -17
  88. package/engine/internal/state/workflow_manifest.json +9 -89
  89. package/engine/main.go +73 -502
  90. package/engine/root_routing_test.go +183 -212
  91. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/feature.md → work/auth-tokens/README.md} +0 -1
  92. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +3 -0
  93. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +3 -0
  94. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +3 -0
  95. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +3 -0
  96. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +3 -0
  97. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +3 -0
  98. package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/status.md → work/auth-tokens/state.md} +3 -0
  99. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +3 -0
  100. package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +3 -0
  101. package/engine/testdata/fixtures/basic/devrites-root/{features/search-ranking/feature.md → work/search-ranking/README.md} +0 -1
  102. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +3 -0
  103. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +3 -0
  104. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +3 -0
  105. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +3 -0
  106. package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +2 -0
  107. package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +1 -1
  108. package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +1 -1
  109. package/engine/tests/adr_0003_gate_exit_code_test.go +2 -2
  110. package/engine/tests/adr_0011_define_plan_test.go +2 -2
  111. package/engine/tests/adr_0027_readiness_binding_test.go +106 -0
  112. package/engine/tests/cli_test.go +24 -107
  113. package/engine/tests/gate_test.go +177 -200
  114. package/engine/tests/meta_test.go +5 -35
  115. package/engine/tests/parity_closeout_test.go +12 -12
  116. package/engine/tests/parity_resolve_test.go +6 -16
  117. package/engine/tests/parity_test.go +8 -302
  118. package/install.sh +201 -34
  119. package/pack/.claude/agents/devrites-code-reviewer.md +9 -18
  120. package/pack/.claude/agents/devrites-devex-reviewer.md +10 -18
  121. package/pack/.claude/agents/devrites-doubt-reviewer.md +10 -20
  122. package/pack/.claude/agents/devrites-evidence-scout.md +14 -26
  123. package/pack/.claude/agents/devrites-frontend-reviewer.md +9 -15
  124. package/pack/.claude/agents/devrites-performance-reviewer.md +13 -21
  125. package/pack/.claude/agents/devrites-plan-drafter.md +24 -28
  126. package/pack/.claude/agents/devrites-plan-reviewer.md +30 -47
  127. package/pack/.claude/agents/devrites-proof-runner.md +41 -47
  128. package/pack/.claude/agents/devrites-retrospector.md +34 -79
  129. package/pack/.claude/agents/devrites-security-auditor.md +9 -17
  130. package/pack/.claude/agents/devrites-simplifier-reviewer.md +9 -18
  131. package/pack/.claude/agents/devrites-slice-wright.md +44 -62
  132. package/pack/.claude/agents/devrites-spec-reviewer.md +38 -50
  133. package/pack/.claude/agents/devrites-strategy-reviewer.md +10 -19
  134. package/pack/.claude/agents/devrites-test-analyst.md +22 -21
  135. package/pack/.claude/agents/devrites-upgrade-planner.md +52 -71
  136. package/pack/.claude/settings.json +12 -100
  137. package/pack/.claude/skills/devrites-api-interface/SKILL.md +1 -2
  138. package/pack/.claude/skills/devrites-audit/SKILL.md +6 -9
  139. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +0 -1
  140. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  141. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  142. package/pack/.claude/skills/devrites-doubt/SKILL.md +4 -6
  143. package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  144. package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  145. package/pack/.claude/skills/devrites-interview/SKILL.md +31 -36
  146. package/pack/.claude/skills/devrites-lib/SKILL.md +37 -66
  147. package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  148. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +20 -12
  149. package/pack/.claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  150. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  151. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  152. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +6 -3
  153. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  154. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  155. package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  156. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  157. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  158. package/pack/.claude/skills/devrites-lib/reference/standards/core.md +59 -54
  159. package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  160. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  161. package/pack/.claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  162. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +13 -31
  163. package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  164. package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  165. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  166. package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  167. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  168. package/pack/.claude/skills/devrites-prose-craft/SKILL.md +3 -4
  169. package/pack/.claude/skills/devrites-source-driven/SKILL.md +3 -4
  170. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +3 -3
  171. package/pack/.claude/skills/rite/SKILL.md +12 -13
  172. package/pack/.claude/skills/rite/reference/menu.md +11 -11
  173. package/pack/.claude/skills/rite-adopt/SKILL.md +29 -84
  174. package/pack/.claude/skills/rite-adopt/reference/adoption.md +16 -50
  175. package/pack/.claude/skills/rite-autocomplete/SKILL.md +65 -54
  176. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +24 -17
  177. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  178. package/pack/.claude/skills/rite-build/SKILL.md +28 -34
  179. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +36 -34
  180. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +5 -19
  181. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  182. package/pack/.claude/skills/rite-build/reference/checkpoint.md +17 -17
  183. package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  184. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  185. package/pack/.claude/skills/rite-build/reference/output.md +14 -24
  186. package/pack/.claude/skills/rite-build/reference/phase-contract.md +49 -188
  187. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  188. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  189. package/pack/.claude/skills/rite-clarify/SKILL.md +46 -40
  190. package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  191. package/pack/.claude/skills/rite-converge/SKILL.md +9 -33
  192. package/pack/.claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  193. package/pack/.claude/skills/rite-customize/SKILL.md +34 -50
  194. package/pack/.claude/skills/rite-define/SKILL.md +34 -58
  195. package/pack/.claude/skills/rite-define/reference/plan-template.md +15 -0
  196. package/pack/.claude/skills/rite-doctor/SKILL.md +52 -64
  197. package/pack/.claude/skills/rite-dogfood/SKILL.md +1 -2
  198. package/pack/.claude/skills/rite-explain/SKILL.md +1 -20
  199. package/pack/.claude/skills/rite-frame/SKILL.md +1 -7
  200. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +1 -1
  201. package/pack/.claude/skills/rite-handoff/SKILL.md +1 -2
  202. package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  203. package/pack/.claude/skills/rite-learn/SKILL.md +44 -104
  204. package/pack/.claude/skills/rite-plan/SKILL.md +14 -26
  205. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  206. package/pack/.claude/skills/rite-polish/SKILL.md +20 -28
  207. package/pack/.claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  208. package/pack/{generated/claude/skills/rite-ship → .claude/skills/rite-polish}/reference/design-memory.md +23 -21
  209. package/pack/.claude/skills/rite-polish/reference/ledger.md +62 -0
  210. package/pack/.claude/skills/rite-pov/SKILL.md +28 -32
  211. package/pack/.claude/skills/rite-pr-feedback/SKILL.md +0 -1
  212. package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -13
  213. package/pack/.claude/skills/rite-prototype/SKILL.md +0 -6
  214. package/pack/.claude/skills/rite-prove/SKILL.md +73 -68
  215. package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  216. package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  217. package/pack/.claude/skills/rite-quick/SKILL.md +5 -19
  218. package/pack/.claude/skills/rite-resolve/SKILL.md +4 -21
  219. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  220. package/pack/.claude/skills/rite-review/SKILL.md +34 -57
  221. package/pack/.claude/skills/rite-seal/SKILL.md +54 -87
  222. package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  223. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +31 -56
  224. package/pack/.claude/skills/rite-seal/reference/go-no-go.md +27 -51
  225. package/pack/.claude/skills/rite-seal/reference/output.md +2 -4
  226. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +42 -140
  227. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  228. package/pack/.claude/skills/rite-seal/reference/seal-template.md +17 -28
  229. package/pack/.claude/skills/rite-ship/SKILL.md +65 -93
  230. package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  231. package/pack/.claude/skills/rite-ship/reference/close-out.md +4 -12
  232. package/pack/.claude/skills/rite-ship/reference/git-ship.md +86 -48
  233. package/pack/.claude/skills/rite-ship/reference/ship-template.md +22 -16
  234. package/pack/.claude/skills/rite-spec/SKILL.md +62 -95
  235. package/pack/.claude/skills/rite-spec/reference/investigation.md +1 -4
  236. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +21 -20
  237. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  238. package/pack/.claude/skills/rite-spec/reference/spec-template.md +47 -49
  239. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +28 -13
  240. package/pack/.claude/skills/rite-status/SKILL.md +30 -88
  241. package/pack/.claude/skills/rite-temper/SKILL.md +12 -31
  242. package/pack/.claude/skills/rite-upgrade/SKILL.md +86 -101
  243. package/pack/.claude/skills/rite-vet/SKILL.md +88 -117
  244. package/pack/.claude/skills/rite-vet/reference/artifacts.md +56 -50
  245. package/pack/.claude/skills/rite-vet/reference/cross-model.md +15 -39
  246. package/pack/.claude/skills/rite-vet/reference/depth.md +2 -2
  247. package/pack/.claude/skills/rite-vet/reference/review-axes.md +3 -6
  248. package/pack/.claude/skills/rite-zoom-out/SKILL.md +0 -6
  249. package/pack/generated/README.md +2 -2
  250. package/pack/generated/claude/agents/devrites-code-reviewer.md +9 -18
  251. package/pack/generated/claude/agents/devrites-devex-reviewer.md +10 -18
  252. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +10 -20
  253. package/pack/generated/claude/agents/devrites-evidence-scout.md +14 -26
  254. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +9 -15
  255. package/pack/generated/claude/agents/devrites-performance-reviewer.md +13 -21
  256. package/pack/generated/claude/agents/devrites-plan-drafter.md +24 -28
  257. package/pack/generated/claude/agents/devrites-plan-reviewer.md +30 -47
  258. package/pack/generated/claude/agents/devrites-proof-runner.md +41 -47
  259. package/pack/generated/claude/agents/devrites-retrospector.md +34 -79
  260. package/pack/generated/claude/agents/devrites-security-auditor.md +9 -17
  261. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +9 -18
  262. package/pack/generated/claude/agents/devrites-slice-wright.md +44 -62
  263. package/pack/generated/claude/agents/devrites-spec-reviewer.md +38 -50
  264. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +10 -19
  265. package/pack/generated/claude/agents/devrites-test-analyst.md +22 -21
  266. package/pack/generated/claude/agents/devrites-upgrade-planner.md +52 -71
  267. package/pack/generated/claude/settings.json +12 -100
  268. package/pack/generated/claude/skills/devrites-api-interface/SKILL.md +1 -2
  269. package/pack/generated/claude/skills/devrites-audit/SKILL.md +6 -9
  270. package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +0 -1
  271. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +21 -27
  272. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  273. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +4 -6
  274. package/pack/generated/claude/skills/devrites-frontend-craft/SKILL.md +1 -2
  275. package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  276. package/pack/generated/claude/skills/devrites-interview/SKILL.md +31 -36
  277. package/pack/generated/claude/skills/devrites-lib/SKILL.md +37 -66
  278. package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  279. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +20 -12
  280. package/pack/generated/claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  281. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  282. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +26 -93
  283. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +6 -3
  284. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  285. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +90 -216
  286. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  287. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
  288. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  289. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +59 -54
  290. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
  291. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +29 -143
  292. package/pack/generated/claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  293. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +13 -31
  294. package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  295. package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  296. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +22 -11
  297. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
  298. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  299. package/pack/generated/claude/skills/devrites-prose-craft/SKILL.md +3 -4
  300. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +3 -4
  301. package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +3 -3
  302. package/pack/generated/claude/skills/rite/SKILL.md +12 -13
  303. package/pack/generated/claude/skills/rite/reference/menu.md +11 -11
  304. package/pack/generated/claude/skills/rite-adopt/SKILL.md +29 -84
  305. package/pack/generated/claude/skills/rite-adopt/reference/adoption.md +16 -50
  306. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +65 -54
  307. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +24 -17
  308. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  309. package/pack/generated/claude/skills/rite-build/SKILL.md +28 -34
  310. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +36 -34
  311. package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +5 -19
  312. package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  313. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +17 -17
  314. package/pack/generated/claude/skills/rite-build/reference/frontend-trigger.md +3 -3
  315. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
  316. package/pack/generated/claude/skills/rite-build/reference/output.md +14 -24
  317. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +49 -188
  318. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
  319. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +43 -133
  320. package/pack/generated/claude/skills/rite-clarify/SKILL.md +46 -40
  321. package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
  322. package/pack/generated/claude/skills/rite-converge/SKILL.md +9 -33
  323. package/pack/generated/claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
  324. package/pack/generated/claude/skills/rite-customize/SKILL.md +34 -50
  325. package/pack/generated/claude/skills/rite-define/SKILL.md +34 -58
  326. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +15 -0
  327. package/pack/generated/claude/skills/rite-doctor/SKILL.md +52 -64
  328. package/pack/generated/claude/skills/rite-dogfood/SKILL.md +1 -2
  329. package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -20
  330. package/pack/generated/claude/skills/rite-frame/SKILL.md +1 -7
  331. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +1 -1
  332. package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -2
  333. package/pack/generated/claude/skills/rite-handoff/reference/handoff-template.md +1 -1
  334. package/pack/generated/claude/skills/rite-learn/SKILL.md +44 -104
  335. package/pack/generated/claude/skills/rite-plan/SKILL.md +14 -26
  336. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -1
  337. package/pack/generated/claude/skills/rite-polish/SKILL.md +20 -28
  338. package/pack/generated/claude/skills/rite-polish/reference/adr-promotion.md +11 -0
  339. package/pack/generated/{codex/skills/rite-ship → claude/skills/rite-polish}/reference/design-memory.md +23 -21
  340. package/pack/generated/claude/skills/rite-polish/reference/ledger.md +62 -0
  341. package/pack/generated/claude/skills/rite-pov/SKILL.md +28 -32
  342. package/pack/generated/claude/skills/rite-pr-feedback/SKILL.md +0 -1
  343. package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -13
  344. package/pack/generated/claude/skills/rite-prototype/SKILL.md +0 -6
  345. package/pack/generated/claude/skills/rite-prove/SKILL.md +73 -68
  346. package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
  347. package/pack/generated/claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
  348. package/pack/generated/claude/skills/rite-quick/SKILL.md +5 -19
  349. package/pack/generated/claude/skills/rite-resolve/SKILL.md +4 -21
  350. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  351. package/pack/generated/claude/skills/rite-review/SKILL.md +34 -57
  352. package/pack/generated/claude/skills/rite-seal/SKILL.md +54 -87
  353. package/pack/generated/claude/skills/rite-seal/reference/anti-patterns.md +2 -2
  354. package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +31 -56
  355. package/pack/generated/claude/skills/rite-seal/reference/go-no-go.md +27 -51
  356. package/pack/generated/claude/skills/rite-seal/reference/output.md +2 -4
  357. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +42 -140
  358. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  359. package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +17 -28
  360. package/pack/generated/claude/skills/rite-ship/SKILL.md +65 -93
  361. package/pack/generated/claude/skills/rite-ship/reference/anti-patterns.md +4 -1
  362. package/pack/generated/claude/skills/rite-ship/reference/close-out.md +4 -12
  363. package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +86 -48
  364. package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +22 -16
  365. package/pack/generated/claude/skills/rite-spec/SKILL.md +62 -95
  366. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +1 -4
  367. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +21 -20
  368. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +16 -17
  369. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +47 -49
  370. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +28 -13
  371. package/pack/generated/claude/skills/rite-status/SKILL.md +30 -88
  372. package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -31
  373. package/pack/generated/claude/skills/rite-upgrade/SKILL.md +86 -101
  374. package/pack/generated/claude/skills/rite-vet/SKILL.md +88 -117
  375. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +56 -50
  376. package/pack/generated/claude/skills/rite-vet/reference/cross-model.md +15 -39
  377. package/pack/generated/claude/skills/rite-vet/reference/depth.md +2 -2
  378. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +3 -6
  379. package/pack/generated/claude/skills/rite-zoom-out/SKILL.md +0 -6
  380. package/pack/generated/codex/AGENTS.md +5 -4
  381. package/pack/generated/codex/agents/devrites-code-reviewer.toml +8 -27
  382. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +9 -27
  383. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +9 -29
  384. package/pack/generated/codex/agents/devrites-evidence-scout.toml +14 -36
  385. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +8 -24
  386. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +12 -30
  387. package/pack/generated/codex/agents/devrites-plan-drafter.toml +24 -38
  388. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +30 -57
  389. package/pack/generated/codex/agents/devrites-proof-runner.toml +41 -57
  390. package/pack/generated/codex/agents/devrites-retrospector.toml +34 -89
  391. package/pack/generated/codex/agents/devrites-security-auditor.toml +8 -26
  392. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +8 -27
  393. package/pack/generated/codex/agents/devrites-slice-wright.toml +44 -71
  394. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +38 -60
  395. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +9 -28
  396. package/pack/generated/codex/agents/devrites-test-analyst.toml +21 -30
  397. package/pack/generated/codex/agents/devrites-upgrade-planner.toml +52 -81
  398. package/pack/generated/codex/config.toml +7 -0
  399. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +1 -21
  400. package/pack/generated/codex/skills/devrites-audit/SKILL.md +6 -28
  401. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +1 -21
  402. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +21 -46
  403. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
  404. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +4 -25
  405. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +1 -21
  406. package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +2 -1
  407. package/pack/generated/codex/skills/devrites-interview/SKILL.md +32 -56
  408. package/pack/generated/codex/skills/devrites-lib/SKILL.md +38 -86
  409. package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +51 -0
  410. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +20 -12
  411. package/pack/generated/codex/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
  412. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
  413. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +26 -93
  414. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +6 -3
  415. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
  416. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +90 -216
  417. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
  418. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +9 -28
  419. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
  420. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +59 -54
  421. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -5
  422. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +29 -143
  423. package/pack/generated/codex/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
  424. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +13 -31
  425. package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
  426. package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
  427. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +22 -11
  428. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +15 -24
  429. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
  430. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +3 -23
  431. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +3 -23
  432. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +3 -22
  433. package/pack/generated/codex/skills/rite/SKILL.md +12 -32
  434. package/pack/generated/codex/skills/rite/reference/menu.md +11 -11
  435. package/pack/generated/codex/skills/rite-adopt/SKILL.md +29 -103
  436. package/pack/generated/codex/skills/rite-adopt/reference/adoption.md +16 -50
  437. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +65 -73
  438. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +24 -17
  439. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
  440. package/pack/generated/codex/skills/rite-build/SKILL.md +28 -53
  441. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +36 -34
  442. package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +5 -19
  443. package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +10 -9
  444. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +17 -17
  445. package/pack/generated/codex/skills/rite-build/reference/frontend-trigger.md +3 -3
  446. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +11 -14
  447. package/pack/generated/codex/skills/rite-build/reference/output.md +14 -24
  448. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +49 -188
  449. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +1 -1
  450. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +43 -133
  451. package/pack/generated/codex/skills/rite-clarify/SKILL.md +46 -59
  452. package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +4 -10
  453. package/pack/generated/codex/skills/rite-converge/SKILL.md +9 -52
  454. package/pack/generated/codex/skills/rite-converge/reference/convergence-assessment.md +2 -2
  455. package/pack/generated/codex/skills/rite-customize/SKILL.md +32 -67
  456. package/pack/generated/codex/skills/rite-define/SKILL.md +34 -77
  457. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +15 -0
  458. package/pack/generated/codex/skills/rite-doctor/SKILL.md +52 -83
  459. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +1 -21
  460. package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -39
  461. package/pack/generated/codex/skills/rite-frame/SKILL.md +1 -26
  462. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +1 -1
  463. package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -21
  464. package/pack/generated/codex/skills/rite-handoff/reference/handoff-template.md +1 -1
  465. package/pack/generated/codex/skills/rite-learn/SKILL.md +44 -123
  466. package/pack/generated/codex/skills/rite-plan/SKILL.md +14 -45
  467. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +4 -1
  468. package/pack/generated/codex/skills/rite-polish/SKILL.md +20 -47
  469. package/pack/generated/codex/skills/rite-polish/reference/adr-promotion.md +11 -0
  470. package/pack/{.claude/skills/rite-ship → generated/codex/skills/rite-polish}/reference/design-memory.md +23 -21
  471. package/pack/generated/codex/skills/rite-polish/reference/ledger.md +62 -0
  472. package/pack/generated/codex/skills/rite-pov/SKILL.md +28 -51
  473. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +0 -20
  474. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -32
  475. package/pack/generated/codex/skills/rite-prototype/SKILL.md +0 -25
  476. package/pack/generated/codex/skills/rite-prove/SKILL.md +73 -87
  477. package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +34 -30
  478. package/pack/generated/codex/skills/rite-prove/reference/test-command-discovery.md +15 -14
  479. package/pack/generated/codex/skills/rite-quick/SKILL.md +5 -38
  480. package/pack/generated/codex/skills/rite-resolve/SKILL.md +4 -40
  481. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -2
  482. package/pack/generated/codex/skills/rite-review/SKILL.md +34 -76
  483. package/pack/generated/codex/skills/rite-seal/SKILL.md +51 -103
  484. package/pack/generated/codex/skills/rite-seal/reference/anti-patterns.md +2 -2
  485. package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +31 -56
  486. package/pack/generated/codex/skills/rite-seal/reference/go-no-go.md +27 -51
  487. package/pack/generated/codex/skills/rite-seal/reference/output.md +2 -4
  488. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +42 -140
  489. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +2 -2
  490. package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +17 -28
  491. package/pack/generated/codex/skills/rite-ship/SKILL.md +65 -112
  492. package/pack/generated/codex/skills/rite-ship/reference/anti-patterns.md +4 -1
  493. package/pack/generated/codex/skills/rite-ship/reference/close-out.md +4 -12
  494. package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +86 -48
  495. package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +22 -16
  496. package/pack/generated/codex/skills/rite-spec/SKILL.md +62 -114
  497. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +1 -4
  498. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +21 -20
  499. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +16 -17
  500. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +47 -49
  501. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +28 -13
  502. package/pack/generated/codex/skills/rite-status/SKILL.md +30 -107
  503. package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -50
  504. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +86 -120
  505. package/pack/generated/codex/skills/rite-vet/SKILL.md +88 -136
  506. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +56 -50
  507. package/pack/generated/codex/skills/rite-vet/reference/cross-model.md +15 -39
  508. package/pack/generated/codex/skills/rite-vet/reference/depth.md +2 -2
  509. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +3 -6
  510. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +0 -25
  511. package/package.json +6 -5
  512. package/scripts/build-host-artifacts.sh +5 -9
  513. package/scripts/codex-generate.sh +18 -278
  514. package/scripts/install-lib.sh +121 -34
  515. package/uninstall.sh +19 -17
  516. package/update.sh +26 -18
  517. package/docs/engine/agent-contract.md +0 -172
  518. package/docs/extensions.md +0 -178
  519. package/engine/git_guard.go +0 -187
  520. package/engine/git_guard_test.go +0 -283
  521. package/engine/harnessmatrix.go +0 -60
  522. package/engine/hookpolicy.go +0 -113
  523. package/engine/hookpolicy_test.go +0 -160
  524. package/engine/hooks.go +0 -677
  525. package/engine/hooks_agent_dispatch.go +0 -1726
  526. package/engine/hooks_events_test.go +0 -431
  527. package/engine/hooks_workspace.go +0 -1196
  528. package/engine/internal/doctor/doctor.go +0 -306
  529. package/engine/internal/doctor/doctor_test.go +0 -252
  530. package/engine/internal/forge/forge.go +0 -938
  531. package/engine/internal/forge/forge_test.go +0 -576
  532. package/engine/internal/forge/git.go +0 -245
  533. package/engine/internal/forge/liveness_unix.go +0 -48
  534. package/engine/internal/forge/liveness_windows.go +0 -67
  535. package/engine/internal/forge/manifest.go +0 -402
  536. package/engine/internal/harness/compliance.go +0 -96
  537. package/engine/internal/harness/harness.go +0 -301
  538. package/engine/internal/harness/harness_test.go +0 -124
  539. package/engine/internal/iohooks/detach_other.go +0 -11
  540. package/engine/internal/iohooks/detach_unix.go +0 -11
  541. package/engine/internal/iohooks/iohooks.go +0 -839
  542. package/engine/internal/iohooks/iohooks_test.go +0 -561
  543. package/engine/internal/lib/adoption.go +0 -659
  544. package/engine/internal/lib/adoption_test.go +0 -121
  545. package/engine/internal/lib/analyze.go +0 -232
  546. package/engine/internal/lib/archivesearch.go +0 -125
  547. package/engine/internal/lib/archivesearch_test.go +0 -138
  548. package/engine/internal/lib/budget.go +0 -172
  549. package/engine/internal/lib/buildreadiness.go +0 -111
  550. package/engine/internal/lib/checkacceptance.go +0 -176
  551. package/engine/internal/lib/clarifyreturn.go +0 -91
  552. package/engine/internal/lib/clarifyreturn_test.go +0 -123
  553. package/engine/internal/lib/command_other.go +0 -7
  554. package/engine/internal/lib/command_unix.go +0 -24
  555. package/engine/internal/lib/context.go +0 -280
  556. package/engine/internal/lib/conventions.go +0 -449
  557. package/engine/internal/lib/coverage.go +0 -145
  558. package/engine/internal/lib/dispatchwaive.go +0 -31
  559. package/engine/internal/lib/dispatchwaive_test.go +0 -23
  560. package/engine/internal/lib/doubtcoverage.go +0 -78
  561. package/engine/internal/lib/extensions.go +0 -622
  562. package/engine/internal/lib/extensions_test.go +0 -305
  563. package/engine/internal/lib/footprint.go +0 -239
  564. package/engine/internal/lib/gitauthority.go +0 -601
  565. package/engine/internal/lib/gitauthority_test.go +0 -346
  566. package/engine/internal/lib/jsonout.go +0 -129
  567. package/engine/internal/lib/jsonout_test.go +0 -73
  568. package/engine/internal/lib/lanes.go +0 -112
  569. package/engine/internal/lib/learnings.go +0 -397
  570. package/engine/internal/lib/learnings_inject_test.go +0 -77
  571. package/engine/internal/lib/learnings_remediation_test.go +0 -24
  572. package/engine/internal/lib/ledger.go +0 -311
  573. package/engine/internal/lib/ledger_test.go +0 -186
  574. package/engine/internal/lib/mutationgate.go +0 -89
  575. package/engine/internal/lib/observability_test.go +0 -358
  576. package/engine/internal/lib/overrides.go +0 -164
  577. package/engine/internal/lib/overrides_test.go +0 -112
  578. package/engine/internal/lib/preamble.go +0 -159
  579. package/engine/internal/lib/preamble_questions_test.go +0 -10
  580. package/engine/internal/lib/progress.go +0 -210
  581. package/engine/internal/lib/provenance.go +0 -462
  582. package/engine/internal/lib/provenance_test.go +0 -154
  583. package/engine/internal/lib/readiness_contract.json +0 -118
  584. package/engine/internal/lib/readinessartifact.go +0 -533
  585. package/engine/internal/lib/readinessartifact_test.go +0 -422
  586. package/engine/internal/lib/reconcile.go +0 -1496
  587. package/engine/internal/lib/reconcile_test.go +0 -1071
  588. package/engine/internal/lib/recoveryattempts.go +0 -298
  589. package/engine/internal/lib/recoveryattempts_test.go +0 -215
  590. package/engine/internal/lib/reviewers.go +0 -143
  591. package/engine/internal/lib/reviewers_test.go +0 -45
  592. package/engine/internal/lib/reviewerstats.go +0 -199
  593. package/engine/internal/lib/reviewerstats_test.go +0 -104
  594. package/engine/internal/lib/reviewintegrity.go +0 -143
  595. package/engine/internal/lib/reviewintegrity_test.go +0 -103
  596. package/engine/internal/lib/runbook.go +0 -295
  597. package/engine/internal/lib/runbook_context_test.go +0 -182
  598. package/engine/internal/lib/session.go +0 -1020
  599. package/engine/internal/lib/session_test.go +0 -177
  600. package/engine/internal/lib/specparse.go +0 -125
  601. package/engine/internal/lib/specskeleton.go +0 -79
  602. package/engine/internal/lib/specvalidate.go +0 -391
  603. package/engine/internal/lib/specvalidate_test.go +0 -185
  604. package/engine/internal/lib/stuck.go +0 -206
  605. package/engine/internal/lib/templates/conventions_drift.tmpl +0 -6
  606. package/engine/internal/lib/templates/conventions_ledger_header.md +0 -6
  607. package/engine/internal/lib/templates/conventions_orient_header.txt +0 -1
  608. package/engine/internal/lib/testintegrity.go +0 -199
  609. package/engine/internal/lib/testintegrity_test.go +0 -149
  610. package/engine/internal/lib/tickafk.go +0 -86
  611. package/engine/internal/migrate/migrate.go +0 -279
  612. package/engine/internal/migrate/migrate_test.go +0 -157
  613. package/engine/internal/orient/firstrun.go +0 -50
  614. package/engine/internal/orient/firstrun_test.go +0 -72
  615. package/engine/internal/orient/orient.go +0 -69
  616. package/engine/internal/orient/orient_test.go +0 -25
  617. package/engine/internal/profile/profile.go +0 -284
  618. package/engine/internal/profile/profile_test.go +0 -13
  619. package/engine/internal/state/clarify_transition.go +0 -165
  620. package/engine/internal/state/clarify_transition_test.go +0 -130
  621. package/engine/internal/state/snapshot.go +0 -457
  622. package/engine/internal/toolpolicy/classifier.go +0 -533
  623. package/engine/internal/toolpolicy/classifier_test.go +0 -424
  624. package/engine/internal/toolpolicy/git.go +0 -616
  625. package/engine/internal/toolpolicy/scanner.go +0 -382
  626. package/engine/observability_cli_test.go +0 -52
  627. package/engine/profile_test.go +0 -76
  628. package/engine/subagent_orient_context.txt +0 -8
  629. package/engine/testdata/golden/TestParityA1Guard/devrites-edit-allowed.golden +0 -1
  630. package/engine/testdata/golden/TestParityA1Guard/no-window-silent.golden +0 -1
  631. package/engine/testdata/golden/TestParityA1Guard/non-edit-tool.golden +0 -1
  632. package/engine/testdata/golden/TestParityA1Guard/observe-mode-silent.golden +0 -1
  633. package/engine/testdata/golden/TestParityA1Guard/source-edit-enforce-denies.golden +0 -2
  634. package/engine/testdata/golden/TestParityA1Guard/subagent-allowed.golden +0 -1
  635. package/engine/testdata/golden/TestParityAnalyze/noactive.golden +0 -1
  636. package/engine/testdata/golden/TestParityAnalyze/slug=covered.golden +0 -15
  637. package/engine/testdata/golden/TestParityAnalyze/slug=dangling.golden +0 -16
  638. package/engine/testdata/golden/TestParityAnalyze/slug=ghost.golden +0 -1
  639. package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +0 -15
  640. package/engine/testdata/golden/TestParityAnalyze/slug=order.golden +0 -17
  641. package/engine/testdata/golden/TestParityAnalyze/slug=orphan.golden +0 -16
  642. package/engine/testdata/golden/TestParityAnalyze/slug=uncovered.golden +0 -16
  643. package/engine/testdata/golden/TestParityBudget/default-active.golden +0 -4
  644. package/engine/testdata/golden/TestParityBudget/extra-md.golden +0 -5
  645. package/engine/testdata/golden/TestParityBudget/no-active.golden +0 -1
  646. package/engine/testdata/golden/TestParityBudget/no-feature.golden +0 -1
  647. package/engine/testdata/golden/TestParityBudget/over-advisory.golden +0 -6
  648. package/engine/testdata/golden/TestParityBudget/over-strict.golden +0 -6
  649. package/engine/testdata/golden/TestParityBudget/within.golden +0 -6
  650. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +0 -2
  651. package/engine/testdata/golden/TestParityBuildReadiness/arg=approvenone.golden +0 -1
  652. package/engine/testdata/golden/TestParityBuildReadiness/arg=await.golden +0 -1
  653. package/engine/testdata/golden/TestParityBuildReadiness/arg=blocked.golden +0 -1
  654. package/engine/testdata/golden/TestParityBuildReadiness/arg=clarifyopen.golden +0 -1
  655. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +0 -2
  656. package/engine/testdata/golden/TestParityBuildReadiness/arg=ghost.golden +0 -1
  657. package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +0 -1
  658. package/engine/testdata/golden/TestParityBuildReadiness/arg=noapprove.golden +0 -1
  659. package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +0 -1
  660. package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +0 -1
  661. package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +0 -1
  662. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +0 -2
  663. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +0 -2
  664. package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +0 -1
  665. package/engine/testdata/golden/TestParityCheckAcceptance/arg=blank-section.golden +0 -1
  666. package/engine/testdata/golden/TestParityCheckAcceptance/arg=empty.golden +0 -1
  667. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-dropped.golden +0 -1
  668. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +0 -2
  669. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-unchecked.golden +0 -1
  670. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-gap.golden +0 -1
  671. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-ok.golden +0 -2
  672. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-seal.golden +0 -1
  673. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-section.golden +0 -1
  674. package/engine/testdata/golden/TestParityCheckAcceptance/arg=nonexistent.golden +0 -1
  675. package/engine/testdata/golden/TestParityCheckAcceptance.golden +0 -1
  676. package/engine/testdata/golden/TestParityCoverage/arg=flat.golden +0 -7
  677. package/engine/testdata/golden/TestParityCoverage/arg=full.golden +0 -10
  678. package/engine/testdata/golden/TestParityCoverage/arg=nonexistent.golden +0 -1
  679. package/engine/testdata/golden/TestParityCoverage/arg=nospec.golden +0 -1
  680. package/engine/testdata/golden/TestParityCoverage/arg=speconly.golden +0 -9
  681. package/engine/testdata/golden/TestParityCoverage.golden +0 -1
  682. package/engine/testdata/golden/TestParityCursor/bare.golden +0 -3
  683. package/engine/testdata/golden/TestParityCursor/full-afk.golden +0 -6
  684. package/engine/testdata/golden/TestParityCursor/full-hitl.golden +0 -5
  685. package/engine/testdata/golden/TestParityCursor/no-active.golden +0 -1
  686. package/engine/testdata/golden/TestParityCursor/red.golden +0 -5
  687. package/engine/testdata/golden/TestParityCursor/zerogates.golden +0 -5
  688. package/engine/testdata/golden/TestParityDoubtCoverage/arg=doubted.golden +0 -2
  689. package/engine/testdata/golden/TestParityDoubtCoverage/arg=ghost.golden +0 -2
  690. package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing.golden +0 -3
  691. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nodoubt.golden +0 -4
  692. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nolog.golden +0 -2
  693. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nowright.golden +0 -2
  694. package/engine/testdata/golden/TestParityDoubtCoverage.golden +0 -1
  695. package/engine/testdata/golden/TestParityMutationGate/active-slug.golden +0 -3
  696. package/engine/testdata/golden/TestParityMutationGate/explicit-slug.golden +0 -3
  697. package/engine/testdata/golden/TestParityMutationGate/ghost.golden +0 -1
  698. package/engine/testdata/golden/TestParityOrientSilentPath.golden +0 -1
  699. package/engine/testdata/golden/TestParityReconcile/bad-mode.golden +0 -1
  700. package/engine/testdata/golden/TestParityReconcile/check-clean/snapshot.golden +0 -2
  701. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +0 -2
  702. package/engine/testdata/golden/TestParityReconcile/check-no-base.golden +0 -1
  703. package/engine/testdata/golden/TestParityReconcile/check-no-claimed/snapshot.golden +0 -2
  704. package/engine/testdata/golden/TestParityReconcile/check-violation/snapshot.golden +0 -2
  705. package/engine/testdata/golden/TestParityReconcile/check-violation.golden +0 -1
  706. package/engine/testdata/golden/TestParityReconcile/close-window/snapshot.golden +0 -2
  707. package/engine/testdata/golden/TestParityReconcile/close-window.golden +0 -3
  708. package/engine/testdata/golden/TestParityReconcile/not-git.golden +0 -1
  709. package/engine/testdata/golden/TestParityReconcile/snapshot/snapshot.golden +0 -2
  710. package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +0 -1
  711. package/engine/testdata/golden/TestParityReconcile/snapshot.golden +0 -2
  712. package/engine/testdata/golden/TestParityRedwatch/fail-writes-red.golden +0 -2
  713. package/engine/testdata/golden/TestParityRedwatch/non-bash-tool.golden +0 -1
  714. package/engine/testdata/golden/TestParityRedwatch/non-test-command.golden +0 -1
  715. package/engine/testdata/golden/TestParityRedwatch/pass-clears-red.golden +0 -1
  716. package/engine/testdata/golden/TestParityResolve/next-qid.golden +0 -2
  717. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-devrites-agent-denies.golden +0 -2
  718. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-non-devrites-allows.golden +0 -1
  719. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-subagent-type-only-allows.golden +0 -1
  720. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-git-commit.golden +0 -2
  721. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-mutating.golden +0 -2
  722. package/engine/testdata/golden/TestParityReviewerReadonly/non-bash-tool.golden +0 -1
  723. package/engine/testdata/golden/TestParityReviewerReadonly/non-tool-input.golden +0 -1
  724. package/engine/testdata/golden/TestParityReviewerReadonly/observe-mutating-silent.golden +0 -1
  725. package/engine/testdata/golden/TestParityReviewerReadonly/safe-bash-command.golden +0 -1
  726. package/engine/testdata/golden/TestParitySpecValidate/arg=dup.golden +0 -1
  727. package/engine/testdata/golden/TestParitySpecValidate/arg=empty.golden +0 -1
  728. package/engine/testdata/golden/TestParitySpecValidate/arg=flat.golden +0 -2
  729. package/engine/testdata/golden/TestParitySpecValidate/arg=nonexistent.golden +0 -1
  730. package/engine/testdata/golden/TestParitySpecValidate/arg=noscenario.golden +0 -1
  731. package/engine/testdata/golden/TestParitySpecValidate/arg=noshall.golden +0 -1
  732. package/engine/testdata/golden/TestParitySpecValidate/arg=nothen.golden +0 -1
  733. package/engine/testdata/golden/TestParitySpecValidate/arg=valid/spec.md.golden +0 -2
  734. package/engine/testdata/golden/TestParitySpecValidate/arg=valid.golden +0 -2
  735. package/engine/testdata/golden/TestParitySpecValidate.golden +0 -1
  736. package/engine/testdata/golden/TestParityStatusline/full-afk.golden +0 -2
  737. package/engine/testdata/golden/TestParityStatusline/full-hitl.golden +0 -2
  738. package/engine/testdata/golden/TestParityStatusline/no-active.golden +0 -1
  739. package/engine/testdata/golden/TestParityStatusline/nophase.golden +0 -2
  740. package/engine/testdata/golden/TestParityStatusline/red.golden +0 -2
  741. package/engine/testdata/golden/TestParityStuckCheck/bad-cmd.golden +0 -1
  742. package/engine/testdata/golden/TestParityStuckCheck/custom-window-2.golden +0 -2
  743. package/engine/testdata/golden/TestParityStuckCheck/dominant.golden +0 -2
  744. package/engine/testdata/golden/TestParityStuckCheck/few.golden +0 -2
  745. package/engine/testdata/golden/TestParityStuckCheck/ghost.golden +0 -2
  746. package/engine/testdata/golden/TestParityStuckCheck/identical.golden +0 -2
  747. package/engine/testdata/golden/TestParityStuckCheck/no-cmd.golden +0 -1
  748. package/engine/testdata/golden/TestParityStuckCheck/no-slug.golden +0 -1
  749. package/engine/testdata/golden/TestParityStuckCheck/nolog.golden +0 -2
  750. package/engine/testdata/golden/TestParityStuckCheck/pingpong.golden +0 -2
  751. package/engine/testdata/golden/TestParityStuckCheck/varied.golden +0 -2
  752. package/engine/testdata/golden/TestParityStuckCheck/window-04-identical.golden +0 -2
  753. package/engine/testdata/golden/TestParityStuckCheck/window-2.5-varied4.golden +0 -2
  754. package/engine/testdata/golden/TestParityStuckCheck/window-4x-varied4.golden +0 -2
  755. package/engine/testdata/golden/TestParityStuckCheck/window-leadspace-varied4.golden +0 -2
  756. package/engine/testdata/golden/TestParityStuckCheck/window-plus4-identical.golden +0 -2
  757. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent-subagent-type-key.golden +0 -2
  758. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent.golden +0 -2
  759. package/engine/testdata/golden/TestParitySubagentOrient/garbage-input.golden +0 -1
  760. package/engine/testdata/golden/TestParitySubagentOrient/non-devrites-agent.golden +0 -1
  761. package/engine/testdata/golden/TestParityTestIntegrity/assertion-dropped.golden +0 -1
  762. package/engine/testdata/golden/TestParityTestIntegrity/clean.golden +0 -2
  763. package/engine/testdata/golden/TestParityTestIntegrity/deleted.golden +0 -1
  764. package/engine/testdata/golden/TestParityTestIntegrity/ghost-workspace.golden +0 -1
  765. package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +0 -1
  766. package/engine/testdata/golden/TestParityTestIntegrity/skip-added.golden +0 -1
  767. package/engine/testdata/golden/TestParityTickAfk/budget1/state.md.golden +0 -1
  768. package/engine/testdata/golden/TestParityTickAfk/budget1.golden +0 -2
  769. package/engine/testdata/golden/TestParityTickAfk/budget3/state.md.golden +0 -5
  770. package/engine/testdata/golden/TestParityTickAfk/budget3.golden +0 -2
  771. package/engine/testdata/golden/TestParityTickAfk/bulletPipe/state.md.golden +0 -1
  772. package/engine/testdata/golden/TestParityTickAfk/bulletPipe.golden +0 -2
  773. package/engine/testdata/golden/TestParityTickAfk/missingField/state.md.golden +0 -3
  774. package/engine/testdata/golden/TestParityTickAfk/missingField.golden +0 -2
  775. package/engine/testdata/golden/TestParityTickAfk/missingFile.golden +0 -1
  776. package/engine/testdata/golden/TestParityTickAfk/nonNumeric/state.md.golden +0 -1
  777. package/engine/testdata/golden/TestParityTickAfk/nonNumeric.golden +0 -1
  778. package/engine/testdata/golden/TestParityTickAfk/none/state.md.golden +0 -1
  779. package/engine/testdata/golden/TestParityTickAfk/none.golden +0 -2
  780. package/engine/testdata/golden/TestParityWrightScope/agent-required-non-wright.golden +0 -1
  781. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +0 -2
  782. package/engine/testdata/golden/TestParityWrightScope/in-scope-allowed.golden +0 -1
  783. package/engine/testdata/golden/TestParityWrightScope/non-edit-tool.golden +0 -1
  784. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +0 -2
  785. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-observe-silent.golden +0 -1
  786. package/engine/testdata/golden/TestSpecSkeleton/missing-some.golden +0 -2
  787. package/engine/testdata/golden/TestSpecSkeleton/present-all.golden +0 -2
  788. package/engine/testdata/golden/TestStuckLogHashParity.golden +0 -1
  789. package/engine/tests/adr_0006_clock_seam_test.go +0 -36
  790. package/engine/tests/budget_test.go +0 -88
  791. package/engine/tests/canonical_ac_ids_test.go +0 -44
  792. package/engine/tests/concurrency_cli_test.go +0 -131
  793. package/engine/tests/conventions_test.go +0 -106
  794. package/engine/tests/doctor_cli_test.go +0 -223
  795. package/engine/tests/forge_cli_test.go +0 -463
  796. package/engine/tests/hook_test.go +0 -2778
  797. package/engine/tests/hooks_io_test.go +0 -309
  798. package/engine/tests/json_contract_test.go +0 -224
  799. package/engine/tests/lib_parity_test.go +0 -930
  800. package/engine/tests/migrate_cli_test.go +0 -195
  801. package/engine/tests/parity_analyze_test.go +0 -55
  802. package/engine/tests/parity_buildreadiness_test.go +0 -199
  803. package/engine/tests/parity_learnings_test.go +0 -89
  804. package/engine/tests/parity_mutationgate_test.go +0 -36
  805. package/engine/tests/parity_reconcile_test.go +0 -94
  806. package/engine/tests/parity_testintegrity_test.go +0 -65
  807. package/engine/tests/parity_tickafk_test.go +0 -83
  808. package/engine/validatepack.go +0 -192
  809. package/engine/validatepack_test.go +0 -79
  810. package/engine/validatepackskills.go +0 -181
  811. package/engine/validatepackskills_test.go +0 -171
  812. package/pack/.claude/agents/devrites-forge-judge.md +0 -118
  813. package/pack/.claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  814. package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  815. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +0 -45
  816. package/pack/.claude/skills/rite-build/reference/forge.md +0 -210
  817. package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  818. package/pack/.claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  819. package/pack/.claude/skills/rite-ship/reference/ledger.md +0 -56
  820. package/pack/generated/claude/agents/devrites-forge-judge.md +0 -118
  821. package/pack/generated/claude/skills/devrites-lib/reference/model-tiers.md +0 -44
  822. package/pack/generated/claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
  823. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +0 -45
  824. package/pack/generated/claude/skills/rite-build/reference/forge.md +0 -210
  825. package/pack/generated/claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
  826. package/pack/generated/claude/skills/rite-ship/reference/adr-promotion.md +0 -9
  827. package/pack/generated/claude/skills/rite-ship/reference/ledger.md +0 -56
  828. package/pack/generated/codex/agents/devrites-forge-judge.toml +0 -128
  829. package/pack/generated/codex/hooks.json +0 -185
  830. package/pack/generated/codex/skills/devrites-api-interface/agents/openai.yaml +0 -2
  831. package/pack/generated/codex/skills/devrites-audit/agents/openai.yaml +0 -2
  832. package/pack/generated/codex/skills/devrites-browser-proof/agents/openai.yaml +0 -2
  833. package/pack/generated/codex/skills/devrites-debug-recovery/agents/openai.yaml +0 -2
  834. package/pack/generated/codex/skills/devrites-doubt/agents/openai.yaml +0 -2
  835. package/pack/generated/codex/skills/devrites-frontend-craft/agents/openai.yaml +0 -2
  836. package/pack/generated/codex/skills/devrites-interview/agents/openai.yaml +0 -2
  837. package/pack/generated/codex/skills/devrites-lib/reference/model-tiers.md +0 -44
  838. package/pack/generated/codex/skills/devrites-prose-craft/agents/openai.yaml +0 -2
  839. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +0 -71
  840. package/pack/generated/codex/skills/devrites-refresh-indexes/agents/openai.yaml +0 -2
  841. package/pack/generated/codex/skills/devrites-source-driven/agents/openai.yaml +0 -2
  842. package/pack/generated/codex/skills/devrites-ux-shape/agents/openai.yaml +0 -2
  843. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +0 -45
  844. package/pack/generated/codex/skills/rite-build/reference/forge.md +0 -210
  845. package/pack/generated/codex/skills/rite-seal/reference/conventions-ledger.md +0 -60
  846. package/pack/generated/codex/skills/rite-ship/reference/adr-promotion.md +0 -9
  847. package/pack/generated/codex/skills/rite-ship/reference/ledger.md +0 -56
  848. package/scripts/build-binaries.sh +0 -75
  849. package/scripts/build-release-tarball.sh +0 -105
  850. package/scripts/check-authority-drift.py +0 -125
  851. package/scripts/check-cross-refs.py +0 -139
  852. package/scripts/check-generated-skill-budget.mjs +0 -42
  853. package/scripts/check-instruction-size-baseline.mjs +0 -88
  854. package/scripts/check-invocation-integrity.py +0 -115
  855. package/scripts/check-no-global-writes.sh +0 -66
  856. package/scripts/check-no-personal-paths.py +0 -50
  857. package/scripts/check-npm-audit.mjs +0 -110
  858. package/scripts/check-reference-governance.mjs +0 -83
  859. package/scripts/check-reply-contract.sh +0 -64
  860. package/scripts/check-rule-uniqueness.sh +0 -45
  861. package/scripts/devrites-detect.sh +0 -176
  862. package/scripts/grade-feature.sh +0 -204
  863. package/scripts/live-hosts/agent-result.schema.json +0 -231
  864. package/scripts/live-hosts/claude.sh +0 -87
  865. package/scripts/live-hosts/codex.sh +0 -85
  866. package/scripts/live-hosts/common.sh +0 -113
  867. package/scripts/live-hosts/fake-host.py +0 -257
  868. package/scripts/live-hosts/host-transport.py +0 -286
  869. package/scripts/npm-audit-exceptions.json +0 -26
  870. package/scripts/pin.sh +0 -249
  871. package/scripts/release-check.sh +0 -75
  872. package/scripts/run-agent-contract-evals.py +0 -1380
  873. package/scripts/run-behavioral-evals.sh +0 -196
  874. package/scripts/run-evals.sh +0 -132
  875. package/scripts/run-live-behavioral-evals.py +0 -1310
  876. package/scripts/run-outcome-evals.sh +0 -454
  877. package/scripts/run-routing-evals.py +0 -342
  878. package/scripts/run-tests.mjs +0 -232
  879. package/scripts/scan-pack-security.py +0 -209
  880. package/scripts/scan-supply-chain-iocs.py +0 -127
  881. package/scripts/skill-pruning-audit.mjs +0 -121
  882. package/scripts/skills-inventory.mjs +0 -125
  883. package/scripts/supply-chain-iocs.json +0 -11
  884. package/scripts/sync-version.sh +0 -54
  885. package/scripts/validate-agent-composition.py +0 -57
  886. package/scripts/validate-command-parity.py +0 -70
  887. package/scripts/validate-frontmatter.py +0 -218
  888. package/scripts/validate-skill-anatomy.py +0 -139
  889. package/scripts/validate-workflow-security.py +0 -184
  890. package/scripts/validate-workspace-schema.py +0 -800
  891. package/scripts/validate.sh +0 -354
  892. package/scripts/workflow_schema.py +0 -69
  893. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/decisions.md +0 -0
  894. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/plan.md +0 -0
  895. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/spec.md +0 -0
  896. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/tasks.md +0 -0
  897. /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/search-ranking/spec.md +0 -0
@@ -1,9 +1,7 @@
1
1
  package install
2
2
 
3
3
  import (
4
- "archive/tar"
5
4
  "bytes"
6
- "compress/gzip"
7
5
  "context"
8
6
  "crypto/sha256"
9
7
  "encoding/hex"
@@ -16,7 +14,6 @@ import (
16
14
  "os"
17
15
  "os/exec"
18
16
  "path/filepath"
19
- "reflect"
20
17
  "runtime"
21
18
  "slices"
22
19
  "sort"
@@ -26,14 +23,11 @@ import (
26
23
  "github.com/devrites/devrites/internal/devritespaths"
27
24
  "github.com/devrites/devrites/internal/fsutil"
28
25
  "github.com/devrites/devrites/internal/hostpack"
29
- "github.com/devrites/devrites/internal/iohooks"
30
26
  "github.com/devrites/devrites/internal/safepath"
31
27
  "github.com/devrites/devrites/internal/version"
32
28
  )
33
29
 
34
30
  const ManifestName = devritespaths.ManifestName
35
- const defaultRepo = "ViktorsBaikers/DevRites"
36
- const githubBaseURL = "https://github.com"
37
31
 
38
32
  type Mode string
39
33
 
@@ -57,8 +51,6 @@ type Options struct {
57
51
  KeepBinary bool
58
52
  AliasMode string
59
53
  UpdateCheck bool
60
- UpdateTo string
61
- AllowPre bool
62
54
  Stdout io.Writer
63
55
  Stderr io.Writer
64
56
  }
@@ -73,18 +65,18 @@ type stats struct {
73
65
  }
74
66
 
75
67
  type runner struct {
76
- opts Options
77
- target string
78
- payload string
79
- payloadFS fs.FS
80
- source string
81
- manifest []string
82
- records map[string]string
83
- prev map[string]managedRecord
84
- preflight map[string]pathSnapshot
85
- stats stats
86
- releaseBinaryTag string
87
- preparedBinary string
68
+ opts Options
69
+ target string
70
+ payload string
71
+ payloadFS fs.FS
72
+ source string
73
+ manifest []string
74
+ records map[string]string
75
+ prev map[string]managedRecord
76
+ preflight map[string]pathSnapshot
77
+ stats stats
78
+ requiredBinaryTag string
79
+ preparedBinary string
88
80
  }
89
81
 
90
82
  type managedRecord struct {
@@ -162,8 +154,6 @@ func parseArgs(args []string, opts *Options) error {
162
154
  noShortAliases := flags.Bool("no-short-aliases", false, "")
163
155
  shortAliases := flags.String("short-aliases", opts.AliasMode, "")
164
156
  updateCheck := flags.Bool("check", opts.UpdateCheck, "")
165
- allowPre := flags.Bool("pre", opts.AllowPre, "")
166
- updateTo := flags.String("to", opts.UpdateTo, "")
167
157
 
168
158
  if err := flags.Parse(normalized); err != nil {
169
159
  if errors.Is(err, flag.ErrHelp) {
@@ -186,8 +176,6 @@ func parseArgs(args []string, opts *Options) error {
186
176
  opts.WithBinary = !*noBinary
187
177
  opts.KeepBinary = *keepBinary
188
178
  opts.UpdateCheck = *updateCheck
189
- opts.AllowPre = *allowPre
190
- opts.UpdateTo = *updateTo
191
179
  for _, dep := range []struct {
192
180
  set bool
193
181
  name string
@@ -227,7 +215,7 @@ func usage(mode Mode) string {
227
215
  case ModeUninstall:
228
216
  return "usage: devrites-engine uninstall [--target DIR] [--dry-run] [--force] [--keep-binary]\n"
229
217
  case ModeUpdate:
230
- return "usage: devrites-engine update [--target DIR] [--dry-run] [--force] [--check] [--to TAG] [install flags]\n"
218
+ return "usage: devrites-engine update [--target DIR] [--source-dir DIR] [--payload-dir DIR] [--dry-run] [--force] [--check] [install flags]\n"
231
219
  default:
232
220
  return "usage: devrites-engine install [--target DIR] [--dry-run] [--force] [--no-codex] [--no-agents] [--no-skills] [--no-binary] [--short-aliases=all]\n"
233
221
  }
@@ -404,8 +392,8 @@ func (r *runner) install() error {
404
392
  if err := r.mergeMarkerFile(hostpack.CodexAgentsMerge); err != nil {
405
393
  return fmt.Errorf("merge %s: %w", hostpack.CodexAgentsMerge.TargetRel, err)
406
394
  }
407
- if err := r.mergeJSONHooks(hostpack.CodexHooksMerge); err != nil {
408
- return fmt.Errorf("merge %s: %w", hostpack.CodexHooksMerge.TargetRel, err)
395
+ if err := r.mergeCodexConfig(); err != nil {
396
+ return fmt.Errorf("merge %s: %w", hostpack.CodexConfigMerge.TargetRel, err)
409
397
  }
410
398
  }
411
399
  if r.opts.WithSkills {
@@ -435,6 +423,13 @@ func (r *runner) install() error {
435
423
  fmt.Fprintln(r.opts.Stdout, "DevRites installed")
436
424
  }
437
425
  fmt.Fprintf(r.opts.Stdout, " installed: %d overwritten: %d skipped(conflict): %d pruned: %d\n", r.stats.installed, r.stats.overwrote, r.stats.skipped, r.stats.pruned)
426
+ if !r.opts.DryRun && r.opts.WithSkills {
427
+ if r.opts.WithCodex {
428
+ fmt.Fprintln(r.opts.Stdout, "Next: reopen the project, then run /rite (Claude) or $rite (Codex).")
429
+ } else {
430
+ fmt.Fprintln(r.opts.Stdout, "Next: reopen the project, then run /rite.")
431
+ }
432
+ }
438
433
  return nil
439
434
  }
440
435
 
@@ -568,7 +563,7 @@ func (r *runner) desiredInstallPaths() (map[string]bool, error) {
568
563
  }
569
564
  if r.opts.WithSkills && r.opts.WithCodex {
570
565
  out[hostpack.CodexAgentsMerge.MarkerRel] = true
571
- out[hostpack.CodexHooksMerge.MarkerRel] = true
566
+ out[hostpack.CodexConfigMerge.MarkerRel] = true
572
567
  }
573
568
  if r.opts.WithSkills {
574
569
  out[hostpack.ClaudeSettingsMerge.MarkerRel] = true
@@ -581,7 +576,7 @@ func (r *runner) installMergeTargets() []string {
581
576
  if r.opts.WithSkills {
582
577
  out = append(out, hostpack.ClaudeSettingsMerge.TargetRel)
583
578
  if r.opts.WithCodex {
584
- out = append(out, hostpack.CodexAgentsMerge.TargetRel, hostpack.CodexHooksMerge.TargetRel)
579
+ out = append(out, hostpack.CodexAgentsMerge.TargetRel, hostpack.CodexConfigMerge.TargetRel)
585
580
  }
586
581
  }
587
582
  return out
@@ -738,6 +733,20 @@ func (r *runner) installMarker(rel, text string) error {
738
733
  return r.installData([]byte(text+"\n"), rel)
739
734
  }
740
735
 
736
+ func (r *runner) installClaudeSettingsMarker(ownsDefaultMode bool) error {
737
+ ownership := "preexisting"
738
+ if ownsDefaultMode {
739
+ ownership = "added"
740
+ }
741
+ text := hostpack.ClaudeSettingsMerge.MarkerText + "\ndefault-mode=" + ownership
742
+ return r.installMarker(hostpack.ClaudeSettingsMerge.MarkerRel, text)
743
+ }
744
+
745
+ func (r *runner) claudeDefaultModeOwned() bool {
746
+ data, err := os.ReadFile(filepath.Join(r.target, filepath.FromSlash(hostpack.ClaudeSettingsMerge.MarkerRel)))
747
+ return err == nil && strings.Contains(string(data), "\ndefault-mode=added\n")
748
+ }
749
+
741
750
  func (r *runner) mergeMarkerFile(merge hostpack.MarkerMerge) error {
742
751
  block, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
743
752
  if err != nil {
@@ -775,87 +784,81 @@ func (r *runner) mergeMarkerFile(merge hostpack.MarkerMerge) error {
775
784
  return r.installMarker(merge.MarkerRel, merge.MarkerText)
776
785
  }
777
786
 
778
- func (r *runner) mergeJSONHooks(merge hostpack.JSONMerge) error {
779
- dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
780
- devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
787
+ func (r *runner) mergeCodexConfig() error {
788
+ merge := hostpack.CodexConfigMerge
789
+ block, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
781
790
  if err != nil {
782
- return fmt.Errorf("load hooks payload: %w", err)
791
+ return fmt.Errorf("read payload %s: %w", merge.PayloadRel, err)
783
792
  }
793
+ dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
784
794
  if r.opts.DryRun {
785
- fmt.Fprintf(r.opts.Stdout, " [merge] %s\n", merge.DryRunText)
795
+ fmt.Fprintf(r.opts.Stdout, " [merge] %s (prepend DevRites permission block)\n", merge.TargetRel)
786
796
  return r.installMarker(merge.MarkerRel, merge.MarkerText)
787
797
  }
788
798
  if err := r.recheckPath(merge.TargetRel); err != nil {
789
799
  return err
790
800
  }
791
- next := devrites
792
- if current, err := readJSON(dest); err == nil {
793
- if currentStatus, exists := current["statusLine"]; exists && !isDevritesHookCommand(currentStatus) {
794
- if _, wanted := devrites["statusLine"]; wanted {
795
- fmt.Fprintln(r.opts.Stderr, "warning: preserved existing Claude statusLine; DevRites statusline was not installed.")
796
- }
801
+ var current []byte
802
+ if data, readErr := os.ReadFile(dest); readErr == nil {
803
+ current = stripMarkerBlock(data, merge.Begin, merge.End)
804
+ current = stripMarkerBlock(current, "# BEGIN DEVRITES CODEX MCP", "# END DEVRITES CODEX MCP")
805
+ current = stripMarkerBlock(current, "### BEGIN DEVRITES CODEX MCP", "### END DEVRITES CODEX MCP")
806
+ if hasTopLevelTOMLKey(current, "default_permissions") {
807
+ return fmt.Errorf("%s already sets top-level default_permissions; remove that project override before installing the DevRites read-only-root profile", merge.TargetRel)
797
808
  }
798
- next = mergeHooks(stripDevritesHooks(current), devrites)
809
+ } else if !errors.Is(readErr, fs.ErrNotExist) {
810
+ return fmt.Errorf("cannot read %s: %w", merge.TargetRel, readErr)
799
811
  }
800
- data, err := json.MarshalIndent(next, "", " ")
801
- if err != nil {
802
- return fmt.Errorf("encode merged hooks: %w", err)
812
+ next := append([]byte(nil), block...)
813
+ if len(next) == 0 || next[len(next)-1] != '\n' {
814
+ next = append(next, '\n')
803
815
  }
804
- data = append(data, '\n')
805
- if err := fsutil.WriteFileAtomic(dest, data, 0o644); err != nil {
816
+ if len(bytes.TrimSpace(current)) > 0 {
817
+ next = append(next, '\n')
818
+ next = append(next, current...)
819
+ }
820
+ if err := fsutil.WriteFileAtomic(dest, next, 0o644); err != nil {
806
821
  return fmt.Errorf("cannot write %s: %w", merge.TargetRel, err)
807
822
  }
808
823
  return r.installMarker(merge.MarkerRel, merge.MarkerText)
809
824
  }
810
825
 
811
- func (r *runner) seedClaudeSettings() error {
812
- merge := hostpack.ClaudeSettingsMerge
813
- rel := merge.TargetRel
814
- dest := filepath.Join(r.target, rel)
815
- if exists(dest) {
816
- current, err := readJSON(dest)
817
- if err != nil {
818
- return fmt.Errorf("load existing Claude settings: %w", err)
819
- }
820
- devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
821
- if err != nil {
822
- return fmt.Errorf("load Claude settings payload: %w", err)
823
- }
824
- _, managed := r.prev[merge.MarkerRel]
825
- if reflect.DeepEqual(current, devrites) {
826
- if managed {
827
- return r.installMarker(merge.MarkerRel, merge.MarkerText)
828
- }
829
- return nil
830
- }
831
- if containsDevritesHook(current) && len(stripDevritesHooks(current)) == 0 && !managed {
832
- if r.opts.DryRun {
833
- fmt.Fprintln(r.opts.Stdout, " [seed] .claude/settings.json (refresh DevRites hooks)")
834
- return nil
835
- }
836
- data, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
837
- if err != nil {
838
- return fmt.Errorf("read payload claude/settings.json: %w", err)
839
- }
840
- if err := r.recheckPath(rel); err != nil {
841
- return err
842
- }
843
- return fsutil.WriteFileAtomic(dest, data, 0o644)
844
- }
845
- return r.mergeJSONHooks(merge)
826
+ func (r *runner) mergeClaudeSettings(merge hostpack.JSONMerge) error {
827
+ dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
828
+ devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
829
+ if err != nil {
830
+ return fmt.Errorf("load Claude settings payload: %w", err)
846
831
  }
847
- if r.opts.DryRun {
848
- fmt.Fprintln(r.opts.Stdout, " [seed] .claude/settings.json (DevRites hooks - preserved on uninstall/update)")
849
- return nil
832
+ current := map[string]any{}
833
+ if data, readErr := readJSON(dest); readErr == nil {
834
+ current = data
835
+ } else if !errors.Is(readErr, fs.ErrNotExist) {
836
+ return fmt.Errorf("load existing Claude settings: %w", readErr)
850
837
  }
851
- data, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
838
+ next, ownsDefaultMode, err := mergeClaudeSettingsConfig(current, devrites, r.claudeDefaultModeOwned())
852
839
  if err != nil {
853
- return fmt.Errorf("read payload claude/settings.json: %w", err)
840
+ return err
841
+ }
842
+ if r.opts.DryRun {
843
+ fmt.Fprintf(r.opts.Stdout, " [merge] %s\n", merge.DryRunText)
844
+ return r.installClaudeSettingsMarker(ownsDefaultMode)
854
845
  }
855
- if err := r.recheckPath(rel); err != nil {
846
+ if err := r.recheckPath(merge.TargetRel); err != nil {
856
847
  return err
857
848
  }
858
- return fsutil.WriteFileAtomic(dest, data, 0o644)
849
+ data, err := json.MarshalIndent(next, "", " ")
850
+ if err != nil {
851
+ return fmt.Errorf("encode merged Claude settings: %w", err)
852
+ }
853
+ data = append(data, '\n')
854
+ if err := fsutil.WriteFileAtomic(dest, data, 0o644); err != nil {
855
+ return fmt.Errorf("cannot write %s: %w", merge.TargetRel, err)
856
+ }
857
+ return r.installClaudeSettingsMarker(ownsDefaultMode)
858
+ }
859
+
860
+ func (r *runner) seedClaudeSettings() error {
861
+ return r.mergeClaudeSettings(hostpack.ClaudeSettingsMerge)
859
862
  }
860
863
 
861
864
  func (r *runner) seedDevrites() error {
@@ -922,9 +925,8 @@ func (r *runner) pruneDropped() error {
922
925
  if err := r.recheckPath(merge.TargetRel); err != nil {
923
926
  return err
924
927
  }
925
- if merge.TargetRel == hostpack.CodexHooksMerge.TargetRel || merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
926
- preserveEmpty := merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel
927
- _ = r.stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), preserveEmpty)
928
+ if merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
929
+ _ = r.stripClaudeSettings(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), true)
928
930
  } else {
929
931
  _ = stripMarkerPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), merge.Begin, merge.End)
930
932
  }
@@ -1021,10 +1023,9 @@ func (r *runner) uninstall() error {
1021
1023
  if err := r.recheckPath(merge.TargetRel); err != nil {
1022
1024
  return err
1023
1025
  }
1024
- if merge.TargetRel == hostpack.CodexHooksMerge.TargetRel || merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
1025
- preserveEmpty := merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel
1026
- if err := r.stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), preserveEmpty); err != nil {
1027
- return fmt.Errorf("strip hooks from %s: %w", merge.TargetRel, err)
1026
+ if merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
1027
+ if err := r.stripClaudeSettings(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), true); err != nil {
1028
+ return fmt.Errorf("strip DevRites settings from %s: %w", merge.TargetRel, err)
1028
1029
  }
1029
1030
  continue
1030
1031
  }
@@ -1091,21 +1092,21 @@ func (r *runner) uninstall() error {
1091
1092
  return nil
1092
1093
  }
1093
1094
 
1094
- func (r *runner) stripHooksPath(path string, preserveEmpty bool) error {
1095
+ func (r *runner) stripClaudeSettings(path string, preserveEmpty bool) error {
1095
1096
  current, err := readJSON(path)
1096
1097
  if err != nil {
1097
1098
  if errors.Is(err, fs.ErrNotExist) {
1098
1099
  return nil
1099
1100
  }
1100
- return fmt.Errorf("load hooks config: %w", err)
1101
+ return fmt.Errorf("load Claude settings: %w", err)
1101
1102
  }
1102
- next := stripDevritesHooks(current)
1103
+ next := stripDevritesSettings(current, r.claudeDefaultModeOwned())
1103
1104
  if len(next) == 0 && !preserveEmpty {
1104
1105
  return os.Remove(path)
1105
1106
  }
1106
1107
  data, err := json.MarshalIndent(next, "", " ")
1107
1108
  if err != nil {
1108
- return fmt.Errorf("encode hooks config: %w", err)
1109
+ return fmt.Errorf("encode Claude settings: %w", err)
1109
1110
  }
1110
1111
  data = append(data, '\n')
1111
1112
  return fsutil.WriteFileAtomic(path, data, 0o644)
@@ -1124,36 +1125,42 @@ func runUpdate(opts Options) error {
1124
1125
  if installed == "" {
1125
1126
  installed = "unknown"
1126
1127
  }
1127
- latestTag, err := resolveUpdateTag(opts)
1128
+ if opts.SourceDir == "" {
1129
+ return fmt.Errorf("update requires --source-dir with the locally acquired source")
1130
+ }
1131
+ source, err := filepath.Abs(opts.SourceDir)
1128
1132
  if err != nil {
1129
- return fmt.Errorf("resolve update tag: %w", err)
1133
+ return fmt.Errorf("resolve source %s: %w", opts.SourceDir, err)
1130
1134
  }
1131
- latest := strings.TrimPrefix(latestTag, "v")
1135
+ candidate := installedVersion(source)
1136
+ if candidate == "" || candidate == "unknown" {
1137
+ return fmt.Errorf("derive update version from local source %s: package.json has no version", source)
1138
+ }
1139
+ candidate = strings.TrimPrefix(candidate, "v")
1132
1140
  fmt.Fprintln(opts.Stdout, "DevRites update")
1133
1141
  fmt.Fprintf(opts.Stdout, " project: %s\n", target)
1134
1142
  fmt.Fprintf(opts.Stdout, " installed: %s\n", installed)
1135
- fmt.Fprintf(opts.Stdout, " latest: %s\n", latest)
1143
+ fmt.Fprintf(opts.Stdout, " candidate: %s\n", candidate)
1136
1144
  if opts.UpdateCheck {
1137
- if installed == latest || strings.TrimPrefix(installed, "v") == latest {
1145
+ if installed == candidate || strings.TrimPrefix(installed, "v") == candidate {
1138
1146
  fmt.Fprintln(opts.Stdout, "up to date.")
1139
1147
  return nil
1140
1148
  }
1141
- return fmt.Errorf("update available: %s -> %s", installed, latest)
1149
+ return fmt.Errorf("update available: %s -> %s", installed, candidate)
1142
1150
  }
1143
- if !opts.Force && (installed == latest || strings.TrimPrefix(installed, "v") == latest) {
1151
+ if !opts.Force && (installed == candidate || strings.TrimPrefix(installed, "v") == candidate) {
1144
1152
  fmt.Fprintln(opts.Stdout, "already up to date (use --force to reinstall).")
1145
1153
  return nil
1146
1154
  }
1147
- source, cleanup, err := acquireUpdateBundle(latestTag, opts.Stdout)
1155
+ if opts.PayloadDir == "" {
1156
+ return fmt.Errorf("update requires --payload-dir with the locally prepared host payload")
1157
+ }
1158
+ payload, err := filepath.Abs(opts.PayloadDir)
1148
1159
  if err != nil {
1149
- return fmt.Errorf("acquire update %s: %w", latestTag, err)
1160
+ return fmt.Errorf("resolve payload %s: %w", opts.PayloadDir, err)
1150
1161
  }
1151
- defer cleanup()
1152
- payload := filepath.Join(source, "pack", "generated")
1153
- if hostpack.ValidatePayload(os.DirFS(payload), true) != nil {
1154
- if err := buildHostArtifacts(source, payload); err != nil {
1155
- return fmt.Errorf("prepare update payload: %w", err)
1156
- }
1162
+ if err := hostpack.ValidatePayload(os.DirFS(payload), true); err != nil {
1163
+ return fmt.Errorf("local update payload under %s is invalid: %w", payload, err)
1157
1164
  }
1158
1165
  installFlags := manifestHeader(mf, "devrites-flags")
1159
1166
  next := DefaultOptions(ModeInstall)
@@ -1175,9 +1182,9 @@ func runUpdate(opts Options) error {
1175
1182
  if err != nil {
1176
1183
  return fmt.Errorf("prepare installer: %w", err)
1177
1184
  }
1178
- r.releaseBinaryTag = latestTag
1185
+ r.requiredBinaryTag = "v" + candidate
1179
1186
  if next.WithBinary && os.Getenv("DEVRITES_NO_BINARY") != "1" && !next.DryRun {
1180
- staged, cleanup, err := r.acquireBinary(latestTag, githubBaseURL)
1187
+ staged, cleanup, err := r.acquireBinary(r.requiredBinaryTag)
1181
1188
  if err != nil {
1182
1189
  return fmt.Errorf("prepare engine binary: %w", err)
1183
1190
  }
@@ -1187,249 +1194,6 @@ func runUpdate(opts Options) error {
1187
1194
  return r.install()
1188
1195
  }
1189
1196
 
1190
- func resolveUpdateTag(opts Options) (string, error) {
1191
- if opts.UpdateTo != "" {
1192
- return normalizeTag(opts.UpdateTo), nil
1193
- }
1194
- if os.Getenv("DEVRITES_UPDATE_BUNDLE") != "" && opts.SourceDir != "" {
1195
- if v := installedVersion(opts.SourceDir); v != "" && v != "unknown" {
1196
- return normalizeTag(v), nil
1197
- }
1198
- }
1199
- repo := os.Getenv("DEVRITES_REPO")
1200
- if repo == "" {
1201
- repo = defaultRepo
1202
- }
1203
- endpoint := "latest"
1204
- if opts.AllowPre {
1205
- endpoint = ""
1206
- }
1207
- url := fmt.Sprintf("https://api.github.com/repos/%s/releases/%s", repo, endpoint)
1208
- if opts.AllowPre {
1209
- var releases []struct {
1210
- TagName string `json:"tag_name"`
1211
- }
1212
- if err := iohooks.FetchJSON(url, &releases); err != nil {
1213
- return "", fmt.Errorf("resolve latest release: %w", err)
1214
- }
1215
- for _, rel := range releases {
1216
- if rel.TagName != "" {
1217
- return normalizeTag(rel.TagName), nil
1218
- }
1219
- }
1220
- return "", fmt.Errorf("resolve latest release: no releases found")
1221
- }
1222
- var release struct {
1223
- TagName string `json:"tag_name"`
1224
- }
1225
- if err := iohooks.FetchJSON(url, &release); err != nil {
1226
- return "", fmt.Errorf("resolve latest release: %w", err)
1227
- }
1228
- if release.TagName == "" {
1229
- return "", fmt.Errorf("resolve latest release: empty tag")
1230
- }
1231
- return normalizeTag(release.TagName), nil
1232
- }
1233
-
1234
- func acquireUpdateBundle(tag string, stdout io.Writer) (string, func(), error) {
1235
- tmp, err := os.MkdirTemp("", "devrites-update-*")
1236
- if err != nil {
1237
- return "", func() {}, fmt.Errorf("create temp dir: %w", err)
1238
- }
1239
- cleanup := func() { _ = os.RemoveAll(tmp) }
1240
- artifact := fmt.Sprintf("devrites-%s.tar.gz", tag)
1241
- tarball := filepath.Join(tmp, artifact)
1242
- if bundle := os.Getenv("DEVRITES_UPDATE_BUNDLE"); bundle != "" {
1243
- if !exists(bundle) {
1244
- cleanup()
1245
- return "", func() {}, fmt.Errorf("DEVRITES_UPDATE_BUNDLE not readable: %s", bundle)
1246
- }
1247
- fmt.Fprintf(stdout, " bundle: %s\n", bundle)
1248
- return extractUpdateBundle(bundle, tmp, cleanup)
1249
- }
1250
-
1251
- repo := os.Getenv("DEVRITES_REPO")
1252
- if repo == "" {
1253
- repo = defaultRepo
1254
- }
1255
- releaseURL := fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", repo, tag, artifact)
1256
- if err := iohooks.DownloadFile(releaseURL, tarball); err == nil {
1257
- sumPath := tarball + ".sha256"
1258
- if err := iohooks.DownloadFile(releaseURL+".sha256", sumPath); err != nil {
1259
- cleanup()
1260
- return "", func() {}, fmt.Errorf("could not verify release artifact %s: missing checksum: %w", artifact, err)
1261
- }
1262
- if err := verifySHA256(tarball, sumPath); err != nil {
1263
- cleanup()
1264
- return "", func() {}, fmt.Errorf("could not verify release artifact %s: %w", artifact, err)
1265
- }
1266
- fmt.Fprintf(stdout, " bundle: %s\n", releaseURL)
1267
- return extractUpdateBundle(tarball, tmp, cleanup)
1268
- }
1269
-
1270
- sourceURL := fmt.Sprintf("https://github.com/%s/archive/refs/tags/%s.tar.gz", repo, tag)
1271
- sourceTarball := filepath.Join(tmp, "src-"+tag+".tar.gz")
1272
- if err := iohooks.DownloadFile(sourceURL, sourceTarball); err != nil {
1273
- cleanup()
1274
- return "", func() {}, fmt.Errorf("download update bundle: %w", err)
1275
- }
1276
- fmt.Fprintf(stdout, " bundle: %s\n", sourceURL)
1277
- return extractUpdateBundle(sourceTarball, tmp, cleanup)
1278
- }
1279
-
1280
- func verifySHA256(path, sumPath string) error {
1281
- data, err := os.ReadFile(sumPath)
1282
- if err != nil {
1283
- return fmt.Errorf("read checksum %s: %w", sumPath, err)
1284
- }
1285
- want := strings.Fields(string(data))
1286
- if len(want) == 0 {
1287
- return fmt.Errorf("empty checksum file")
1288
- }
1289
- body, err := os.ReadFile(path)
1290
- if err != nil {
1291
- return fmt.Errorf("read artifact %s: %w", path, err)
1292
- }
1293
- got := fmt.Sprintf("%x", sha256.Sum256(body))
1294
- if got != want[0] {
1295
- return fmt.Errorf("checksum mismatch: got %s want %s", got, want[0])
1296
- }
1297
- return nil
1298
- }
1299
-
1300
- func extractUpdateBundle(tarball, tmp string, cleanup func()) (string, func(), error) {
1301
- if err := extractTarGz(tarball, tmp); err != nil {
1302
- cleanup()
1303
- return "", func() {}, fmt.Errorf("extract update bundle: %w", err)
1304
- }
1305
- entries, err := os.ReadDir(tmp)
1306
- if err != nil {
1307
- cleanup()
1308
- return "", func() {}, fmt.Errorf("list extracted bundle: %w", err)
1309
- }
1310
- for _, entry := range entries {
1311
- if !entry.IsDir() {
1312
- continue
1313
- }
1314
- dir := filepath.Join(tmp, entry.Name())
1315
- if exists(filepath.Join(dir, "install.sh")) {
1316
- return dir, cleanup, nil
1317
- }
1318
- }
1319
- cleanup()
1320
- return "", func() {}, fmt.Errorf("extracted update bundle has no install.sh")
1321
- }
1322
-
1323
- const (
1324
- maxUpdateEntries = 4096
1325
- maxUpdateEntryBytes = int64(1) << 30
1326
- maxUpdateTotalBytes = int64(2) << 30
1327
- )
1328
-
1329
- func extractTarGz(tarball, dest string) error {
1330
- f, err := os.Open(tarball)
1331
- if err != nil {
1332
- return fmt.Errorf("open update bundle: %w", err)
1333
- }
1334
- defer f.Close()
1335
- gz, err := gzip.NewReader(f)
1336
- if err != nil {
1337
- return fmt.Errorf("decompress %s: %w", tarball, err)
1338
- }
1339
- defer gz.Close()
1340
- tr := tar.NewReader(gz)
1341
- root, err := filepath.Abs(dest)
1342
- if err != nil {
1343
- return fmt.Errorf("resolve %s: %w", dest, err)
1344
- }
1345
- entryCount := 0
1346
- var totalBytes int64
1347
- for {
1348
- hdr, err := tr.Next()
1349
- if errors.Is(err, io.EOF) {
1350
- return nil
1351
- }
1352
- if err != nil {
1353
- return fmt.Errorf("read tar entry: %w", err)
1354
- }
1355
- entryCount++
1356
- if entryCount > maxUpdateEntries {
1357
- return fmt.Errorf("update bundle has too many entries (maximum %d)", maxUpdateEntries)
1358
- }
1359
- if hdr.Size < 0 {
1360
- return fmt.Errorf("entry %s in update bundle has a negative size", hdr.Name)
1361
- }
1362
- if hdr.Size > maxUpdateEntryBytes {
1363
- return fmt.Errorf("entry %s in update bundle exceeds %d bytes", hdr.Name, maxUpdateEntryBytes)
1364
- }
1365
- if hdr.Size > maxUpdateTotalBytes-totalBytes {
1366
- return fmt.Errorf("update bundle exceeds %d total bytes", maxUpdateTotalBytes)
1367
- }
1368
- totalBytes += hdr.Size
1369
- name := filepath.Clean(hdr.Name)
1370
- if name == "." || filepath.IsAbs(name) || strings.HasPrefix(name, ".."+string(os.PathSeparator)) || name == ".." {
1371
- return fmt.Errorf("unsafe path in update bundle: %s", hdr.Name)
1372
- }
1373
- target := filepath.Join(root, name)
1374
- if !strings.HasPrefix(target, root+string(os.PathSeparator)) && target != root {
1375
- return fmt.Errorf("unsafe path in update bundle: %s", hdr.Name)
1376
- }
1377
- switch hdr.Typeflag {
1378
- case tar.TypeDir:
1379
- if err := os.MkdirAll(target, fs.FileMode(hdr.Mode&0o777)); err != nil {
1380
- return fmt.Errorf("create dir %s: %w", name, err)
1381
- }
1382
- case tar.TypeReg:
1383
- if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
1384
- return fmt.Errorf("create dir for %s: %w", name, err)
1385
- }
1386
- out, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fs.FileMode(hdr.Mode&0o777))
1387
- if err != nil {
1388
- return fmt.Errorf("create %s: %w", name, err)
1389
- }
1390
- // Keep the streaming cap even after validating the header so malformed
1391
- // archives cannot make the extractor trust inconsistent metadata.
1392
- written, copyErr := io.Copy(out, io.LimitReader(tr, maxUpdateEntryBytes+1))
1393
- closeErr := out.Close()
1394
- if copyErr != nil {
1395
- return fmt.Errorf("extract %s: %w", name, copyErr)
1396
- }
1397
- if written > maxUpdateEntryBytes {
1398
- return fmt.Errorf("entry %s in update bundle exceeds %d bytes", hdr.Name, maxUpdateEntryBytes)
1399
- }
1400
- if closeErr != nil {
1401
- return fmt.Errorf("close %s: %w", name, closeErr)
1402
- }
1403
- default:
1404
- continue
1405
- }
1406
- }
1407
- }
1408
-
1409
- func buildHostArtifacts(source, payload string) error {
1410
- script := filepath.Join(source, "scripts", "build-host-artifacts.sh")
1411
- if !exists(script) {
1412
- return fmt.Errorf("generated install payload missing at %s and no builder found at %s", payload, script)
1413
- }
1414
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
1415
- defer cancel()
1416
- cmd := exec.CommandContext(ctx, "bash", script)
1417
- cmd.Dir = source
1418
- cmd.Env = append(os.Environ(), "DEVRITES_HOST_ARTIFACT_DIR="+payload)
1419
- if out, err := cmd.CombinedOutput(); err != nil {
1420
- return fmt.Errorf("build generated install payload: %w: %s", err, strings.TrimSpace(string(out)))
1421
- }
1422
- return nil
1423
- }
1424
-
1425
- func normalizeTag(tag string) string {
1426
- tag = strings.TrimSpace(tag)
1427
- if tag == "" || strings.HasPrefix(tag, "v") {
1428
- return tag
1429
- }
1430
- return "v" + tag
1431
- }
1432
-
1433
1197
  func stripMarkerPath(path, begin, end string) error {
1434
1198
  data, err := os.ReadFile(path)
1435
1199
  if err != nil {
@@ -1438,6 +1202,14 @@ func stripMarkerPath(path, begin, end string) error {
1438
1202
  }
1439
1203
  return fmt.Errorf("read %s: %w", path, err)
1440
1204
  }
1205
+ next := stripMarkerBlock(data, begin, end)
1206
+ if strings.TrimSpace(string(next)) == "" {
1207
+ return os.Remove(path)
1208
+ }
1209
+ return fsutil.WriteFileAtomic(path, next, 0o644)
1210
+ }
1211
+
1212
+ func stripMarkerBlock(data []byte, begin, end string) []byte {
1441
1213
  var out strings.Builder
1442
1214
  inBlock := false
1443
1215
  for _, line := range strings.SplitAfter(string(data), "\n") {
@@ -1455,10 +1227,24 @@ func stripMarkerPath(path, begin, end string) error {
1455
1227
  out.WriteString(line)
1456
1228
  }
1457
1229
  }
1458
- if strings.TrimSpace(out.String()) == "" {
1459
- return os.Remove(path)
1230
+ return []byte(out.String())
1231
+ }
1232
+
1233
+ func hasTopLevelTOMLKey(data []byte, key string) bool {
1234
+ for _, line := range strings.Split(string(data), "\n") {
1235
+ line = strings.TrimSpace(line)
1236
+ if line == "" || strings.HasPrefix(line, "#") {
1237
+ continue
1238
+ }
1239
+ if strings.HasPrefix(line, "[") {
1240
+ return false
1241
+ }
1242
+ name, _, ok := strings.Cut(line, "=")
1243
+ if ok && strings.TrimSpace(name) == key {
1244
+ return true
1245
+ }
1460
1246
  }
1461
- return fsutil.WriteFileAtomic(path, []byte(out.String()), 0o644)
1247
+ return false
1462
1248
  }
1463
1249
 
1464
1250
  func readJSON(path string) (map[string]any, error) {
@@ -1497,27 +1283,6 @@ func isDevritesHookCommand(v any) bool {
1497
1283
  (strings.Contains(command, "printf ") && strings.Contains(command, "DevRites:"))
1498
1284
  }
1499
1285
 
1500
- func containsDevritesHook(v any) bool {
1501
- switch value := v.(type) {
1502
- case map[string]any:
1503
- if isDevritesHookCommand(value) {
1504
- return true
1505
- }
1506
- for _, child := range value {
1507
- if containsDevritesHook(child) {
1508
- return true
1509
- }
1510
- }
1511
- case []any:
1512
- for _, child := range value {
1513
- if containsDevritesHook(child) {
1514
- return true
1515
- }
1516
- }
1517
- }
1518
- return false
1519
- }
1520
-
1521
1286
  func isDevritesHooksComment(comment string) bool {
1522
1287
  return comment == "DevRites hooks" ||
1523
1288
  strings.HasPrefix(comment, "DevRites hooks: every event invokes the global `devrites-engine` engine binary") ||
@@ -1527,6 +1292,11 @@ func isDevritesHooksComment(comment string) bool {
1527
1292
  strings.HasPrefix(comment, "DevRites hooks for Codex. Project hooks load only after")
1528
1293
  }
1529
1294
 
1295
+ func isDevritesSettingsComment(comment string) bool {
1296
+ return isDevritesHooksComment(comment) ||
1297
+ strings.HasPrefix(comment, "DevRites keeps the root orchestration context source-read-only.")
1298
+ }
1299
+
1530
1300
  func stripDevritesHooks(config map[string]any) map[string]any {
1531
1301
  next := map[string]any{}
1532
1302
  for k, v := range config {
@@ -1591,29 +1361,124 @@ func stripDevritesHooks(config map[string]any) map[string]any {
1591
1361
  return next
1592
1362
  }
1593
1363
 
1594
- func mergeHooks(existing, devrites map[string]any) map[string]any {
1595
- hooks, _ := existing["hooks"].(map[string]any)
1596
- if hooks == nil {
1597
- hooks = map[string]any{}
1364
+ func stripDevritesSettings(config map[string]any, removeOwnedDefaultMode bool) map[string]any {
1365
+ next := stripDevritesHooks(config)
1366
+ if comment, ok := next["$comment"].(string); ok && isDevritesSettingsComment(comment) {
1367
+ delete(next, "$comment")
1368
+ }
1369
+
1370
+ rawPermissions, exists := next["permissions"]
1371
+ if !exists {
1372
+ return next
1373
+ }
1374
+ permissions, ok := rawPermissions.(map[string]any)
1375
+ if !ok {
1376
+ return next
1377
+ }
1378
+ clean := make(map[string]any, len(permissions))
1379
+ for key, value := range permissions {
1380
+ switch key {
1381
+ case "allow":
1382
+ rules, ok := value.([]any)
1383
+ if !ok {
1384
+ clean[key] = value
1385
+ continue
1386
+ }
1387
+ kept := make([]any, 0, len(rules))
1388
+ for _, rule := range rules {
1389
+ if !isDevritesPermissionRule(rule) {
1390
+ kept = append(kept, rule)
1391
+ }
1392
+ }
1393
+ if len(kept) > 0 {
1394
+ clean[key] = kept
1395
+ }
1396
+ case "defaultMode":
1397
+ if removeOwnedDefaultMode && value == "plan" {
1398
+ continue
1399
+ }
1400
+ clean[key] = value
1401
+ default:
1402
+ clean[key] = value
1403
+ }
1404
+ }
1405
+ if len(clean) == 0 {
1406
+ delete(next, "permissions")
1407
+ } else {
1408
+ next["permissions"] = clean
1409
+ }
1410
+ return next
1411
+ }
1412
+
1413
+ func mergeClaudeSettingsConfig(current, desired map[string]any, defaultModeOwned bool) (map[string]any, bool, error) {
1414
+ next := stripDevritesSettings(current, false)
1415
+ permissions := map[string]any{}
1416
+ if raw, exists := next["permissions"]; exists {
1417
+ var ok bool
1418
+ permissions, ok = raw.(map[string]any)
1419
+ if !ok {
1420
+ return nil, false, fmt.Errorf("existing Claude permissions must be a JSON object")
1421
+ }
1422
+ }
1423
+
1424
+ switch mode, exists := permissions["defaultMode"]; {
1425
+ case !exists:
1426
+ defaultModeOwned = true
1427
+ case mode == "plan":
1428
+ case mode != "plan":
1429
+ return nil, false, fmt.Errorf("existing Claude permissions.defaultMode is %q; DevRites requires plan mode for the read-only root orchestrator", mode)
1430
+ }
1431
+ permissions["defaultMode"] = "plan"
1432
+
1433
+ desiredPermissions, ok := desired["permissions"].(map[string]any)
1434
+ if !ok || desiredPermissions["defaultMode"] != "plan" {
1435
+ return nil, false, fmt.Errorf("DevRites Claude settings payload must declare permissions.defaultMode=plan")
1598
1436
  }
1599
- devHooks, _ := devrites["hooks"].(map[string]any)
1600
- for event, entries := range devHooks {
1601
- arr, _ := hooks[event].([]any)
1602
- devArr, _ := entries.([]any)
1603
- hooks[event] = append(arr, devArr...)
1437
+ desiredAllow, ok := desiredPermissions["allow"].([]any)
1438
+ if !ok {
1439
+ return nil, false, fmt.Errorf("DevRites Claude settings payload permissions.allow must be an array")
1604
1440
  }
1605
- existing["hooks"] = hooks
1606
- if statusLine, ok := devrites["statusLine"]; ok {
1607
- if current, exists := existing["statusLine"]; !exists || isDevritesHookCommand(current) {
1608
- existing["statusLine"] = statusLine
1441
+ var allow []any
1442
+ if existingAllow, exists := permissions["allow"]; exists {
1443
+ allow, ok = existingAllow.([]any)
1444
+ if !ok {
1445
+ return nil, false, fmt.Errorf("existing Claude permissions.allow must be an array")
1609
1446
  }
1610
1447
  }
1611
- return existing
1448
+ seen := map[string]bool{}
1449
+ for _, rule := range allow {
1450
+ if value, ok := rule.(string); ok {
1451
+ seen[value] = true
1452
+ }
1453
+ }
1454
+ for _, rule := range desiredAllow {
1455
+ value, ok := rule.(string)
1456
+ if !ok {
1457
+ return nil, false, fmt.Errorf("DevRites Claude permission rules must be strings")
1458
+ }
1459
+ if !seen[value] {
1460
+ allow = append(allow, value)
1461
+ seen[value] = true
1462
+ }
1463
+ }
1464
+ permissions["allow"] = allow
1465
+ next["permissions"] = permissions
1466
+ if _, exists := next["$comment"]; !exists {
1467
+ if comment, ok := desired["$comment"].(string); ok {
1468
+ next["$comment"] = comment
1469
+ }
1470
+ }
1471
+ return next, defaultModeOwned, nil
1472
+ }
1473
+
1474
+ func isDevritesPermissionRule(value any) bool {
1475
+ rule, ok := value.(string)
1476
+ return ok && strings.HasPrefix(strings.TrimSpace(rule), "Bash(devrites-engine ")
1612
1477
  }
1613
1478
 
1614
1479
  func (r *runner) installBinary() error {
1615
1480
  if !r.opts.WithBinary || os.Getenv("DEVRITES_NO_BINARY") == "1" {
1616
- fmt.Fprintln(r.opts.Stdout, " engine binary: skipped (--no-binary); hooks fail open.")
1481
+ fmt.Fprintln(r.opts.Stdout, " engine binary: skipped (--no-binary).")
1617
1482
  return nil
1618
1483
  }
1619
1484
  if r.opts.DryRun {
@@ -1632,7 +1497,7 @@ func (r *runner) installBinary() error {
1632
1497
  staged, cleanup := r.preparedBinary, func() {}
1633
1498
  if staged == "" {
1634
1499
  var err error
1635
- staged, cleanup, err = r.acquireBinary(tag, githubBaseURL)
1500
+ staged, cleanup, err = r.acquireBinary(tag)
1636
1501
  if err != nil {
1637
1502
  return r.binaryInstallFailure(err)
1638
1503
  }
@@ -1672,17 +1537,14 @@ func (r *runner) installBinary() error {
1672
1537
  return fmt.Errorf("verify installed engine binary: %w (bad binary removed)", err)
1673
1538
  }
1674
1539
  fmt.Fprintf(r.opts.Stdout, " engine binary: installed %s\n", dest)
1675
- if !binaryReachableFromPATH(dest) {
1676
- fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %s is not on PATH; installed hooks will fail open until devrites-engine is reachable.\n", dest)
1677
- }
1678
1540
  return nil
1679
1541
  }
1680
1542
 
1681
1543
  func (r *runner) binaryTag() string {
1682
- if r.releaseBinaryTag != "" {
1683
- return r.releaseBinaryTag
1544
+ if r.requiredBinaryTag != "" {
1545
+ return r.requiredBinaryTag
1684
1546
  }
1685
- if tag := os.Getenv("DEVRITES_REF"); tag != "" {
1547
+ if tag := os.Getenv("DEVRITES_REF"); semverLike(tag) {
1686
1548
  return tag
1687
1549
  }
1688
1550
  return "v" + strings.TrimPrefix(installedVersion(r.source), "v")
@@ -1737,81 +1599,25 @@ func (r *runner) binaryInstallFailure(err error) error {
1737
1599
  if r.preparedBinary != "" {
1738
1600
  return err
1739
1601
  }
1740
- fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v - hooks fail open until you install it.\n", err)
1741
- return nil
1742
- }
1743
-
1744
- func (r *runner) acquireBinary(tag, baseURL string) (string, func(), error) {
1745
- if v := os.Getenv("DEVRITES_ENGINE_CLI"); v != "" {
1746
- if exists(v) {
1747
- if r.releaseBinaryTag == "" || strings.TrimPrefix(engineVersion(v), "v") == strings.TrimPrefix(r.releaseBinaryTag, "v") {
1748
- return v, func() {}, nil
1749
- }
1750
- } else {
1751
- return "", func() {}, fmt.Errorf("DEVRITES_ENGINE_CLI points to a missing binary: %s", v)
1752
- }
1753
- }
1754
- tmp, err := os.MkdirTemp("", "devrites-engine-*")
1755
- if err != nil {
1756
- return "", func() {}, fmt.Errorf("create temp dir: %w", err)
1757
- }
1758
- cleanup := func() { _ = os.RemoveAll(tmp) }
1759
- staged := filepath.Join(tmp, engineBinaryName())
1760
- var releaseErr error
1761
- if r.releaseBinaryTag != "" && os.Getenv("DEVRITES_UPDATE_BUNDLE") == "" {
1762
- repo := os.Getenv("DEVRITES_REPO")
1763
- if repo == "" {
1764
- repo = defaultRepo
1765
- }
1766
- downloaded, err := downloadReleaseBinary(baseURL, repo, r.releaseBinaryTag, staged)
1767
- if err == nil {
1768
- return staged, cleanup, nil
1769
- }
1770
- if downloaded {
1771
- return "", cleanup, err
1772
- }
1773
- releaseErr = err
1774
- }
1775
- if r.source != "" && exists(filepath.Join(r.source, "engine", "go.mod")) {
1776
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
1777
- defer cancel()
1778
- cmd := exec.CommandContext(ctx, "go", "build", "-buildvcs=false", "-trimpath", "-ldflags", "-s -w -X github.com/devrites/devrites/internal/version.Version="+tag, "-o", staged, ".")
1779
- cmd.Dir = filepath.Join(r.source, "engine")
1780
- if out, err := cmd.CombinedOutput(); err != nil {
1781
- if releaseErr != nil {
1782
- return "", cleanup, fmt.Errorf("could not download release binary: %v; source fallback failed: %w: %s", releaseErr, err, strings.TrimSpace(string(out)))
1783
- }
1784
- return "", cleanup, fmt.Errorf("could not build from source: %w: %s", err, strings.TrimSpace(string(out)))
1785
- }
1786
- return staged, cleanup, nil
1787
- }
1788
- if releaseErr != nil {
1789
- return "", cleanup, fmt.Errorf("could not obtain a binary: %v; no Go source fallback", releaseErr)
1602
+ if os.Getenv("DEVRITES_ENGINE_CLI") != "" {
1603
+ return err
1790
1604
  }
1791
- return "", cleanup, fmt.Errorf("could not obtain a binary (no DEVRITES_ENGINE_CLI handoff, no Go source)")
1605
+ fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v; continuing without it.\n", err)
1606
+ return nil
1792
1607
  }
1793
1608
 
1794
- func downloadReleaseBinary(baseURL, repo, tag, dest string) (bool, error) {
1795
- asset := fmt.Sprintf("devrites-%s-%s", runtime.GOOS, runtime.GOARCH)
1796
- if runtime.GOOS == "windows" {
1797
- asset += ".exe"
1798
- }
1799
- url := fmt.Sprintf("%s/%s/releases/download/%s/%s", strings.TrimRight(baseURL, "/"), repo, tag, asset)
1800
- if err := iohooks.DownloadFile(url, dest); err != nil {
1801
- return false, fmt.Errorf("download %s: %w", asset, err)
1609
+ func (r *runner) acquireBinary(tag string) (string, func(), error) {
1610
+ path := os.Getenv("DEVRITES_ENGINE_CLI")
1611
+ if path == "" {
1612
+ return "", func() {}, fmt.Errorf("no DEVRITES_ENGINE_CLI handoff")
1802
1613
  }
1803
- sumPath := dest + ".sha256"
1804
- defer os.Remove(sumPath)
1805
- if err := iohooks.DownloadFile(url+".sha256", sumPath); err != nil {
1806
- return true, fmt.Errorf("could not verify release binary %s: missing checksum: %w", asset, err)
1614
+ if !exists(path) {
1615
+ return "", func() {}, fmt.Errorf("DEVRITES_ENGINE_CLI points to a missing binary: %s", path)
1807
1616
  }
1808
- if err := verifySHA256(dest, sumPath); err != nil {
1809
- return true, fmt.Errorf("could not verify release binary %s: %w", asset, err)
1617
+ if _, err := verifyEngineBinary(path, tag, 30*time.Second); err != nil {
1618
+ return "", func() {}, fmt.Errorf("DEVRITES_ENGINE_CLI is incompatible: %w", err)
1810
1619
  }
1811
- if err := os.Chmod(dest, 0o755); err != nil && runtime.GOOS != "windows" {
1812
- return true, fmt.Errorf("make release binary executable: %w", err)
1813
- }
1814
- return true, nil
1620
+ return path, func() {}, nil
1815
1621
  }
1816
1622
 
1817
1623
  func (r *runner) removeBinary() error {
@@ -1864,36 +1670,6 @@ func binaryCandidates() []string {
1864
1670
  return append(candidates, filepath.Join("/usr/local/bin", engineBinaryName()))
1865
1671
  }
1866
1672
 
1867
- func binaryReachableFromPATH(dest string) bool {
1868
- if _, err := exec.LookPath("devrites-engine"); err == nil {
1869
- return true
1870
- }
1871
- return pathContainsDir(filepath.Dir(dest))
1872
- }
1873
-
1874
- func pathContainsDir(dir string) bool {
1875
- if dir == "" {
1876
- return false
1877
- }
1878
- want, err := filepath.Abs(dir)
1879
- if err != nil {
1880
- want = filepath.Clean(dir)
1881
- }
1882
- for _, entry := range filepath.SplitList(os.Getenv("PATH")) {
1883
- if entry == "" {
1884
- continue
1885
- }
1886
- got, err := filepath.Abs(entry)
1887
- if err != nil {
1888
- got = filepath.Clean(entry)
1889
- }
1890
- if got == want {
1891
- return true
1892
- }
1893
- }
1894
- return false
1895
- }
1896
-
1897
1673
  func engineVersion(path string) string {
1898
1674
  got, err := readEngineVersion(path, 30*time.Second)
1899
1675
  if err != nil {
@@ -2016,14 +1792,16 @@ func manifestHeader(path, key string) string {
2016
1792
  func installedVersion(source string) string {
2017
1793
  if source != "" {
2018
1794
  data, err := os.ReadFile(filepath.Join(source, "package.json"))
2019
- if err == nil {
2020
- var pkg struct {
2021
- Version string `json:"version"`
2022
- }
2023
- if json.Unmarshal(data, &pkg) == nil && pkg.Version != "" {
2024
- return pkg.Version
2025
- }
1795
+ if err != nil {
1796
+ return "unknown"
1797
+ }
1798
+ var pkg struct {
1799
+ Version string `json:"version"`
1800
+ }
1801
+ if json.Unmarshal(data, &pkg) != nil || pkg.Version == "" {
1802
+ return "unknown"
2026
1803
  }
1804
+ return pkg.Version
2027
1805
  }
2028
1806
  if version.Version != "" && version.Version != "dev" {
2029
1807
  return strings.TrimPrefix(version.Version, "v")