devrites 2.6.1 → 3.0.1

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 (1079) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +85 -50
  3. package/SECURITY.md +65 -39
  4. package/bin/devrites.mjs +135 -51
  5. package/docs/agents/domain.md +58 -0
  6. package/docs/agents/issue-driven-rites.md +11 -0
  7. package/docs/agents/issue-tracker.md +29 -0
  8. package/docs/agents/triage-labels.md +15 -0
  9. package/docs/architecture.md +60 -35
  10. package/docs/capability-surface-selection.md +18 -0
  11. package/docs/cli.md +53 -0
  12. package/docs/command-map.md +59 -22
  13. package/docs/engine/agent-contract.md +92 -0
  14. package/docs/engine/commands.md +323 -0
  15. package/docs/engine/state-schema.md +134 -0
  16. package/docs/engine/workspace-schema.md +150 -0
  17. package/docs/extensions.md +178 -0
  18. package/docs/flow.md +19 -16
  19. package/docs/harness-compliance.md +45 -0
  20. package/docs/orchestration.md +6 -4
  21. package/docs/porting-to-a-new-harness.md +27 -0
  22. package/docs/quick-reference.md +22 -0
  23. package/docs/release.md +3 -3
  24. package/docs/skills.md +42 -48
  25. package/docs/usage.md +21 -8
  26. package/engine/.gosec.json +5 -0
  27. package/engine/Makefile +64 -0
  28. package/engine/commands.go +195 -0
  29. package/engine/go.mod +5 -0
  30. package/engine/harnessmatrix.go +60 -0
  31. package/engine/harnessmatrix_test.go +59 -0
  32. package/engine/hookpolicy.go +114 -0
  33. package/engine/hookpolicy_test.go +64 -0
  34. package/engine/hooks.go +336 -0
  35. package/engine/hooks_events_test.go +106 -0
  36. package/engine/hooks_workspace.go +603 -0
  37. package/engine/internal/devritespaths/paths.go +76 -0
  38. package/engine/internal/doctor/doctor.go +178 -0
  39. package/engine/internal/doctor/doctor_test.go +150 -0
  40. package/engine/internal/fsutil/copy.go +101 -0
  41. package/engine/internal/fsutil/replace_other.go +7 -0
  42. package/engine/internal/fsutil/replace_windows.go +31 -0
  43. package/engine/internal/gate/gate.go +243 -0
  44. package/engine/internal/gate/gate_test.go +145 -0
  45. package/engine/internal/harness/compliance.go +102 -0
  46. package/engine/internal/harness/harness.go +304 -0
  47. package/engine/internal/harness/harness_test.go +104 -0
  48. package/engine/internal/hostpack/hostpack.go +259 -0
  49. package/engine/internal/hostpack/hostpack_test.go +111 -0
  50. package/engine/internal/hostpack/templates/alias_skill.md.tmpl +11 -0
  51. package/engine/internal/hostpack/templates/devrites_readme.md.tmpl +14 -0
  52. package/engine/internal/install/install.go +1560 -0
  53. package/engine/internal/install/install_test.go +564 -0
  54. package/engine/internal/iohooks/detach_other.go +11 -0
  55. package/engine/internal/iohooks/detach_unix.go +11 -0
  56. package/engine/internal/iohooks/iohooks.go +546 -0
  57. package/engine/internal/iohooks/iohooks_test.go +130 -0
  58. package/engine/internal/lib/adoption.go +634 -0
  59. package/engine/internal/lib/adoption_test.go +91 -0
  60. package/engine/internal/lib/analyze.go +224 -0
  61. package/engine/internal/lib/archivesearch.go +93 -0
  62. package/engine/internal/lib/archivesearch_test.go +110 -0
  63. package/engine/internal/lib/budget.go +172 -0
  64. package/engine/internal/lib/buildreadiness.go +88 -0
  65. package/engine/internal/lib/checkacceptance.go +176 -0
  66. package/engine/internal/lib/closeout.go +69 -0
  67. package/engine/internal/lib/command_other.go +7 -0
  68. package/engine/internal/lib/command_unix.go +24 -0
  69. package/engine/internal/lib/context.go +236 -0
  70. package/engine/internal/lib/conventions.go +449 -0
  71. package/engine/internal/lib/coverage.go +145 -0
  72. package/engine/internal/lib/doubtcoverage.go +88 -0
  73. package/engine/internal/lib/evidencefresh.go +85 -0
  74. package/engine/internal/lib/extensions.go +619 -0
  75. package/engine/internal/lib/extensions_test.go +305 -0
  76. package/engine/internal/lib/footprint.go +239 -0
  77. package/engine/internal/lib/jsonout.go +115 -0
  78. package/engine/internal/lib/jsonout_test.go +57 -0
  79. package/engine/internal/lib/lanes.go +106 -0
  80. package/engine/internal/lib/learnings.go +391 -0
  81. package/engine/internal/lib/learnings_inject_test.go +77 -0
  82. package/engine/internal/lib/learnings_remediation_test.go +24 -0
  83. package/engine/internal/lib/ledger.go +298 -0
  84. package/engine/internal/lib/ledger_test.go +162 -0
  85. package/engine/internal/lib/mutationgate.go +85 -0
  86. package/engine/internal/lib/overrides.go +164 -0
  87. package/engine/internal/lib/overrides_test.go +112 -0
  88. package/engine/internal/lib/packageexistence.go +175 -0
  89. package/engine/internal/lib/preamble.go +159 -0
  90. package/engine/internal/lib/progress.go +168 -0
  91. package/engine/internal/lib/reconcile.go +173 -0
  92. package/engine/internal/lib/reconcile_test.go +197 -0
  93. package/engine/internal/lib/resolve.go +376 -0
  94. package/engine/internal/lib/resolve_remediation_test.go +49 -0
  95. package/engine/internal/lib/reviewers.go +143 -0
  96. package/engine/internal/lib/reviewers_test.go +45 -0
  97. package/engine/internal/lib/reviewerstats.go +199 -0
  98. package/engine/internal/lib/reviewerstats_test.go +104 -0
  99. package/engine/internal/lib/reviewintegrity.go +143 -0
  100. package/engine/internal/lib/reviewintegrity_test.go +103 -0
  101. package/engine/internal/lib/runbook.go +292 -0
  102. package/engine/internal/lib/runbook_context_test.go +119 -0
  103. package/engine/internal/lib/session.go +328 -0
  104. package/engine/internal/lib/session_test.go +110 -0
  105. package/engine/internal/lib/specparse.go +125 -0
  106. package/engine/internal/lib/specskeleton.go +79 -0
  107. package/engine/internal/lib/specvalidate.go +391 -0
  108. package/engine/internal/lib/specvalidate_test.go +185 -0
  109. package/engine/internal/lib/stuck.go +206 -0
  110. package/engine/internal/lib/templates/conventions_drift.tmpl +6 -0
  111. package/engine/internal/lib/templates/conventions_ledger_header.md +6 -0
  112. package/engine/internal/lib/templates/conventions_orient_header.txt +1 -0
  113. package/engine/internal/lib/testintegrity.go +203 -0
  114. package/engine/internal/lib/testintegrity_test.go +72 -0
  115. package/engine/internal/lib/tickafk.go +99 -0
  116. package/engine/internal/lib/util.go +68 -0
  117. package/engine/internal/migrate/migrate.go +213 -0
  118. package/engine/internal/migrate/migrate_test.go +105 -0
  119. package/engine/internal/orient/firstrun.go +50 -0
  120. package/engine/internal/orient/firstrun_test.go +72 -0
  121. package/engine/internal/orient/orient.go +69 -0
  122. package/engine/internal/orient/orient_test.go +25 -0
  123. package/engine/internal/profile/profile.go +270 -0
  124. package/engine/internal/state/concurrency_test.go +184 -0
  125. package/engine/internal/state/feature.go +309 -0
  126. package/engine/internal/state/lock_other.go +24 -0
  127. package/engine/internal/state/lock_unix.go +35 -0
  128. package/engine/internal/state/lock_windows.go +56 -0
  129. package/engine/internal/state/schema.go +102 -0
  130. package/engine/internal/state/snapshot.go +384 -0
  131. package/engine/internal/state/state_test.go +260 -0
  132. package/engine/internal/state/status.go +90 -0
  133. package/engine/internal/state/write.go +59 -0
  134. package/engine/internal/testutil/testutil.go +66 -0
  135. package/engine/internal/version/version.go +57 -0
  136. package/engine/internal/workflow/commands.go +64 -0
  137. package/engine/main.go +530 -0
  138. package/engine/profile_test.go +76 -0
  139. package/engine/subagent_orient_context.txt +6 -0
  140. package/engine/testdata/fixtures/basic/devrites-root/features/auth-tokens/decisions.md +4 -0
  141. package/engine/testdata/fixtures/basic/devrites-root/features/auth-tokens/feature.md +8 -0
  142. package/engine/testdata/fixtures/basic/devrites-root/features/auth-tokens/plan.md +5 -0
  143. package/engine/testdata/fixtures/basic/devrites-root/features/auth-tokens/spec.md +6 -0
  144. package/engine/testdata/fixtures/basic/devrites-root/features/auth-tokens/status.md +4 -0
  145. package/engine/testdata/fixtures/basic/devrites-root/features/auth-tokens/tasks.md +1 -0
  146. package/engine/testdata/fixtures/basic/devrites-root/features/search-ranking/feature.md +8 -0
  147. package/engine/testdata/fixtures/basic/devrites-root/features/search-ranking/spec.md +6 -0
  148. package/engine/testdata/golden/TestParityA1Guard/devrites-edit-allowed.golden +1 -0
  149. package/engine/testdata/golden/TestParityA1Guard/inline-fallback-silent.golden +1 -0
  150. package/engine/testdata/golden/TestParityA1Guard/no-window-silent.golden +1 -0
  151. package/engine/testdata/golden/TestParityA1Guard/non-edit-tool.golden +1 -0
  152. package/engine/testdata/golden/TestParityA1Guard/observe-mode-silent.golden +1 -0
  153. package/engine/testdata/golden/TestParityA1Guard/source-edit-enforce-denies.golden +2 -0
  154. package/engine/testdata/golden/TestParityA1Guard/subagent-allowed.golden +1 -0
  155. package/engine/testdata/golden/TestParityAnalyze/noactive.golden +1 -0
  156. package/engine/testdata/golden/TestParityAnalyze/slug=covered.golden +15 -0
  157. package/engine/testdata/golden/TestParityAnalyze/slug=dangling.golden +16 -0
  158. package/engine/testdata/golden/TestParityAnalyze/slug=ghost.golden +1 -0
  159. package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +15 -0
  160. package/engine/testdata/golden/TestParityAnalyze/slug=order.golden +17 -0
  161. package/engine/testdata/golden/TestParityAnalyze/slug=orphan.golden +16 -0
  162. package/engine/testdata/golden/TestParityAnalyze/slug=uncovered.golden +16 -0
  163. package/engine/testdata/golden/TestParityBudget/default-active.golden +4 -0
  164. package/engine/testdata/golden/TestParityBudget/extra-md.golden +5 -0
  165. package/engine/testdata/golden/TestParityBudget/no-active.golden +1 -0
  166. package/engine/testdata/golden/TestParityBudget/no-feature.golden +1 -0
  167. package/engine/testdata/golden/TestParityBudget/over-advisory.golden +6 -0
  168. package/engine/testdata/golden/TestParityBudget/over-strict.golden +6 -0
  169. package/engine/testdata/golden/TestParityBudget/within.golden +6 -0
  170. package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +2 -0
  171. package/engine/testdata/golden/TestParityBuildReadiness/arg=approvenone.golden +1 -0
  172. package/engine/testdata/golden/TestParityBuildReadiness/arg=await.golden +1 -0
  173. package/engine/testdata/golden/TestParityBuildReadiness/arg=blocked.golden +1 -0
  174. package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +2 -0
  175. package/engine/testdata/golden/TestParityBuildReadiness/arg=ghost.golden +1 -0
  176. package/engine/testdata/golden/TestParityBuildReadiness/arg=noapprove.golden +1 -0
  177. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +2 -0
  178. package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +2 -0
  179. package/engine/testdata/golden/TestParityCheckAcceptance/arg=blank-section.golden +1 -0
  180. package/engine/testdata/golden/TestParityCheckAcceptance/arg=empty.golden +1 -0
  181. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-dropped.golden +1 -0
  182. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +2 -0
  183. package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-unchecked.golden +1 -0
  184. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-gap.golden +1 -0
  185. package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-ok.golden +2 -0
  186. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-seal.golden +1 -0
  187. package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-section.golden +1 -0
  188. package/engine/testdata/golden/TestParityCheckAcceptance/arg=nonexistent.golden +1 -0
  189. package/engine/testdata/golden/TestParityCheckAcceptance.golden +1 -0
  190. package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +2 -0
  191. package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +2 -0
  192. package/engine/testdata/golden/TestParityCloseOut/archive_clobber.golden +1 -0
  193. package/engine/testdata/golden/TestParityCloseOut/missing_slug.golden +1 -0
  194. package/engine/testdata/golden/TestParityCloseOut/missing_workspace.golden +1 -0
  195. package/engine/testdata/golden/TestParityCoverage/arg=flat.golden +7 -0
  196. package/engine/testdata/golden/TestParityCoverage/arg=full.golden +10 -0
  197. package/engine/testdata/golden/TestParityCoverage/arg=nonexistent.golden +1 -0
  198. package/engine/testdata/golden/TestParityCoverage/arg=nospec.golden +1 -0
  199. package/engine/testdata/golden/TestParityCoverage/arg=speconly.golden +9 -0
  200. package/engine/testdata/golden/TestParityCoverage.golden +1 -0
  201. package/engine/testdata/golden/TestParityCursor/bare.golden +3 -0
  202. package/engine/testdata/golden/TestParityCursor/full-afk.golden +6 -0
  203. package/engine/testdata/golden/TestParityCursor/full-hitl.golden +5 -0
  204. package/engine/testdata/golden/TestParityCursor/no-active.golden +1 -0
  205. package/engine/testdata/golden/TestParityCursor/red.golden +5 -0
  206. package/engine/testdata/golden/TestParityCursor/zerogates.golden +5 -0
  207. package/engine/testdata/golden/TestParityDoubtCoverage/arg=doubted.golden +2 -0
  208. package/engine/testdata/golden/TestParityDoubtCoverage/arg=ghost.golden +2 -0
  209. package/engine/testdata/golden/TestParityDoubtCoverage/arg=inline.golden +3 -0
  210. package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing-inline.golden +3 -0
  211. package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing.golden +3 -0
  212. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nodoubt.golden +4 -0
  213. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nolog.golden +2 -0
  214. package/engine/testdata/golden/TestParityDoubtCoverage/arg=nowright.golden +2 -0
  215. package/engine/testdata/golden/TestParityDoubtCoverage.golden +1 -0
  216. package/engine/testdata/golden/TestParityLearnings/add.golden +2 -0
  217. package/engine/testdata/golden/TestParityLearnings/list.golden +5 -0
  218. package/engine/testdata/golden/TestParityLearnings/mine-no-archive.golden +2 -0
  219. package/engine/testdata/golden/TestParityLearnings/mine.golden +5 -0
  220. package/engine/testdata/golden/TestParityLearnings/nudge.golden +2 -0
  221. package/engine/testdata/golden/TestParityLearnings/unknown.golden +1 -0
  222. package/engine/testdata/golden/TestParityMutationGate/active-slug.golden +3 -0
  223. package/engine/testdata/golden/TestParityMutationGate/explicit-slug.golden +3 -0
  224. package/engine/testdata/golden/TestParityMutationGate/ghost.golden +1 -0
  225. package/engine/testdata/golden/TestParityOrientSilentPath.golden +1 -0
  226. package/engine/testdata/golden/TestParityPackageExistence/declared.golden +2 -0
  227. package/engine/testdata/golden/TestParityPackageExistence/default-import-subpath.golden +2 -0
  228. package/engine/testdata/golden/TestParityPackageExistence/ghost-workspace.golden +1 -0
  229. package/engine/testdata/golden/TestParityPackageExistence/no-manifest.golden +1 -0
  230. package/engine/testdata/golden/TestParityPackageExistence/not-git.golden +1 -0
  231. package/engine/testdata/golden/TestParityPackageExistence/undeclared.golden +1 -0
  232. package/engine/testdata/golden/TestParityPreamble/afk/a.golden +14 -0
  233. package/engine/testdata/golden/TestParityPreamble/hitl/active.golden +18 -0
  234. package/engine/testdata/golden/TestParityPreamble/hitl/bare.golden +10 -0
  235. package/engine/testdata/golden/TestParityPreamble/hitl/full.golden +18 -0
  236. package/engine/testdata/golden/TestParityPreamble/hitl/ghost.golden +2 -0
  237. package/engine/testdata/golden/TestParityPreamble/hitl/nonl.golden +11 -0
  238. package/engine/testdata/golden/TestParityPreamble/hitl/openend.golden +13 -0
  239. package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +4 -0
  240. package/engine/testdata/golden/TestParityProgress/arg=done.golden +3 -0
  241. package/engine/testdata/golden/TestParityProgress/arg=ghost.golden +1 -0
  242. package/engine/testdata/golden/TestParityProgress/arg=mid.golden +4 -0
  243. package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +3 -0
  244. package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +3 -0
  245. package/engine/testdata/golden/TestParityProgress/arg=plan.golden +4 -0
  246. package/engine/testdata/golden/TestParityProgress/arg=seal.golden +3 -0
  247. package/engine/testdata/golden/TestParityReconcile/bad-mode.golden +1 -0
  248. package/engine/testdata/golden/TestParityReconcile/check-clean/snapshot.golden +2 -0
  249. package/engine/testdata/golden/TestParityReconcile/check-clean.golden +2 -0
  250. package/engine/testdata/golden/TestParityReconcile/check-no-base.golden +1 -0
  251. package/engine/testdata/golden/TestParityReconcile/check-no-claimed/snapshot.golden +2 -0
  252. package/engine/testdata/golden/TestParityReconcile/check-no-claimed.golden +1 -0
  253. package/engine/testdata/golden/TestParityReconcile/check-violation/snapshot.golden +2 -0
  254. package/engine/testdata/golden/TestParityReconcile/check-violation.golden +1 -0
  255. package/engine/testdata/golden/TestParityReconcile/inline-fallback/snapshot.golden +2 -0
  256. package/engine/testdata/golden/TestParityReconcile/inline-fallback.golden +1 -0
  257. package/engine/testdata/golden/TestParityReconcile/not-git.golden +1 -0
  258. package/engine/testdata/golden/TestParityReconcile/snapshot/snapshot.golden +2 -0
  259. package/engine/testdata/golden/TestParityReconcile/snapshot.golden +2 -0
  260. package/engine/testdata/golden/TestParityRedwatch/fail-writes-red.golden +2 -0
  261. package/engine/testdata/golden/TestParityRedwatch/non-bash-tool.golden +1 -0
  262. package/engine/testdata/golden/TestParityRedwatch/non-test-command.golden +1 -0
  263. package/engine/testdata/golden/TestParityRedwatch/pass-clears-red.golden +1 -0
  264. package/engine/testdata/golden/TestParityResolve/answer/questions.md.golden +9 -0
  265. package/engine/testdata/golden/TestParityResolve/answer/state.md.golden +6 -0
  266. package/engine/testdata/golden/TestParityResolve/answer.golden +4 -0
  267. package/engine/testdata/golden/TestParityResolve/batch/questions.md.golden +9 -0
  268. package/engine/testdata/golden/TestParityResolve/batch-unterminated/questions.md.golden +9 -0
  269. package/engine/testdata/golden/TestParityResolve/batch-unterminated.golden +2 -0
  270. package/engine/testdata/golden/TestParityResolve/batch.golden +3 -0
  271. package/engine/testdata/golden/TestParityResolve/drop/questions.md.golden +9 -0
  272. package/engine/testdata/golden/TestParityResolve/drop/state.md.golden +8 -0
  273. package/engine/testdata/golden/TestParityResolve/drop.golden +4 -0
  274. package/engine/testdata/golden/TestParityResolve/missing-answer.golden +1 -0
  275. package/engine/testdata/golden/TestParityResolve/next-qid.golden +2 -0
  276. package/engine/testdata/golden/TestParityResolve/no-workspace.golden +1 -0
  277. package/engine/testdata/golden/TestParityResolve/not-found.golden +1 -0
  278. package/engine/testdata/golden/TestParityResolve/not-open.golden +1 -0
  279. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-devrites-agent-denies.golden +2 -0
  280. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-non-devrites-allows.golden +1 -0
  281. package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-subagent-type-only-allows.golden +1 -0
  282. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-git-commit.golden +2 -0
  283. package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-mutating.golden +2 -0
  284. package/engine/testdata/golden/TestParityReviewerReadonly/non-bash-tool.golden +1 -0
  285. package/engine/testdata/golden/TestParityReviewerReadonly/non-tool-input.golden +1 -0
  286. package/engine/testdata/golden/TestParityReviewerReadonly/observe-mutating-silent.golden +1 -0
  287. package/engine/testdata/golden/TestParityReviewerReadonly/safe-bash-command.golden +1 -0
  288. package/engine/testdata/golden/TestParitySpecValidate/arg=dup.golden +1 -0
  289. package/engine/testdata/golden/TestParitySpecValidate/arg=empty.golden +1 -0
  290. package/engine/testdata/golden/TestParitySpecValidate/arg=flat.golden +2 -0
  291. package/engine/testdata/golden/TestParitySpecValidate/arg=nonexistent.golden +1 -0
  292. package/engine/testdata/golden/TestParitySpecValidate/arg=noscenario.golden +1 -0
  293. package/engine/testdata/golden/TestParitySpecValidate/arg=noshall.golden +1 -0
  294. package/engine/testdata/golden/TestParitySpecValidate/arg=nothen.golden +1 -0
  295. package/engine/testdata/golden/TestParitySpecValidate/arg=valid/spec.md.golden +2 -0
  296. package/engine/testdata/golden/TestParitySpecValidate/arg=valid.golden +2 -0
  297. package/engine/testdata/golden/TestParitySpecValidate.golden +1 -0
  298. package/engine/testdata/golden/TestParityStatusline/full-afk.golden +2 -0
  299. package/engine/testdata/golden/TestParityStatusline/full-hitl.golden +2 -0
  300. package/engine/testdata/golden/TestParityStatusline/no-active.golden +1 -0
  301. package/engine/testdata/golden/TestParityStatusline/nophase.golden +2 -0
  302. package/engine/testdata/golden/TestParityStatusline/red.golden +2 -0
  303. package/engine/testdata/golden/TestParityStuckCheck/bad-cmd.golden +1 -0
  304. package/engine/testdata/golden/TestParityStuckCheck/custom-window-2.golden +2 -0
  305. package/engine/testdata/golden/TestParityStuckCheck/dominant.golden +2 -0
  306. package/engine/testdata/golden/TestParityStuckCheck/few.golden +2 -0
  307. package/engine/testdata/golden/TestParityStuckCheck/ghost.golden +2 -0
  308. package/engine/testdata/golden/TestParityStuckCheck/identical.golden +2 -0
  309. package/engine/testdata/golden/TestParityStuckCheck/no-cmd.golden +1 -0
  310. package/engine/testdata/golden/TestParityStuckCheck/no-slug.golden +1 -0
  311. package/engine/testdata/golden/TestParityStuckCheck/nolog.golden +2 -0
  312. package/engine/testdata/golden/TestParityStuckCheck/pingpong.golden +2 -0
  313. package/engine/testdata/golden/TestParityStuckCheck/varied.golden +2 -0
  314. package/engine/testdata/golden/TestParityStuckCheck/window-04-identical.golden +2 -0
  315. package/engine/testdata/golden/TestParityStuckCheck/window-2.5-varied4.golden +2 -0
  316. package/engine/testdata/golden/TestParityStuckCheck/window-4x-varied4.golden +2 -0
  317. package/engine/testdata/golden/TestParityStuckCheck/window-leadspace-varied4.golden +2 -0
  318. package/engine/testdata/golden/TestParityStuckCheck/window-plus4-identical.golden +2 -0
  319. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent-subagent-type-key.golden +2 -0
  320. package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent.golden +2 -0
  321. package/engine/testdata/golden/TestParitySubagentOrient/garbage-input.golden +1 -0
  322. package/engine/testdata/golden/TestParitySubagentOrient/non-devrites-agent.golden +1 -0
  323. package/engine/testdata/golden/TestParityTestIntegrity/assertion-dropped.golden +1 -0
  324. package/engine/testdata/golden/TestParityTestIntegrity/clean.golden +2 -0
  325. package/engine/testdata/golden/TestParityTestIntegrity/deleted.golden +1 -0
  326. package/engine/testdata/golden/TestParityTestIntegrity/ghost-workspace.golden +1 -0
  327. package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +1 -0
  328. package/engine/testdata/golden/TestParityTestIntegrity/skip-added.golden +1 -0
  329. package/engine/testdata/golden/TestParityTickAfk/budget1/state.md.golden +1 -0
  330. package/engine/testdata/golden/TestParityTickAfk/budget1.golden +2 -0
  331. package/engine/testdata/golden/TestParityTickAfk/budget3/state.md.golden +5 -0
  332. package/engine/testdata/golden/TestParityTickAfk/budget3.golden +2 -0
  333. package/engine/testdata/golden/TestParityTickAfk/bulletPipe/state.md.golden +1 -0
  334. package/engine/testdata/golden/TestParityTickAfk/bulletPipe.golden +2 -0
  335. package/engine/testdata/golden/TestParityTickAfk/missingField/state.md.golden +3 -0
  336. package/engine/testdata/golden/TestParityTickAfk/missingField.golden +2 -0
  337. package/engine/testdata/golden/TestParityTickAfk/missingFile.golden +1 -0
  338. package/engine/testdata/golden/TestParityTickAfk/nonNumeric/state.md.golden +1 -0
  339. package/engine/testdata/golden/TestParityTickAfk/nonNumeric.golden +1 -0
  340. package/engine/testdata/golden/TestParityTickAfk/none/state.md.golden +1 -0
  341. package/engine/testdata/golden/TestParityTickAfk/none.golden +2 -0
  342. package/engine/testdata/golden/TestParityWrightScope/agent-required-non-wright.golden +1 -0
  343. package/engine/testdata/golden/TestParityWrightScope/devrites-edit-allowed.golden +1 -0
  344. package/engine/testdata/golden/TestParityWrightScope/in-scope-allowed.golden +1 -0
  345. package/engine/testdata/golden/TestParityWrightScope/non-edit-tool.golden +1 -0
  346. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +2 -0
  347. package/engine/testdata/golden/TestParityWrightScope/out-of-scope-observe-silent.golden +1 -0
  348. package/engine/testdata/golden/TestSpecSkeleton/missing-some.golden +2 -0
  349. package/engine/testdata/golden/TestSpecSkeleton/present-all.golden +2 -0
  350. package/engine/testdata/golden/TestStatusLiveWorkspace.golden +10 -0
  351. package/engine/testdata/golden/TestStuckLogHashParity.golden +1 -0
  352. package/engine/testdata/golden/status/auth-tokens.txt +9 -0
  353. package/engine/testdata/golden/status/search-ranking.txt +9 -0
  354. package/engine/tests/adr_0003_gate_exit_code_test.go +32 -0
  355. package/engine/tests/adr_0004_required_by_phase_test.go +62 -0
  356. package/engine/tests/adr_0006_clock_seam_test.go +36 -0
  357. package/engine/tests/budget_test.go +77 -0
  358. package/engine/tests/canonical_ac_ids_test.go +44 -0
  359. package/engine/tests/cli_test.go +209 -0
  360. package/engine/tests/concurrency_cli_test.go +125 -0
  361. package/engine/tests/conventions_test.go +106 -0
  362. package/engine/tests/doctor_cli_test.go +62 -0
  363. package/engine/tests/gate_test.go +235 -0
  364. package/engine/tests/hook_test.go +390 -0
  365. package/engine/tests/hooks_io_test.go +218 -0
  366. package/engine/tests/json_contract_test.go +99 -0
  367. package/engine/tests/lib_parity_test.go +942 -0
  368. package/engine/tests/meta_test.go +67 -0
  369. package/engine/tests/migrate_cli_test.go +193 -0
  370. package/engine/tests/parity_analyze_test.go +55 -0
  371. package/engine/tests/parity_buildreadiness_test.go +42 -0
  372. package/engine/tests/parity_closeout_test.go +102 -0
  373. package/engine/tests/parity_githelpers_test.go +41 -0
  374. package/engine/tests/parity_learnings_test.go +90 -0
  375. package/engine/tests/parity_mutationgate_test.go +36 -0
  376. package/engine/tests/parity_packageexistence_test.go +64 -0
  377. package/engine/tests/parity_reconcile_test.go +88 -0
  378. package/engine/tests/parity_resolve_test.go +144 -0
  379. package/engine/tests/parity_test.go +380 -0
  380. package/engine/tests/parity_testintegrity_test.go +65 -0
  381. package/engine/tests/parity_tickafk_test.go +83 -0
  382. package/engine/validatepack.go +192 -0
  383. package/engine/validatepack_test.go +79 -0
  384. package/engine/validatepackskills.go +181 -0
  385. package/engine/validatepackskills_test.go +171 -0
  386. package/install.sh +67 -912
  387. package/pack/.claude/agents/devrites-code-reviewer.md +20 -3
  388. package/pack/.claude/agents/devrites-devex-reviewer.md +13 -3
  389. package/pack/.claude/agents/devrites-doubt-reviewer.md +16 -3
  390. package/pack/.claude/agents/devrites-forge-judge.md +14 -4
  391. package/pack/.claude/agents/devrites-frontend-reviewer.md +27 -3
  392. package/pack/.claude/agents/devrites-performance-reviewer.md +18 -2
  393. package/pack/.claude/agents/devrites-plan-reviewer.md +13 -3
  394. package/pack/.claude/agents/devrites-retrospector.md +12 -7
  395. package/pack/.claude/agents/devrites-security-auditor.md +20 -4
  396. package/pack/.claude/agents/devrites-simplifier-reviewer.md +17 -2
  397. package/pack/.claude/agents/devrites-slice-wright.md +32 -16
  398. package/pack/.claude/agents/devrites-spec-reviewer.md +17 -2
  399. package/pack/.claude/agents/devrites-strategy-reviewer.md +12 -2
  400. package/pack/.claude/agents/devrites-test-analyst.md +20 -3
  401. package/pack/.claude/settings.json +58 -8
  402. package/pack/.claude/skills/devrites-api-interface/SKILL.md +22 -5
  403. package/pack/.claude/skills/devrites-audit/SKILL.md +1 -1
  404. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +26 -53
  405. package/pack/.claude/skills/devrites-browser-proof/reference/browser-performance.md +15 -0
  406. package/pack/.claude/skills/devrites-browser-proof/reference/visual-verdict.md +34 -0
  407. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +21 -6
  408. package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +11 -0
  409. package/pack/.claude/skills/devrites-debug-recovery/reference/regression-test.md +2 -3
  410. package/pack/.claude/skills/devrites-debug-recovery/reference/trace.md +25 -0
  411. package/pack/.claude/skills/devrites-doubt/SKILL.md +10 -3
  412. package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +16 -13
  413. package/pack/.claude/skills/devrites-frontend-craft/reference/craft.md +15 -3
  414. package/pack/.claude/skills/devrites-frontend-craft/reference/quality-standards.md +19 -0
  415. package/pack/.claude/skills/devrites-frontend-craft/reference/reuse-first.md +3 -3
  416. package/pack/.claude/skills/devrites-interview/SKILL.md +30 -7
  417. package/pack/.claude/skills/devrites-lib/SKILL.md +35 -36
  418. package/pack/.claude/skills/devrites-lib/reference/intent-map.md +16 -0
  419. package/pack/.claude/skills/devrites-lib/reference/model-tiers.md +43 -0
  420. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +104 -20
  421. package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +111 -0
  422. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/README.md +14 -6
  423. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/afk-hitl.md +10 -18
  424. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/agents.md +81 -16
  425. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/anti-patterns.md +3 -4
  426. package/pack/.claude/skills/devrites-lib/reference/standards/browser-proof-checklist.md +9 -0
  427. package/pack/.claude/skills/devrites-lib/reference/standards/ci-cd.md +62 -0
  428. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +141 -0
  429. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/context-hygiene.md +17 -0
  430. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/core.md +9 -18
  431. package/pack/.claude/skills/devrites-lib/reference/standards/definition-of-done.md +16 -0
  432. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/deprecation.md +21 -0
  433. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/developer-experience.md +3 -3
  434. package/pack/.claude/skills/devrites-lib/reference/standards/development-workflow.md +51 -0
  435. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/documentation.md +9 -1
  436. package/pack/.claude/skills/devrites-lib/reference/standards/edge-case-trace.md +19 -0
  437. package/pack/.claude/skills/devrites-lib/reference/standards/elicitation.md +80 -0
  438. package/pack/.claude/skills/devrites-lib/reference/standards/git-workflow.md +65 -0
  439. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/observability.md +13 -0
  440. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/performance.md +1 -1
  441. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/principles.md +3 -3
  442. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/prose-style.md +18 -2
  443. package/pack/.claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +9 -0
  444. package/pack/.claude/skills/devrites-lib/reference/standards/review-checklist.md +10 -0
  445. package/pack/.claude/skills/devrites-lib/reference/standards/security-checklist.md +9 -0
  446. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/security.md +45 -4
  447. package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +105 -0
  448. package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +185 -0
  449. package/pack/.claude/skills/devrites-lib/reference/standards/test-proof-checklist.md +9 -0
  450. package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +162 -0
  451. package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/tooling.md +29 -2
  452. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +110 -0
  453. package/pack/.claude/skills/devrites-prose-craft/SKILL.md +40 -76
  454. package/pack/.claude/skills/devrites-prose-craft/reference/examples.md +19 -5
  455. package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +3 -6
  456. package/pack/.claude/skills/devrites-source-driven/SKILL.md +17 -1
  457. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +20 -7
  458. package/pack/.claude/skills/devrites-ux-shape/reference/brief-template.md +25 -10
  459. package/pack/.claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +2 -2
  460. package/pack/.claude/skills/rite/SKILL.md +67 -42
  461. package/pack/.claude/skills/rite/reference/menu.md +4 -1
  462. package/pack/.claude/skills/rite-adopt/SKILL.md +31 -22
  463. package/pack/.claude/skills/rite-adopt/reference/adoption.md +6 -9
  464. package/pack/.claude/skills/rite-adopt/reference/anti-patterns.md +1 -1
  465. package/pack/.claude/skills/rite-autocomplete/SKILL.md +25 -25
  466. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +2 -2
  467. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +1 -1
  468. package/pack/.claude/skills/rite-build/SKILL.md +24 -256
  469. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +3 -3
  470. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +1 -1
  471. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +1 -1
  472. package/pack/.claude/skills/rite-build/reference/checkpoint.md +44 -0
  473. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +10 -7
  474. package/pack/.claude/skills/rite-build/reference/forge.md +17 -7
  475. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +2 -0
  476. package/pack/.claude/skills/rite-build/reference/output.md +41 -0
  477. package/pack/.claude/skills/rite-build/reference/phase-contract.md +258 -0
  478. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +11 -10
  479. package/pack/.claude/skills/rite-converge/SKILL.md +131 -0
  480. package/pack/.claude/skills/rite-converge/reference/anti-patterns.md +35 -0
  481. package/pack/.claude/skills/rite-converge/reference/convergence-assessment.md +61 -0
  482. package/pack/.claude/skills/rite-customize/SKILL.md +67 -0
  483. package/pack/.claude/skills/rite-define/SKILL.md +82 -93
  484. package/pack/.claude/skills/rite-define/reference/anti-patterns.md +2 -1
  485. package/pack/.claude/skills/rite-define/reference/gates.md +3 -3
  486. package/pack/.claude/skills/rite-define/reference/plan-template.md +35 -6
  487. package/pack/.claude/skills/rite-doctor/SKILL.md +41 -16
  488. package/pack/.claude/skills/rite-dogfood/SKILL.md +49 -0
  489. package/pack/.claude/skills/rite-explain/SKILL.md +148 -0
  490. package/pack/.claude/skills/rite-explain/reference/intake.md +79 -0
  491. package/pack/.claude/skills/rite-frame/SKILL.md +14 -15
  492. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +1 -1
  493. package/pack/.claude/skills/rite-handoff/SKILL.md +19 -17
  494. package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +16 -6
  495. package/pack/.claude/skills/rite-learn/SKILL.md +46 -24
  496. package/pack/.claude/skills/rite-plan/SKILL.md +35 -26
  497. package/pack/.claude/skills/rite-plan/reference/anti-patterns.md +1 -1
  498. package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +3 -3
  499. package/pack/.claude/skills/rite-plan/reference/slicing.md +20 -7
  500. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +15 -16
  501. package/pack/.claude/skills/rite-polish/SKILL.md +18 -23
  502. package/pack/.claude/skills/rite-polish/reference/anti-ai-slop.md +31 -2
  503. package/pack/.claude/skills/rite-polish/reference/anti-patterns.md +4 -1
  504. package/pack/.claude/skills/rite-polish/reference/code.md +16 -5
  505. package/pack/.claude/skills/rite-polish/reference/design-system-discovery.md +5 -32
  506. package/pack/.claude/skills/rite-pov/SKILL.md +46 -0
  507. package/pack/.claude/skills/rite-pr-feedback/SKILL.md +49 -0
  508. package/pack/.claude/skills/rite-pressure-test/SKILL.md +40 -10
  509. package/pack/.claude/skills/rite-prototype/SKILL.md +23 -2
  510. package/pack/.claude/skills/rite-prove/SKILL.md +41 -96
  511. package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +48 -0
  512. package/pack/.claude/skills/rite-prove/reference/anti-patterns.md +1 -1
  513. package/pack/.claude/skills/rite-prove/reference/browser-proof.md +36 -13
  514. package/pack/.claude/skills/rite-quick/SKILL.md +19 -12
  515. package/pack/.claude/skills/rite-resolve/SKILL.md +21 -25
  516. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
  517. package/pack/.claude/skills/rite-review/SKILL.md +43 -45
  518. package/pack/.claude/skills/rite-review/reference/anti-patterns.md +1 -1
  519. package/pack/.claude/skills/rite-review/reference/performance-checklist.md +1 -1
  520. package/pack/.claude/skills/rite-review/reference/performance-review.md +4 -22
  521. package/pack/.claude/skills/rite-review/reference/security-review.md +2 -2
  522. package/pack/.claude/skills/rite-seal/SKILL.md +40 -174
  523. package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +1 -1
  524. package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +6 -9
  525. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +6 -14
  526. package/pack/.claude/skills/rite-seal/reference/go-no-go.md +3 -3
  527. package/pack/.claude/skills/rite-seal/reference/output.md +7 -0
  528. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +211 -0
  529. package/pack/.claude/skills/rite-seal/reference/seal-template.md +1 -1
  530. package/pack/.claude/skills/rite-ship/SKILL.md +45 -75
  531. package/pack/.claude/skills/rite-ship/reference/adr-promotion.md +9 -0
  532. package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +1 -1
  533. package/pack/.claude/skills/rite-ship/reference/close-out.md +12 -1
  534. package/pack/.claude/skills/rite-ship/reference/design-memory.md +2 -2
  535. package/pack/.claude/skills/rite-ship/reference/git-ship.md +32 -1
  536. package/pack/.claude/skills/rite-ship/reference/ledger.md +56 -0
  537. package/pack/.claude/skills/rite-ship/reference/rollout.md +43 -0
  538. package/pack/.claude/skills/rite-spec/SKILL.md +110 -60
  539. package/pack/.claude/skills/rite-spec/reference/ai-spec-template.md +33 -0
  540. package/pack/.claude/skills/rite-spec/reference/anti-patterns.md +4 -1
  541. package/pack/.claude/skills/rite-spec/reference/investigation.md +34 -14
  542. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +7 -37
  543. package/pack/.claude/skills/rite-spec/reference/references-intake.md +23 -18
  544. package/pack/.claude/skills/rite-spec/reference/spec-template.md +94 -118
  545. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +128 -139
  546. package/pack/.claude/skills/rite-status/SKILL.md +26 -22
  547. package/pack/.claude/skills/rite-temper/SKILL.md +34 -27
  548. package/pack/.claude/skills/rite-temper/reference/anti-patterns.md +1 -1
  549. package/pack/.claude/skills/rite-temper/reference/scope-modes.md +1 -1
  550. package/pack/.claude/skills/rite-temper/reference/significance.md +1 -1
  551. package/pack/.claude/skills/rite-vet/SKILL.md +64 -71
  552. package/pack/.claude/skills/rite-vet/reference/anti-patterns.md +1 -1
  553. package/pack/.claude/skills/rite-vet/reference/cross-model.md +7 -5
  554. package/pack/.claude/skills/rite-vet/reference/depth.md +1 -1
  555. package/pack/.claude/skills/rite-vet/reference/review-axes.md +3 -3
  556. package/pack/.claude/skills/rite-zoom-out/SKILL.md +16 -17
  557. package/pack/generated/README.md +10 -0
  558. package/pack/generated/claude/agents/devrites-code-reviewer.md +103 -0
  559. package/pack/generated/claude/agents/devrites-devex-reviewer.md +104 -0
  560. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +68 -0
  561. package/pack/generated/claude/agents/devrites-forge-judge.md +102 -0
  562. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +84 -0
  563. package/pack/generated/claude/agents/devrites-performance-reviewer.md +111 -0
  564. package/pack/generated/claude/agents/devrites-plan-reviewer.md +89 -0
  565. package/pack/generated/claude/agents/devrites-retrospector.md +96 -0
  566. package/pack/generated/claude/agents/devrites-security-auditor.md +85 -0
  567. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +90 -0
  568. package/pack/generated/claude/agents/devrites-slice-wright.md +210 -0
  569. package/pack/generated/claude/agents/devrites-spec-reviewer.md +87 -0
  570. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +72 -0
  571. package/pack/generated/claude/agents/devrites-test-analyst.md +64 -0
  572. package/pack/generated/claude/settings.json +103 -0
  573. package/pack/generated/claude/skills/devrites-api-interface/SKILL.md +61 -0
  574. package/pack/generated/claude/skills/devrites-audit/SKILL.md +73 -0
  575. package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +76 -0
  576. package/pack/generated/claude/skills/devrites-browser-proof/reference/browser-performance.md +15 -0
  577. package/pack/generated/claude/skills/devrites-browser-proof/reference/visual-verdict.md +34 -0
  578. package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +65 -0
  579. package/pack/generated/claude/skills/devrites-debug-recovery/reference/build-the-loop.md +58 -0
  580. package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +17 -0
  581. package/pack/generated/claude/skills/devrites-debug-recovery/reference/hypotheses.md +17 -0
  582. package/pack/generated/claude/skills/devrites-debug-recovery/reference/instrumentation.md +21 -0
  583. package/pack/generated/claude/skills/devrites-debug-recovery/reference/regression-test.md +30 -0
  584. package/pack/generated/claude/skills/devrites-debug-recovery/reference/trace.md +25 -0
  585. package/pack/generated/claude/skills/devrites-doubt/SKILL.md +82 -0
  586. package/pack/generated/claude/skills/devrites-frontend-craft/SKILL.md +99 -0
  587. package/pack/generated/claude/skills/devrites-frontend-craft/reference/craft.md +71 -0
  588. package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +116 -0
  589. package/pack/generated/claude/skills/devrites-frontend-craft/reference/fullstack.md +45 -0
  590. package/pack/generated/claude/skills/devrites-frontend-craft/reference/quality-standards.md +234 -0
  591. package/pack/generated/claude/skills/devrites-frontend-craft/reference/reuse-first.md +59 -0
  592. package/pack/generated/claude/skills/devrites-frontend-craft/reference/shape.md +60 -0
  593. package/pack/generated/claude/skills/devrites-interview/SKILL.md +104 -0
  594. package/pack/generated/claude/skills/devrites-lib/SKILL.md +82 -0
  595. package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +16 -0
  596. package/pack/generated/claude/skills/devrites-lib/reference/model-tiers.md +43 -0
  597. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +165 -0
  598. package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +111 -0
  599. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +61 -0
  600. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +243 -0
  601. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +192 -0
  602. package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +58 -0
  603. package/pack/generated/claude/skills/devrites-lib/reference/standards/browser-proof-checklist.md +9 -0
  604. package/pack/generated/claude/skills/devrites-lib/reference/standards/ci-cd.md +62 -0
  605. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +141 -0
  606. package/pack/generated/claude/skills/devrites-lib/reference/standards/coding-style.md +55 -0
  607. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +114 -0
  608. package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +119 -0
  609. package/pack/generated/claude/skills/devrites-lib/reference/standards/definition-of-done.md +16 -0
  610. package/pack/generated/claude/skills/devrites-lib/reference/standards/deprecation.md +71 -0
  611. package/pack/generated/claude/skills/devrites-lib/reference/standards/developer-experience.md +119 -0
  612. package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md +51 -0
  613. package/pack/generated/claude/skills/devrites-lib/reference/standards/documentation.md +35 -0
  614. package/pack/generated/claude/skills/devrites-lib/reference/standards/edge-case-trace.md +19 -0
  615. package/pack/generated/claude/skills/devrites-lib/reference/standards/elicitation.md +80 -0
  616. package/pack/generated/claude/skills/devrites-lib/reference/standards/error-handling.md +33 -0
  617. package/pack/generated/claude/skills/devrites-lib/reference/standards/git-workflow.md +65 -0
  618. package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +38 -0
  619. package/pack/generated/claude/skills/devrites-lib/reference/standards/observability.md +73 -0
  620. package/pack/generated/claude/skills/devrites-lib/reference/standards/patterns.md +48 -0
  621. package/pack/generated/claude/skills/devrites-lib/reference/standards/performance.md +36 -0
  622. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +158 -0
  623. package/pack/generated/claude/skills/devrites-lib/reference/standards/prose-style.md +117 -0
  624. package/pack/generated/claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +9 -0
  625. package/pack/generated/claude/skills/devrites-lib/reference/standards/review-checklist.md +10 -0
  626. package/pack/generated/claude/skills/devrites-lib/reference/standards/security-checklist.md +9 -0
  627. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +135 -0
  628. package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +105 -0
  629. package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +185 -0
  630. package/pack/generated/claude/skills/devrites-lib/reference/standards/test-proof-checklist.md +9 -0
  631. package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +162 -0
  632. package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +99 -0
  633. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +110 -0
  634. package/pack/generated/claude/skills/devrites-prose-craft/SKILL.md +69 -0
  635. package/pack/generated/claude/skills/devrites-prose-craft/reference/banned-phrases.md +95 -0
  636. package/pack/generated/claude/skills/devrites-prose-craft/reference/examples.md +102 -0
  637. package/pack/generated/claude/skills/devrites-prose-craft/reference/structures.md +134 -0
  638. package/pack/generated/claude/skills/devrites-refresh-indexes/SKILL.md +51 -0
  639. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +52 -0
  640. package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +134 -0
  641. package/pack/generated/claude/skills/devrites-ux-shape/reference/brief-template.md +108 -0
  642. package/pack/generated/claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +48 -0
  643. package/pack/generated/claude/skills/rite/SKILL.md +142 -0
  644. package/pack/generated/claude/skills/rite/reference/menu.md +42 -0
  645. package/pack/generated/claude/skills/rite-adopt/SKILL.md +104 -0
  646. package/pack/generated/claude/skills/rite-adopt/reference/adoption.md +55 -0
  647. package/pack/generated/claude/skills/rite-adopt/reference/anti-patterns.md +19 -0
  648. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +98 -0
  649. package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +35 -0
  650. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +54 -0
  651. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +59 -0
  652. package/pack/generated/claude/skills/rite-build/SKILL.md +84 -0
  653. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +145 -0
  654. package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +37 -0
  655. package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +149 -0
  656. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +44 -0
  657. package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +35 -0
  658. package/pack/generated/claude/skills/rite-build/reference/forge.md +181 -0
  659. package/pack/generated/claude/skills/rite-build/reference/frontend-trigger.md +39 -0
  660. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +42 -0
  661. package/pack/generated/claude/skills/rite-build/reference/output.md +41 -0
  662. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +258 -0
  663. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +43 -0
  664. package/pack/generated/claude/skills/rite-build/reference/tdd.md +26 -0
  665. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +140 -0
  666. package/pack/generated/claude/skills/rite-converge/SKILL.md +131 -0
  667. package/pack/generated/claude/skills/rite-converge/reference/anti-patterns.md +35 -0
  668. package/pack/generated/claude/skills/rite-converge/reference/convergence-assessment.md +61 -0
  669. package/pack/generated/claude/skills/rite-customize/SKILL.md +67 -0
  670. package/pack/generated/claude/skills/rite-define/SKILL.md +159 -0
  671. package/pack/generated/claude/skills/rite-define/reference/anti-patterns.md +26 -0
  672. package/pack/generated/claude/skills/rite-define/reference/gates.md +152 -0
  673. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +94 -0
  674. package/pack/generated/claude/skills/rite-doctor/SKILL.md +76 -0
  675. package/pack/generated/claude/skills/rite-dogfood/SKILL.md +49 -0
  676. package/pack/generated/claude/skills/rite-explain/SKILL.md +148 -0
  677. package/pack/generated/claude/skills/rite-explain/reference/intake.md +79 -0
  678. package/pack/generated/claude/skills/rite-frame/SKILL.md +120 -0
  679. package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +68 -0
  680. package/pack/generated/claude/skills/rite-handoff/SKILL.md +97 -0
  681. package/pack/generated/claude/skills/rite-handoff/reference/handoff-template.md +44 -0
  682. package/pack/generated/claude/skills/rite-learn/SKILL.md +117 -0
  683. package/pack/generated/claude/skills/rite-plan/SKILL.md +95 -0
  684. package/pack/generated/claude/skills/rite-plan/reference/anti-patterns.md +24 -0
  685. package/pack/generated/claude/skills/rite-plan/reference/dependency-graph.md +33 -0
  686. package/pack/generated/claude/skills/rite-plan/reference/replan-and-repair.md +42 -0
  687. package/pack/generated/claude/skills/rite-plan/reference/slicing.md +65 -0
  688. package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +33 -0
  689. package/pack/generated/claude/skills/rite-polish/SKILL.md +87 -0
  690. package/pack/generated/claude/skills/rite-polish/reference/anti-ai-slop.md +206 -0
  691. package/pack/generated/claude/skills/rite-polish/reference/anti-patterns.md +30 -0
  692. package/pack/generated/claude/skills/rite-polish/reference/backend-polish.md +80 -0
  693. package/pack/generated/claude/skills/rite-polish/reference/browser-polish-evidence.md +31 -0
  694. package/pack/generated/claude/skills/rite-polish/reference/code.md +100 -0
  695. package/pack/generated/claude/skills/rite-polish/reference/design-system-discovery.md +8 -0
  696. package/pack/generated/claude/skills/rite-polish/reference/harden-checklist.md +109 -0
  697. package/pack/generated/claude/skills/rite-polish/reference/ui.md +136 -0
  698. package/pack/generated/claude/skills/rite-pov/SKILL.md +46 -0
  699. package/pack/generated/claude/skills/rite-pr-feedback/SKILL.md +49 -0
  700. package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +74 -0
  701. package/pack/generated/claude/skills/rite-prototype/SKILL.md +110 -0
  702. package/pack/generated/claude/skills/rite-prove/SKILL.md +104 -0
  703. package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +48 -0
  704. package/pack/generated/claude/skills/rite-prove/reference/anti-patterns.md +25 -0
  705. package/pack/generated/claude/skills/rite-prove/reference/browser-proof.md +49 -0
  706. package/pack/generated/claude/skills/rite-prove/reference/failure-triage.md +25 -0
  707. package/pack/generated/claude/skills/rite-prove/reference/proof-ladder.md +28 -0
  708. package/pack/generated/claude/skills/rite-prove/reference/test-command-discovery.md +30 -0
  709. package/pack/generated/claude/skills/rite-quick/SKILL.md +93 -0
  710. package/pack/generated/claude/skills/rite-resolve/SKILL.md +109 -0
  711. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +115 -0
  712. package/pack/generated/claude/skills/rite-review/SKILL.md +171 -0
  713. package/pack/generated/claude/skills/rite-review/reference/anti-patterns.md +32 -0
  714. package/pack/generated/claude/skills/rite-review/reference/cognitive-load.md +92 -0
  715. package/pack/generated/claude/skills/rite-review/reference/feature-scoped-review.md +26 -0
  716. package/pack/generated/claude/skills/rite-review/reference/five-axis-review.md +60 -0
  717. package/pack/generated/claude/skills/rite-review/reference/nielsen-heuristics.md +130 -0
  718. package/pack/generated/claude/skills/rite-review/reference/performance-checklist.md +80 -0
  719. package/pack/generated/claude/skills/rite-review/reference/performance-review.md +14 -0
  720. package/pack/generated/claude/skills/rite-review/reference/security-review.md +37 -0
  721. package/pack/generated/claude/skills/rite-seal/SKILL.md +101 -0
  722. package/pack/generated/claude/skills/rite-seal/reference/anti-patterns.md +29 -0
  723. package/pack/generated/claude/skills/rite-seal/reference/conventions-ledger.md +60 -0
  724. package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +64 -0
  725. package/pack/generated/claude/skills/rite-seal/reference/go-no-go.md +53 -0
  726. package/pack/generated/claude/skills/rite-seal/reference/output.md +7 -0
  727. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +211 -0
  728. package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +30 -0
  729. package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +36 -0
  730. package/pack/generated/claude/skills/rite-ship/SKILL.md +111 -0
  731. package/pack/generated/claude/skills/rite-ship/reference/adr-promotion.md +9 -0
  732. package/pack/generated/claude/skills/rite-ship/reference/anti-patterns.md +25 -0
  733. package/pack/generated/claude/skills/rite-ship/reference/close-out.md +42 -0
  734. package/pack/generated/claude/skills/rite-ship/reference/design-memory.md +120 -0
  735. package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +73 -0
  736. package/pack/generated/claude/skills/rite-ship/reference/ledger.md +56 -0
  737. package/pack/generated/claude/skills/rite-ship/reference/rollout.md +43 -0
  738. package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +33 -0
  739. package/pack/generated/claude/skills/rite-spec/SKILL.md +219 -0
  740. package/pack/generated/claude/skills/rite-spec/reference/acceptance-criteria.md +31 -0
  741. package/pack/generated/claude/skills/rite-spec/reference/ai-spec-template.md +33 -0
  742. package/pack/generated/claude/skills/rite-spec/reference/anti-patterns.md +28 -0
  743. package/pack/generated/claude/skills/rite-spec/reference/interview-patterns.md +56 -0
  744. package/pack/generated/claude/skills/rite-spec/reference/investigation.md +84 -0
  745. package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +31 -0
  746. package/pack/generated/claude/skills/rite-spec/reference/references-intake.md +62 -0
  747. package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +73 -0
  748. package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +110 -0
  749. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +156 -0
  750. package/pack/generated/claude/skills/rite-status/SKILL.md +107 -0
  751. package/pack/generated/claude/skills/rite-temper/SKILL.md +126 -0
  752. package/pack/generated/claude/skills/rite-temper/reference/anti-patterns.md +29 -0
  753. package/pack/generated/claude/skills/rite-temper/reference/review-dimensions.md +65 -0
  754. package/pack/generated/claude/skills/rite-temper/reference/scope-modes.md +53 -0
  755. package/pack/generated/claude/skills/rite-temper/reference/significance.md +46 -0
  756. package/pack/generated/claude/skills/rite-temper/reference/strategy-template.md +90 -0
  757. package/pack/generated/claude/skills/rite-vet/SKILL.md +180 -0
  758. package/pack/generated/claude/skills/rite-vet/reference/anti-patterns.md +29 -0
  759. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +135 -0
  760. package/pack/generated/claude/skills/rite-vet/reference/cross-model.md +43 -0
  761. package/pack/generated/claude/skills/rite-vet/reference/depth.md +53 -0
  762. package/pack/generated/claude/skills/rite-vet/reference/eng-lenses.md +48 -0
  763. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +167 -0
  764. package/pack/generated/claude/skills/rite-zoom-out/SKILL.md +75 -0
  765. package/pack/generated/codex/AGENTS.md +23 -0
  766. package/pack/generated/codex/agents/devrites-code-reviewer.toml +101 -0
  767. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +102 -0
  768. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +66 -0
  769. package/pack/generated/codex/agents/devrites-forge-judge.toml +100 -0
  770. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +80 -0
  771. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +109 -0
  772. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +93 -0
  773. package/pack/generated/codex/agents/devrites-retrospector.toml +94 -0
  774. package/pack/generated/codex/agents/devrites-security-auditor.toml +83 -0
  775. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +88 -0
  776. package/pack/generated/codex/agents/devrites-slice-wright.toml +207 -0
  777. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +85 -0
  778. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +76 -0
  779. package/pack/generated/codex/agents/devrites-test-analyst.toml +62 -0
  780. package/pack/generated/codex/hooks.json +151 -0
  781. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +73 -0
  782. package/pack/generated/codex/skills/devrites-api-interface/agents/openai.yaml +2 -0
  783. package/pack/generated/codex/skills/devrites-audit/SKILL.md +85 -0
  784. package/pack/generated/codex/skills/devrites-audit/agents/openai.yaml +2 -0
  785. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +88 -0
  786. package/pack/generated/codex/skills/devrites-browser-proof/agents/openai.yaml +2 -0
  787. package/pack/generated/codex/skills/devrites-browser-proof/reference/browser-performance.md +15 -0
  788. package/pack/generated/codex/skills/devrites-browser-proof/reference/visual-verdict.md +34 -0
  789. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +77 -0
  790. package/pack/generated/codex/skills/devrites-debug-recovery/agents/openai.yaml +2 -0
  791. package/pack/generated/codex/skills/devrites-debug-recovery/reference/build-the-loop.md +58 -0
  792. package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +17 -0
  793. package/pack/generated/codex/skills/devrites-debug-recovery/reference/hypotheses.md +17 -0
  794. package/pack/generated/codex/skills/devrites-debug-recovery/reference/instrumentation.md +21 -0
  795. package/pack/generated/codex/skills/devrites-debug-recovery/reference/regression-test.md +30 -0
  796. package/pack/generated/codex/skills/devrites-debug-recovery/reference/trace.md +25 -0
  797. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +94 -0
  798. package/pack/generated/codex/skills/devrites-doubt/agents/openai.yaml +2 -0
  799. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +111 -0
  800. package/pack/generated/codex/skills/devrites-frontend-craft/agents/openai.yaml +2 -0
  801. package/pack/generated/codex/skills/devrites-frontend-craft/reference/craft.md +71 -0
  802. package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +116 -0
  803. package/pack/generated/codex/skills/devrites-frontend-craft/reference/fullstack.md +45 -0
  804. package/pack/generated/codex/skills/devrites-frontend-craft/reference/quality-standards.md +234 -0
  805. package/pack/generated/codex/skills/devrites-frontend-craft/reference/reuse-first.md +59 -0
  806. package/pack/generated/codex/skills/devrites-frontend-craft/reference/shape.md +60 -0
  807. package/pack/generated/codex/skills/devrites-interview/SKILL.md +116 -0
  808. package/pack/generated/codex/skills/devrites-interview/agents/openai.yaml +2 -0
  809. package/pack/generated/codex/skills/devrites-lib/SKILL.md +94 -0
  810. package/pack/generated/codex/skills/devrites-lib/agents/openai.yaml +2 -0
  811. package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +16 -0
  812. package/pack/generated/codex/skills/devrites-lib/reference/model-tiers.md +43 -0
  813. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +165 -0
  814. package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +111 -0
  815. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +61 -0
  816. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +243 -0
  817. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +192 -0
  818. package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +58 -0
  819. package/pack/generated/codex/skills/devrites-lib/reference/standards/browser-proof-checklist.md +9 -0
  820. package/pack/generated/codex/skills/devrites-lib/reference/standards/ci-cd.md +62 -0
  821. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +141 -0
  822. package/pack/generated/codex/skills/devrites-lib/reference/standards/coding-style.md +55 -0
  823. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +114 -0
  824. package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +119 -0
  825. package/pack/generated/codex/skills/devrites-lib/reference/standards/definition-of-done.md +16 -0
  826. package/pack/generated/codex/skills/devrites-lib/reference/standards/deprecation.md +71 -0
  827. package/pack/generated/codex/skills/devrites-lib/reference/standards/developer-experience.md +119 -0
  828. package/pack/generated/codex/skills/devrites-lib/reference/standards/development-workflow.md +51 -0
  829. package/pack/generated/codex/skills/devrites-lib/reference/standards/documentation.md +35 -0
  830. package/pack/generated/codex/skills/devrites-lib/reference/standards/edge-case-trace.md +19 -0
  831. package/pack/generated/codex/skills/devrites-lib/reference/standards/elicitation.md +80 -0
  832. package/pack/generated/codex/skills/devrites-lib/reference/standards/error-handling.md +33 -0
  833. package/pack/generated/codex/skills/devrites-lib/reference/standards/git-workflow.md +65 -0
  834. package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +38 -0
  835. package/pack/generated/codex/skills/devrites-lib/reference/standards/observability.md +73 -0
  836. package/pack/generated/codex/skills/devrites-lib/reference/standards/patterns.md +48 -0
  837. package/pack/generated/codex/skills/devrites-lib/reference/standards/performance.md +36 -0
  838. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +158 -0
  839. package/pack/generated/codex/skills/devrites-lib/reference/standards/prose-style.md +117 -0
  840. package/pack/generated/codex/skills/devrites-lib/reference/standards/release/ship-checklist.md +9 -0
  841. package/pack/generated/codex/skills/devrites-lib/reference/standards/review-checklist.md +10 -0
  842. package/pack/generated/codex/skills/devrites-lib/reference/standards/security-checklist.md +9 -0
  843. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +135 -0
  844. package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +105 -0
  845. package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +185 -0
  846. package/pack/generated/codex/skills/devrites-lib/reference/standards/test-proof-checklist.md +9 -0
  847. package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +162 -0
  848. package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +99 -0
  849. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +110 -0
  850. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +81 -0
  851. package/pack/generated/codex/skills/devrites-prose-craft/agents/openai.yaml +2 -0
  852. package/pack/generated/codex/skills/devrites-prose-craft/reference/banned-phrases.md +95 -0
  853. package/pack/generated/codex/skills/devrites-prose-craft/reference/examples.md +102 -0
  854. package/pack/generated/codex/skills/devrites-prose-craft/reference/structures.md +134 -0
  855. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +63 -0
  856. package/pack/generated/codex/skills/devrites-refresh-indexes/agents/openai.yaml +2 -0
  857. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +64 -0
  858. package/pack/generated/codex/skills/devrites-source-driven/agents/openai.yaml +2 -0
  859. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +146 -0
  860. package/pack/generated/codex/skills/devrites-ux-shape/agents/openai.yaml +2 -0
  861. package/pack/generated/codex/skills/devrites-ux-shape/reference/brief-template.md +108 -0
  862. package/pack/generated/codex/skills/devrites-ux-shape/reference/visual-direction-probe.md +48 -0
  863. package/pack/generated/codex/skills/rite/SKILL.md +154 -0
  864. package/pack/generated/codex/skills/rite/agents/openai.yaml +2 -0
  865. package/pack/generated/codex/skills/rite/reference/menu.md +42 -0
  866. package/pack/generated/codex/skills/rite-adopt/SKILL.md +116 -0
  867. package/pack/generated/codex/skills/rite-adopt/reference/adoption.md +55 -0
  868. package/pack/generated/codex/skills/rite-adopt/reference/anti-patterns.md +19 -0
  869. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +110 -0
  870. package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +35 -0
  871. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +54 -0
  872. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +59 -0
  873. package/pack/generated/codex/skills/rite-build/SKILL.md +96 -0
  874. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +145 -0
  875. package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +37 -0
  876. package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +149 -0
  877. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +44 -0
  878. package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +35 -0
  879. package/pack/generated/codex/skills/rite-build/reference/forge.md +181 -0
  880. package/pack/generated/codex/skills/rite-build/reference/frontend-trigger.md +39 -0
  881. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +42 -0
  882. package/pack/generated/codex/skills/rite-build/reference/output.md +41 -0
  883. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +258 -0
  884. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +43 -0
  885. package/pack/generated/codex/skills/rite-build/reference/tdd.md +26 -0
  886. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +140 -0
  887. package/pack/generated/codex/skills/rite-converge/SKILL.md +143 -0
  888. package/pack/generated/codex/skills/rite-converge/reference/anti-patterns.md +35 -0
  889. package/pack/generated/codex/skills/rite-converge/reference/convergence-assessment.md +61 -0
  890. package/pack/generated/codex/skills/rite-customize/SKILL.md +79 -0
  891. package/pack/generated/codex/skills/rite-customize/agents/openai.yaml +2 -0
  892. package/pack/generated/codex/skills/rite-define/SKILL.md +171 -0
  893. package/pack/generated/codex/skills/rite-define/reference/anti-patterns.md +26 -0
  894. package/pack/generated/codex/skills/rite-define/reference/gates.md +152 -0
  895. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +94 -0
  896. package/pack/generated/codex/skills/rite-doctor/SKILL.md +88 -0
  897. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +61 -0
  898. package/pack/generated/codex/skills/rite-dogfood/agents/openai.yaml +2 -0
  899. package/pack/generated/codex/skills/rite-explain/SKILL.md +160 -0
  900. package/pack/generated/codex/skills/rite-explain/agents/openai.yaml +2 -0
  901. package/pack/generated/codex/skills/rite-explain/reference/intake.md +79 -0
  902. package/pack/generated/codex/skills/rite-frame/SKILL.md +132 -0
  903. package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +68 -0
  904. package/pack/generated/codex/skills/rite-handoff/SKILL.md +109 -0
  905. package/pack/generated/codex/skills/rite-handoff/agents/openai.yaml +2 -0
  906. package/pack/generated/codex/skills/rite-handoff/reference/handoff-template.md +44 -0
  907. package/pack/generated/codex/skills/rite-learn/SKILL.md +129 -0
  908. package/pack/generated/codex/skills/rite-learn/agents/openai.yaml +2 -0
  909. package/pack/generated/codex/skills/rite-plan/SKILL.md +107 -0
  910. package/pack/generated/codex/skills/rite-plan/reference/anti-patterns.md +24 -0
  911. package/pack/generated/codex/skills/rite-plan/reference/dependency-graph.md +33 -0
  912. package/pack/generated/codex/skills/rite-plan/reference/replan-and-repair.md +42 -0
  913. package/pack/generated/codex/skills/rite-plan/reference/slicing.md +65 -0
  914. package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +33 -0
  915. package/pack/generated/codex/skills/rite-polish/SKILL.md +99 -0
  916. package/pack/generated/codex/skills/rite-polish/reference/anti-ai-slop.md +206 -0
  917. package/pack/generated/codex/skills/rite-polish/reference/anti-patterns.md +30 -0
  918. package/pack/generated/codex/skills/rite-polish/reference/backend-polish.md +80 -0
  919. package/pack/generated/codex/skills/rite-polish/reference/browser-polish-evidence.md +31 -0
  920. package/pack/generated/codex/skills/rite-polish/reference/code.md +100 -0
  921. package/pack/generated/codex/skills/rite-polish/reference/design-system-discovery.md +8 -0
  922. package/pack/generated/codex/skills/rite-polish/reference/harden-checklist.md +109 -0
  923. package/pack/generated/codex/skills/rite-polish/reference/ui.md +136 -0
  924. package/pack/generated/codex/skills/rite-pov/SKILL.md +58 -0
  925. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +61 -0
  926. package/pack/generated/codex/skills/rite-pr-feedback/agents/openai.yaml +2 -0
  927. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +86 -0
  928. package/pack/generated/codex/skills/rite-prototype/SKILL.md +122 -0
  929. package/pack/generated/codex/skills/rite-prototype/agents/openai.yaml +2 -0
  930. package/pack/generated/codex/skills/rite-prove/SKILL.md +116 -0
  931. package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +48 -0
  932. package/pack/generated/codex/skills/rite-prove/reference/anti-patterns.md +25 -0
  933. package/pack/generated/codex/skills/rite-prove/reference/browser-proof.md +49 -0
  934. package/pack/generated/codex/skills/rite-prove/reference/failure-triage.md +25 -0
  935. package/pack/generated/codex/skills/rite-prove/reference/proof-ladder.md +28 -0
  936. package/pack/generated/codex/skills/rite-prove/reference/test-command-discovery.md +30 -0
  937. package/pack/generated/codex/skills/rite-quick/SKILL.md +105 -0
  938. package/pack/generated/codex/skills/rite-resolve/SKILL.md +121 -0
  939. package/pack/generated/codex/skills/rite-resolve/agents/openai.yaml +2 -0
  940. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +115 -0
  941. package/pack/generated/codex/skills/rite-review/SKILL.md +183 -0
  942. package/pack/generated/codex/skills/rite-review/reference/anti-patterns.md +32 -0
  943. package/pack/generated/codex/skills/rite-review/reference/cognitive-load.md +92 -0
  944. package/pack/generated/codex/skills/rite-review/reference/feature-scoped-review.md +26 -0
  945. package/pack/generated/codex/skills/rite-review/reference/five-axis-review.md +60 -0
  946. package/pack/generated/codex/skills/rite-review/reference/nielsen-heuristics.md +130 -0
  947. package/pack/generated/codex/skills/rite-review/reference/performance-checklist.md +80 -0
  948. package/pack/generated/codex/skills/rite-review/reference/performance-review.md +14 -0
  949. package/pack/generated/codex/skills/rite-review/reference/security-review.md +37 -0
  950. package/pack/generated/codex/skills/rite-seal/SKILL.md +113 -0
  951. package/pack/generated/codex/skills/rite-seal/reference/anti-patterns.md +29 -0
  952. package/pack/generated/codex/skills/rite-seal/reference/conventions-ledger.md +60 -0
  953. package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +64 -0
  954. package/pack/generated/codex/skills/rite-seal/reference/go-no-go.md +53 -0
  955. package/pack/generated/codex/skills/rite-seal/reference/output.md +7 -0
  956. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +211 -0
  957. package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +30 -0
  958. package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +36 -0
  959. package/pack/generated/codex/skills/rite-ship/SKILL.md +123 -0
  960. package/pack/generated/codex/skills/rite-ship/reference/adr-promotion.md +9 -0
  961. package/pack/generated/codex/skills/rite-ship/reference/anti-patterns.md +25 -0
  962. package/pack/generated/codex/skills/rite-ship/reference/close-out.md +42 -0
  963. package/pack/generated/codex/skills/rite-ship/reference/design-memory.md +120 -0
  964. package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +73 -0
  965. package/pack/generated/codex/skills/rite-ship/reference/ledger.md +56 -0
  966. package/pack/generated/codex/skills/rite-ship/reference/rollout.md +43 -0
  967. package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +33 -0
  968. package/pack/generated/codex/skills/rite-spec/SKILL.md +231 -0
  969. package/pack/generated/codex/skills/rite-spec/reference/acceptance-criteria.md +31 -0
  970. package/pack/generated/codex/skills/rite-spec/reference/ai-spec-template.md +33 -0
  971. package/pack/generated/codex/skills/rite-spec/reference/anti-patterns.md +28 -0
  972. package/pack/generated/codex/skills/rite-spec/reference/interview-patterns.md +56 -0
  973. package/pack/generated/codex/skills/rite-spec/reference/investigation.md +84 -0
  974. package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +31 -0
  975. package/pack/generated/codex/skills/rite-spec/reference/references-intake.md +62 -0
  976. package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +73 -0
  977. package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +110 -0
  978. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +156 -0
  979. package/pack/generated/codex/skills/rite-status/SKILL.md +119 -0
  980. package/pack/generated/codex/skills/rite-status/agents/openai.yaml +2 -0
  981. package/pack/generated/codex/skills/rite-temper/SKILL.md +138 -0
  982. package/pack/generated/codex/skills/rite-temper/reference/anti-patterns.md +29 -0
  983. package/pack/generated/codex/skills/rite-temper/reference/review-dimensions.md +65 -0
  984. package/pack/generated/codex/skills/rite-temper/reference/scope-modes.md +53 -0
  985. package/pack/generated/codex/skills/rite-temper/reference/significance.md +46 -0
  986. package/pack/generated/codex/skills/rite-temper/reference/strategy-template.md +90 -0
  987. package/pack/generated/codex/skills/rite-vet/SKILL.md +192 -0
  988. package/pack/generated/codex/skills/rite-vet/reference/anti-patterns.md +29 -0
  989. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +135 -0
  990. package/pack/generated/codex/skills/rite-vet/reference/cross-model.md +43 -0
  991. package/pack/generated/codex/skills/rite-vet/reference/depth.md +53 -0
  992. package/pack/generated/codex/skills/rite-vet/reference/eng-lenses.md +48 -0
  993. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +167 -0
  994. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +87 -0
  995. package/pack/generated/codex/skills/rite-zoom-out/agents/openai.yaml +2 -0
  996. package/package.json +8 -4
  997. package/scripts/build-binaries.sh +75 -0
  998. package/scripts/build-host-artifacts.sh +100 -0
  999. package/scripts/build-release-tarball.sh +16 -2
  1000. package/scripts/check-cross-refs.py +17 -3
  1001. package/scripts/check-generated-skill-budget.mjs +41 -0
  1002. package/scripts/check-instruction-size-baseline.mjs +79 -0
  1003. package/scripts/check-invocation-integrity.py +113 -0
  1004. package/scripts/check-no-global-writes.sh +22 -0
  1005. package/scripts/check-no-personal-paths.py +50 -0
  1006. package/scripts/check-reference-governance.mjs +82 -0
  1007. package/scripts/check-reply-contract.sh +64 -0
  1008. package/scripts/check-rule-uniqueness.sh +4 -32
  1009. package/scripts/codex-generate.sh +300 -0
  1010. package/scripts/devrites-detect.sh +2 -1
  1011. package/scripts/eval-runner.py +4 -2
  1012. package/scripts/eval-scorer.mjs +46 -0
  1013. package/scripts/grade-feature.sh +24 -7
  1014. package/scripts/install-lib.sh +112 -19
  1015. package/scripts/pin.sh +4 -4
  1016. package/scripts/release-check.sh +71 -0
  1017. package/scripts/run-behavioral-evals.sh +34 -2
  1018. package/scripts/run-evals.sh +45 -30
  1019. package/scripts/run-live-behavioral-evals.py +180 -0
  1020. package/scripts/run-outcome-evals.sh +27 -0
  1021. package/scripts/run-routing-evals.py +342 -0
  1022. package/scripts/run-tests.mjs +203 -0
  1023. package/scripts/skill-pruning-audit.mjs +120 -0
  1024. package/scripts/skills-inventory.mjs +107 -0
  1025. package/scripts/validate-agent-composition.py +57 -0
  1026. package/scripts/validate-command-parity.py +70 -0
  1027. package/scripts/validate-frontmatter.py +43 -7
  1028. package/scripts/validate-skill-anatomy.py +139 -0
  1029. package/scripts/validate-workflow-security.py +22 -3
  1030. package/scripts/validate-workspace-schema.py +512 -0
  1031. package/scripts/validate.sh +174 -34
  1032. package/uninstall.sh +22 -183
  1033. package/update.sh +29 -185
  1034. package/docs/cli-mcp.md +0 -62
  1035. package/mcp/devrites-mcp.mjs +0 -105
  1036. package/pack/.claude/hooks/devrites-a1-guard.sh +0 -99
  1037. package/pack/.claude/hooks/devrites-allow.sh +0 -44
  1038. package/pack/.claude/hooks/devrites-cursor.sh +0 -28
  1039. package/pack/.claude/hooks/devrites-orient.sh +0 -53
  1040. package/pack/.claude/hooks/devrites-redwatch.sh +0 -39
  1041. package/pack/.claude/hooks/devrites-refresh-indexes.sh +0 -127
  1042. package/pack/.claude/hooks/devrites-reviewer-readonly.sh +0 -37
  1043. package/pack/.claude/hooks/devrites-statusline.sh +0 -18
  1044. package/pack/.claude/hooks/devrites-stop-gate.sh +0 -45
  1045. package/pack/.claude/hooks/devrites-wright-scope.sh +0 -64
  1046. package/pack/.claude/hooks/hooks.json +0 -52
  1047. package/pack/.claude/rules/code-review.md +0 -48
  1048. package/pack/.claude/rules/development-workflow.md +0 -40
  1049. package/pack/.claude/rules/git-workflow.md +0 -35
  1050. package/pack/.claude/rules/spec-grammar.md +0 -106
  1051. package/pack/.claude/rules/testing.md +0 -88
  1052. package/pack/.claude/skills/devrites-lib/scripts/analyze.sh +0 -78
  1053. package/pack/.claude/skills/devrites-lib/scripts/check-acceptance.sh +0 -75
  1054. package/pack/.claude/skills/devrites-lib/scripts/close-out.sh +0 -47
  1055. package/pack/.claude/skills/devrites-lib/scripts/conventions.py +0 -273
  1056. package/pack/.claude/skills/devrites-lib/scripts/coverage.sh +0 -51
  1057. package/pack/.claude/skills/devrites-lib/scripts/devrites.sh +0 -77
  1058. package/pack/.claude/skills/devrites-lib/scripts/doctor.sh +0 -156
  1059. package/pack/.claude/skills/devrites-lib/scripts/doubt-coverage.sh +0 -59
  1060. package/pack/.claude/skills/devrites-lib/scripts/evidence-fresh.sh +0 -63
  1061. package/pack/.claude/skills/devrites-lib/scripts/footprint.sh +0 -45
  1062. package/pack/.claude/skills/devrites-lib/scripts/learnings.sh +0 -74
  1063. package/pack/.claude/skills/devrites-lib/scripts/mutation-gate.sh +0 -52
  1064. package/pack/.claude/skills/devrites-lib/scripts/package-existence.sh +0 -68
  1065. package/pack/.claude/skills/devrites-lib/scripts/preamble.sh +0 -76
  1066. package/pack/.claude/skills/devrites-lib/scripts/progress.sh +0 -103
  1067. package/pack/.claude/skills/devrites-lib/scripts/readiness.sh +0 -62
  1068. package/pack/.claude/skills/devrites-lib/scripts/reconcile.sh +0 -123
  1069. package/pack/.claude/skills/devrites-lib/scripts/resolve.sh +0 -279
  1070. package/pack/.claude/skills/devrites-lib/scripts/rites.sh +0 -41
  1071. package/pack/.claude/skills/devrites-lib/scripts/spec-validate.sh +0 -130
  1072. package/pack/.claude/skills/devrites-lib/scripts/stuck.sh +0 -67
  1073. package/pack/.claude/skills/devrites-lib/scripts/test-integrity.sh +0 -87
  1074. package/pack/.claude/skills/devrites-lib/scripts/tick-afk.sh +0 -52
  1075. package/scripts/merge-codex-hooks.mjs +0 -78
  1076. /package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/coding-style.md +0 -0
  1077. /package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/error-handling.md +0 -0
  1078. /package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/hooks.md +0 -0
  1079. /package/pack/.claude/{rules → skills/devrites-lib/reference/standards}/patterns.md +0 -0
@@ -0,0 +1,1560 @@
1
+ package install
2
+
3
+ import (
4
+ "archive/tar"
5
+ "bytes"
6
+ "compress/gzip"
7
+ "context"
8
+ "crypto/sha256"
9
+ "encoding/json"
10
+ "errors"
11
+ "flag"
12
+ "fmt"
13
+ "io"
14
+ "io/fs"
15
+ "os"
16
+ "os/exec"
17
+ "path/filepath"
18
+ "reflect"
19
+ "slices"
20
+ "sort"
21
+ "strings"
22
+ "time"
23
+
24
+ "github.com/devrites/devrites/internal/devritespaths"
25
+ "github.com/devrites/devrites/internal/fsutil"
26
+ "github.com/devrites/devrites/internal/hostpack"
27
+ "github.com/devrites/devrites/internal/iohooks"
28
+ "github.com/devrites/devrites/internal/version"
29
+ )
30
+
31
+ const ManifestName = devritespaths.ManifestName
32
+ const defaultRepo = "ViktorsBaikers/DevRites"
33
+
34
+ type Mode string
35
+
36
+ const (
37
+ ModeInstall Mode = "install"
38
+ ModeUpdate Mode = "update"
39
+ ModeUninstall Mode = "uninstall"
40
+ )
41
+
42
+ type Options struct {
43
+ Mode Mode
44
+ Target string
45
+ PayloadDir string
46
+ SourceDir string
47
+ DryRun bool
48
+ Force bool
49
+ WithSkills bool
50
+ WithAgents bool
51
+ WithCodex bool
52
+ WithBinary bool
53
+ KeepBinary bool
54
+ AliasMode string
55
+ UpdateCheck bool
56
+ UpdateTo string
57
+ AllowPre bool
58
+ Stdout io.Writer
59
+ Stderr io.Writer
60
+ }
61
+
62
+ type stats struct {
63
+ installed int
64
+ overwrote int
65
+ skipped int
66
+ pruned int
67
+ removed int
68
+ missing int
69
+ }
70
+
71
+ type runner struct {
72
+ opts Options
73
+ target string
74
+ payload string
75
+ payloadFS fs.FS
76
+ source string
77
+ manifest []string
78
+ records map[string]string
79
+ prev map[string]bool
80
+ stats stats
81
+ }
82
+
83
+ func DefaultOptions(mode Mode) Options {
84
+ return Options{
85
+ Mode: mode,
86
+ Target: ".",
87
+ WithSkills: true,
88
+ WithAgents: true,
89
+ WithCodex: true,
90
+ WithBinary: true,
91
+ AliasMode: "safe",
92
+ Stdout: io.Discard,
93
+ Stderr: io.Discard,
94
+ }
95
+ }
96
+
97
+ func Run(args []string, stdout, stderr io.Writer, mode Mode) int {
98
+ opts := DefaultOptions(mode)
99
+ opts.Stdout = stdout
100
+ opts.Stderr = stderr
101
+ if err := parseArgs(args, &opts); err != nil {
102
+ if errors.Is(err, errHelp) {
103
+ fmt.Fprint(stdout, usage(mode))
104
+ return 0
105
+ }
106
+ fmt.Fprintf(stderr, "devrites: %v\n", err)
107
+ return 2
108
+ }
109
+ if err := Apply(opts); err != nil {
110
+ fmt.Fprintf(stderr, "devrites: %v\n", err)
111
+ return 1
112
+ }
113
+ return 0
114
+ }
115
+
116
+ var errHelp = errors.New("help")
117
+
118
+ func parseArgs(args []string, opts *Options) error {
119
+ normalized := make([]string, 0, len(args))
120
+ shortAliasesWithoutValue := false
121
+ for _, arg := range args {
122
+ // flag treats -short-aliases and --short-aliases alike; without this
123
+ // rewrite a bare occurrence would consume the next argument as its value.
124
+ if arg == "--short-aliases" || arg == "-short-aliases" {
125
+ normalized = append(normalized, "--short-aliases=")
126
+ shortAliasesWithoutValue = true
127
+ continue
128
+ }
129
+ normalized = append(normalized, arg)
130
+ }
131
+
132
+ flags := flag.NewFlagSet("devrites-engine "+string(opts.Mode), flag.ContinueOnError)
133
+ flags.SetOutput(io.Discard)
134
+ target := flags.String("target", opts.Target, "")
135
+ payloadDir := flags.String("payload-dir", opts.PayloadDir, "")
136
+ sourceDir := flags.String("source-dir", opts.SourceDir, "")
137
+ dryRun := flags.Bool("dry-run", opts.DryRun, "")
138
+ force := flags.Bool("force", opts.Force, "")
139
+ noCodex := flags.Bool("no-codex", !opts.WithCodex, "")
140
+ noAgents := flags.Bool("no-agents", !opts.WithAgents, "")
141
+ noSkills := flags.Bool("no-skills", !opts.WithSkills, "")
142
+ noBinary := flags.Bool("no-binary", !opts.WithBinary, "")
143
+ keepBinary := flags.Bool("keep-binary", opts.KeepBinary, "")
144
+ noRules := flags.Bool("no-rules", false, "")
145
+ rulesOnly := flags.Bool("rules-only", false, "")
146
+ noShortAliases := flags.Bool("no-short-aliases", false, "")
147
+ shortAliases := flags.String("short-aliases", opts.AliasMode, "")
148
+ updateCheck := flags.Bool("check", opts.UpdateCheck, "")
149
+ allowPre := flags.Bool("pre", opts.AllowPre, "")
150
+ updateTo := flags.String("to", opts.UpdateTo, "")
151
+
152
+ if err := flags.Parse(normalized); err != nil {
153
+ if errors.Is(err, flag.ErrHelp) {
154
+ return errHelp
155
+ }
156
+ return fmt.Errorf("parse flags: %w", err)
157
+ }
158
+ if flags.NArg() > 0 {
159
+ return fmt.Errorf("unknown option: %s (try --help)", flags.Arg(0))
160
+ }
161
+
162
+ opts.Target = *target
163
+ opts.PayloadDir = *payloadDir
164
+ opts.SourceDir = *sourceDir
165
+ opts.DryRun = *dryRun
166
+ opts.Force = *force
167
+ opts.WithCodex = !*noCodex
168
+ opts.WithAgents = !*noAgents
169
+ opts.WithSkills = !*noSkills
170
+ opts.WithBinary = !*noBinary
171
+ opts.KeepBinary = *keepBinary
172
+ opts.UpdateCheck = *updateCheck
173
+ opts.AllowPre = *allowPre
174
+ opts.UpdateTo = *updateTo
175
+ for _, dep := range []struct {
176
+ set bool
177
+ name string
178
+ }{{*noRules, "--no-rules"}, {*rulesOnly, "--rules-only"}} {
179
+ if dep.set {
180
+ fmt.Fprintf(opts.Stderr, "warning: %s is deprecated and now a no-op - DevRites engineering standards ship inside the devrites-lib skill.\n", dep.name)
181
+ }
182
+ }
183
+ if *noShortAliases {
184
+ opts.AliasMode = "off"
185
+ } else if flagWasSet(flags, "short-aliases") {
186
+ switch {
187
+ case *shortAliases == "" && shortAliasesWithoutValue:
188
+ opts.AliasMode = "off"
189
+ fmt.Fprintln(opts.Stderr, "warning: --short-aliases with no value is a no-op; use --short-aliases=all.")
190
+ case *shortAliases == "all":
191
+ opts.AliasMode = "all"
192
+ default:
193
+ return fmt.Errorf("unknown option: --short-aliases=%s (try --help)", *shortAliases)
194
+ }
195
+ }
196
+ return nil
197
+ }
198
+
199
+ func flagWasSet(flags *flag.FlagSet, name string) bool {
200
+ found := false
201
+ flags.Visit(func(f *flag.Flag) {
202
+ if f.Name == name {
203
+ found = true
204
+ }
205
+ })
206
+ return found
207
+ }
208
+
209
+ func usage(mode Mode) string {
210
+ switch mode {
211
+ case ModeUninstall:
212
+ return "usage: devrites-engine uninstall [--target DIR] [--dry-run] [--keep-binary]\n"
213
+ case ModeUpdate:
214
+ return "usage: devrites-engine update [--target DIR] [--dry-run] [--force] [--check] [--to TAG] [install flags]\n"
215
+ default:
216
+ return "usage: devrites-engine install [--target DIR] [--dry-run] [--force] [--no-codex] [--no-agents] [--no-skills] [--no-binary] [--short-aliases=all]\n"
217
+ }
218
+ }
219
+
220
+ func Apply(opts Options) error {
221
+ if opts.Stdout == nil {
222
+ opts.Stdout = io.Discard
223
+ }
224
+ if opts.Stderr == nil {
225
+ opts.Stderr = io.Discard
226
+ }
227
+ if opts.Mode == "" {
228
+ opts.Mode = ModeInstall
229
+ }
230
+ if opts.Mode == ModeUpdate {
231
+ return runUpdate(opts)
232
+ }
233
+ r, err := newRunner(opts)
234
+ if err != nil {
235
+ return fmt.Errorf("prepare installer: %w", err)
236
+ }
237
+ if opts.Mode == ModeUninstall {
238
+ return r.uninstall()
239
+ }
240
+ return r.install()
241
+ }
242
+
243
+ func newRunner(opts Options) (*runner, error) {
244
+ target, err := filepath.Abs(opts.Target)
245
+ if err != nil {
246
+ return nil, fmt.Errorf("resolve target %s: %w", opts.Target, err)
247
+ }
248
+ info, err := os.Stat(target)
249
+ if err != nil {
250
+ return nil, fmt.Errorf("target does not exist: %s", opts.Target)
251
+ }
252
+ if !info.IsDir() {
253
+ return nil, fmt.Errorf("target is not a directory: %s", opts.Target)
254
+ }
255
+ target, err = filepath.EvalSymlinks(target)
256
+ if err != nil {
257
+ return nil, fmt.Errorf("resolve symlinks for %s: %w", target, err)
258
+ }
259
+ if isGlobalTarget(target) {
260
+ return nil, fmt.Errorf("refusing to target a global agent home. DevRites is project-local; choose a project directory")
261
+ }
262
+ source := opts.SourceDir
263
+ if source == "" {
264
+ source = inferSourceDir()
265
+ }
266
+ if source != "" {
267
+ source, _ = filepath.Abs(source)
268
+ }
269
+ payload := opts.PayloadDir
270
+ if payload == "" {
271
+ payload = inferPayloadDir(source)
272
+ }
273
+ if payload != "" {
274
+ payload, _ = filepath.Abs(payload)
275
+ }
276
+ r := &runner{opts: opts, target: target, payload: payload, payloadFS: os.DirFS(payload), source: source, prev: map[string]bool{}, records: map[string]string{}}
277
+ if opts.Mode != ModeUninstall {
278
+ if err := r.validatePayload(); err != nil {
279
+ return nil, fmt.Errorf("validate payload: %w", err)
280
+ }
281
+ }
282
+ r.prev = readManifest(filepath.Join(target, ManifestName))
283
+ return r, nil
284
+ }
285
+
286
+ func inferSourceDir() string {
287
+ if v := os.Getenv("DEVRITES_SOURCE_DIR"); v != "" {
288
+ return v
289
+ }
290
+ exe, err := os.Executable()
291
+ if err == nil {
292
+ dir := filepath.Dir(exe)
293
+ for i := 0; i < 4; i++ {
294
+ if exists(filepath.Join(dir, "pack")) || exists(filepath.Join(dir, "engine")) {
295
+ return dir
296
+ }
297
+ parent := filepath.Dir(dir)
298
+ if parent == dir {
299
+ break
300
+ }
301
+ dir = parent
302
+ }
303
+ }
304
+ return ""
305
+ }
306
+
307
+ func inferPayloadDir(source string) string {
308
+ if v := os.Getenv("DEVRITES_HOST_ARTIFACT_DIR"); v != "" {
309
+ return v
310
+ }
311
+ if source != "" {
312
+ if exists(filepath.Join(source, "pack", "generated")) {
313
+ return filepath.Join(source, "pack", "generated")
314
+ }
315
+ }
316
+ return ""
317
+ }
318
+
319
+ func (r *runner) validatePayload() error {
320
+ if r.payload == "" {
321
+ return fmt.Errorf("generated install payload not found; pass --payload-dir or run scripts/build-host-artifacts.sh")
322
+ }
323
+ if err := hostpack.ValidatePayload(r.payloadFS, r.opts.WithCodex); err != nil {
324
+ return fmt.Errorf("generated install payload under %s: %w", r.payload, err)
325
+ }
326
+ return nil
327
+ }
328
+
329
+ func (r *runner) install() error {
330
+ fmt.Fprintln(r.opts.Stdout, "DevRites installer")
331
+ fmt.Fprintf(r.opts.Stdout, " target : %s\n", r.target)
332
+ fmt.Fprintf(r.opts.Stdout, " payload: %s\n", r.payload)
333
+ fmt.Fprintf(r.opts.Stdout, " skills : %s\n", yesno(r.opts.WithSkills))
334
+ fmt.Fprintln(r.opts.Stdout, " standards: ship inside the devrites-lib skill")
335
+ fmt.Fprintf(r.opts.Stdout, " agents : %s\n", yesno(r.opts.WithAgents))
336
+ fmt.Fprintf(r.opts.Stdout, " codex : %s\n", yesno(r.opts.WithCodex))
337
+ fmt.Fprintf(r.opts.Stdout, " aliases: %s\n", r.opts.AliasMode)
338
+ if r.opts.DryRun {
339
+ fmt.Fprintln(r.opts.Stdout, " (dry run - no changes will be made)")
340
+ }
341
+ fmt.Fprintln(r.opts.Stdout)
342
+
343
+ for _, tree := range hostpack.InstallTrees(r.opts.WithSkills, r.opts.WithAgents, r.opts.WithCodex) {
344
+ if err := r.installTree(tree.PayloadPrefix, tree.TargetPrefix); err != nil {
345
+ return fmt.Errorf("install tree %s: %w", tree.TargetPrefix, err)
346
+ }
347
+ }
348
+ if r.opts.WithSkills && r.opts.AliasMode == "all" {
349
+ for _, alias := range hostpack.Aliases {
350
+ data, err := hostpack.RenderAliasSkill(alias)
351
+ if err != nil {
352
+ return fmt.Errorf("render alias skill %s: %w", alias.Name, err)
353
+ }
354
+ for _, rel := range hostpack.AliasTargets(alias, r.opts.WithCodex) {
355
+ if err := r.installData(data, rel); err != nil {
356
+ return fmt.Errorf("install alias: %w", err)
357
+ }
358
+ }
359
+ }
360
+ }
361
+ if r.opts.WithSkills && r.opts.WithCodex {
362
+ if err := r.mergeMarkerFile(hostpack.CodexAgentsMerge); err != nil {
363
+ return fmt.Errorf("merge %s: %w", hostpack.CodexAgentsMerge.TargetRel, err)
364
+ }
365
+ if err := r.mergeJSONHooks(hostpack.CodexHooksMerge); err != nil {
366
+ return fmt.Errorf("merge %s: %w", hostpack.CodexHooksMerge.TargetRel, err)
367
+ }
368
+ }
369
+ if r.opts.WithSkills {
370
+ if err := r.seedClaudeSettings(); err != nil {
371
+ return fmt.Errorf("seed claude settings: %w", err)
372
+ }
373
+ }
374
+ if err := r.seedDevrites(); err != nil {
375
+ return fmt.Errorf("seed .devrites: %w", err)
376
+ }
377
+ if err := r.pruneDropped(); err != nil {
378
+ return fmt.Errorf("prune dropped files: %w", err)
379
+ }
380
+ if !r.opts.DryRun {
381
+ if err := r.writeManifest(); err != nil {
382
+ return fmt.Errorf("write manifest: %w", err)
383
+ }
384
+ }
385
+ if err := r.installBinary(); err != nil {
386
+ return fmt.Errorf("install engine binary: %w", err)
387
+ }
388
+
389
+ fmt.Fprintln(r.opts.Stdout)
390
+ if r.opts.DryRun {
391
+ fmt.Fprintln(r.opts.Stdout, "DevRites install plan complete (dry run)")
392
+ } else {
393
+ fmt.Fprintln(r.opts.Stdout, "DevRites installed")
394
+ }
395
+ 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)
396
+ return nil
397
+ }
398
+
399
+ func (r *runner) installTree(srcPrefix, relPrefix string) error {
400
+ return fs.WalkDir(r.payloadFS, srcPrefix, func(path string, d fs.DirEntry, err error) error {
401
+ if err != nil {
402
+ return fmt.Errorf("walk payload %s: %w", path, err)
403
+ }
404
+ if d.IsDir() {
405
+ return nil
406
+ }
407
+ rel, err := filepath.Rel(srcPrefix, path)
408
+ if err != nil {
409
+ return fmt.Errorf("resolve relative path for %s: %w", path, err)
410
+ }
411
+ return r.installFile(path, filepath.ToSlash(filepath.Join(relPrefix, rel)))
412
+ })
413
+ }
414
+
415
+ func (r *runner) installFile(src, rel string) error {
416
+ data, err := fs.ReadFile(r.payloadFS, src)
417
+ if err != nil {
418
+ return fmt.Errorf("read payload %s: %w", src, err)
419
+ }
420
+ return r.installData(data, rel)
421
+ }
422
+
423
+ func (r *runner) installData(data []byte, rel string) error {
424
+ dest := filepath.Join(r.target, filepath.FromSlash(rel))
425
+ action := "install"
426
+ if exists(dest) {
427
+ switch {
428
+ case r.prev[rel]:
429
+ action = "overwrite"
430
+ case r.opts.Force:
431
+ action = "overwrite(force)"
432
+ default:
433
+ if r.opts.DryRun {
434
+ fmt.Fprintf(r.opts.Stdout, " [skip] %s (exists, not DevRites-managed)\n", rel)
435
+ } else {
436
+ fmt.Fprintf(r.opts.Stderr, "warning: skip %s (exists, not DevRites-managed; use --force to overwrite)\n", rel)
437
+ }
438
+ r.stats.skipped++
439
+ return nil
440
+ }
441
+ }
442
+ if r.opts.DryRun {
443
+ fmt.Fprintf(r.opts.Stdout, " [%s] %s\n", action, rel)
444
+ } else if err := fsutil.WriteFileAtomic(dest, data, 0o644); err != nil {
445
+ return fmt.Errorf("cannot write %s: %w", rel, err)
446
+ }
447
+ r.addManifest(rel)
448
+ r.addInstallRecord(rel, data)
449
+ if action == "install" {
450
+ r.stats.installed++
451
+ } else {
452
+ r.stats.overwrote++
453
+ }
454
+ return nil
455
+ }
456
+
457
+ func (r *runner) addManifest(rel string) {
458
+ r.manifest = append(r.manifest, filepath.ToSlash(rel))
459
+ }
460
+
461
+ func (r *runner) addInstallRecord(rel string, data []byte) {
462
+ if r.records == nil {
463
+ r.records = map[string]string{}
464
+ }
465
+ sum := sha256.Sum256(data)
466
+ r.records[filepath.ToSlash(rel)] = fmt.Sprintf("sha256:%x", sum[:])
467
+ }
468
+
469
+ func (r *runner) installMarker(rel, text string) error {
470
+ return r.installData([]byte(text+"\n"), rel)
471
+ }
472
+
473
+ func (r *runner) mergeMarkerFile(merge hostpack.MarkerMerge) error {
474
+ block, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
475
+ if err != nil {
476
+ return fmt.Errorf("read payload %s: %w", merge.PayloadRel, err)
477
+ }
478
+ dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
479
+ if r.opts.DryRun {
480
+ verb := "create DevRites block"
481
+ if exists(dest) {
482
+ current, _ := os.ReadFile(dest)
483
+ if bytes.Contains(current, []byte(merge.Begin)) {
484
+ verb = "refresh DevRites block"
485
+ } else {
486
+ verb = "append DevRites block"
487
+ }
488
+ }
489
+ fmt.Fprintf(r.opts.Stdout, " [merge] %s (%s)\n", merge.TargetRel, verb)
490
+ } else {
491
+ next := block
492
+ if current, err := os.ReadFile(dest); err == nil {
493
+ next = hostpack.MergeMarkerBlock(current, block, merge.Begin, merge.End)
494
+ }
495
+ if err := fsutil.WriteFileAtomic(dest, next, 0o644); err != nil {
496
+ return fmt.Errorf("cannot write %s: %w", merge.TargetRel, err)
497
+ }
498
+ }
499
+ return r.installMarker(merge.MarkerRel, merge.MarkerText)
500
+ }
501
+
502
+ func (r *runner) mergeJSONHooks(merge hostpack.JSONMerge) error {
503
+ dest := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
504
+ devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
505
+ if err != nil {
506
+ return fmt.Errorf("load hooks payload: %w", err)
507
+ }
508
+ if r.opts.DryRun {
509
+ fmt.Fprintf(r.opts.Stdout, " [merge] %s\n", merge.DryRunText)
510
+ return r.installMarker(merge.MarkerRel, merge.MarkerText)
511
+ }
512
+ next := devrites
513
+ if current, err := readJSON(dest); err == nil {
514
+ if currentStatus, exists := current["statusLine"]; exists && !isDevritesHookCommand(currentStatus) {
515
+ if _, wanted := devrites["statusLine"]; wanted {
516
+ fmt.Fprintln(r.opts.Stderr, "warning: preserved existing Claude statusLine; DevRites statusline was not installed.")
517
+ }
518
+ }
519
+ next = mergeHooks(stripDevritesHooks(current), devrites)
520
+ }
521
+ data, err := json.MarshalIndent(next, "", " ")
522
+ if err != nil {
523
+ return fmt.Errorf("encode merged hooks: %w", err)
524
+ }
525
+ data = append(data, '\n')
526
+ if err := fsutil.WriteFileAtomic(dest, data, 0o644); err != nil {
527
+ return fmt.Errorf("cannot write %s: %w", merge.TargetRel, err)
528
+ }
529
+ return r.installMarker(merge.MarkerRel, merge.MarkerText)
530
+ }
531
+
532
+ func (r *runner) seedClaudeSettings() error {
533
+ merge := hostpack.ClaudeSettingsMerge
534
+ rel := merge.TargetRel
535
+ dest := filepath.Join(r.target, rel)
536
+ if exists(dest) {
537
+ current, err := readJSON(dest)
538
+ if err != nil {
539
+ return fmt.Errorf("load existing Claude settings: %w", err)
540
+ }
541
+ devrites, err := readJSONFS(r.payloadFS, merge.PayloadRel)
542
+ if err != nil {
543
+ return fmt.Errorf("load Claude settings payload: %w", err)
544
+ }
545
+ managed := r.prev[merge.MarkerRel]
546
+ if reflect.DeepEqual(current, devrites) {
547
+ if managed {
548
+ return r.installMarker(merge.MarkerRel, merge.MarkerText)
549
+ }
550
+ return nil
551
+ }
552
+ if containsDevritesHook(current) && len(stripDevritesHooks(current)) == 0 && !managed {
553
+ if r.opts.DryRun {
554
+ fmt.Fprintln(r.opts.Stdout, " [seed] .claude/settings.json (refresh DevRites hooks)")
555
+ return nil
556
+ }
557
+ data, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
558
+ if err != nil {
559
+ return fmt.Errorf("read payload claude/settings.json: %w", err)
560
+ }
561
+ return fsutil.WriteFileAtomic(dest, data, 0o644)
562
+ }
563
+ return r.mergeJSONHooks(merge)
564
+ }
565
+ if r.opts.DryRun {
566
+ fmt.Fprintln(r.opts.Stdout, " [seed] .claude/settings.json (DevRites hooks - preserved on uninstall/update)")
567
+ return nil
568
+ }
569
+ data, err := fs.ReadFile(r.payloadFS, merge.PayloadRel)
570
+ if err != nil {
571
+ return fmt.Errorf("read payload claude/settings.json: %w", err)
572
+ }
573
+ return fsutil.WriteFileAtomic(dest, data, 0o644)
574
+ }
575
+
576
+ func (r *runner) seedDevrites() error {
577
+ readme, err := hostpack.RenderDevritesReadme()
578
+ if err != nil {
579
+ return fmt.Errorf("render readme: %w", err)
580
+ }
581
+ if err := r.installData(readme, ".devrites/README.md"); err != nil {
582
+ return fmt.Errorf("install readme: %w", err)
583
+ }
584
+ active := filepath.Join(r.target, ".devrites", "ACTIVE")
585
+ if exists(active) {
586
+ return nil
587
+ }
588
+ if r.opts.DryRun {
589
+ fmt.Fprintln(r.opts.Stdout, " [seed] .devrites/ACTIVE (runtime state - preserved on uninstall)")
590
+ return nil
591
+ }
592
+ if err := os.MkdirAll(filepath.Dir(active), 0o755); err != nil {
593
+ return fmt.Errorf("create %s: %w", filepath.Dir(active), err)
594
+ }
595
+ f, err := os.OpenFile(active, os.O_CREATE|os.O_EXCL, 0o644)
596
+ if err != nil {
597
+ if os.IsExist(err) {
598
+ return nil
599
+ }
600
+ return fmt.Errorf("create %s: %w", active, err)
601
+ }
602
+ return f.Close()
603
+ }
604
+
605
+ func (r *runner) pruneDropped() error {
606
+ if len(r.prev) == 0 {
607
+ return nil
608
+ }
609
+ next := map[string]bool{}
610
+ for _, rel := range r.manifest {
611
+ next[rel] = true
612
+ }
613
+ keys := make([]string, 0, len(r.prev))
614
+ for rel := range r.prev {
615
+ keys = append(keys, rel)
616
+ }
617
+ sort.Strings(keys)
618
+ for _, rel := range keys {
619
+ if next[rel] || hostpack.PreserveOnPrune(rel) {
620
+ continue
621
+ }
622
+ dead := filepath.Join(r.target, filepath.FromSlash(rel))
623
+ if !exists(dead) {
624
+ continue
625
+ }
626
+ if r.opts.DryRun {
627
+ fmt.Fprintf(r.opts.Stdout, " [prune] %s (dropped from pack)\n", rel)
628
+ } else {
629
+ if merge, ok := hostpack.ManagedMergeForMarker(rel); ok {
630
+ if merge.TargetRel == hostpack.CodexHooksMerge.TargetRel || merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
631
+ preserveEmpty := merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel
632
+ _ = r.stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), preserveEmpty)
633
+ } else {
634
+ _ = stripMarkerPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), merge.Begin, merge.End)
635
+ }
636
+ }
637
+ if err := os.Remove(dead); err != nil && !os.IsNotExist(err) {
638
+ return fmt.Errorf("remove %s: %w", rel, err)
639
+ }
640
+ pruneEmptyDirs(filepath.Dir(dead), r.target)
641
+ fmt.Fprintf(r.opts.Stdout, " [prune] %s\n", rel)
642
+ }
643
+ r.stats.pruned++
644
+ }
645
+ return nil
646
+ }
647
+
648
+ func (r *runner) writeManifest() error {
649
+ sort.Strings(r.manifest)
650
+ r.manifest = slices.Compact(r.manifest)
651
+ var b strings.Builder
652
+ b.WriteString("# DevRites install manifest - do not edit by hand.\n")
653
+ b.WriteString("# Generated " + time.Now().UTC().Format(time.RFC3339) + ". Uninstall removes exactly these paths.\n")
654
+ b.WriteString("# devrites-version: " + installedVersion(r.source) + "\n")
655
+ b.WriteString("# devrites-flags: " + r.flagsString() + "\n")
656
+ b.WriteString("# managed-records: source=npx payload=pack/generated format=rel sha256\n")
657
+ for _, rel := range r.manifest {
658
+ if hash := r.records[rel]; hash != "" {
659
+ b.WriteString("# managed: " + rel + " " + hash + "\n")
660
+ }
661
+ }
662
+ for _, rel := range r.manifest {
663
+ b.WriteString(rel + "\n")
664
+ }
665
+ return fsutil.WriteFileAtomic(filepath.Join(r.target, ManifestName), []byte(b.String()), 0o644)
666
+ }
667
+
668
+ func (r *runner) flagsString() string {
669
+ var flags []string
670
+ if !r.opts.WithSkills {
671
+ flags = append(flags, "--no-skills")
672
+ }
673
+ if !r.opts.WithAgents {
674
+ flags = append(flags, "--no-agents")
675
+ }
676
+ if !r.opts.WithCodex {
677
+ flags = append(flags, "--no-codex")
678
+ }
679
+ if !r.opts.WithBinary {
680
+ flags = append(flags, "--no-binary")
681
+ }
682
+ if r.opts.AliasMode == "off" {
683
+ flags = append(flags, "--no-short-aliases")
684
+ }
685
+ if r.opts.AliasMode == "all" {
686
+ flags = append(flags, "--short-aliases=all")
687
+ }
688
+ return strings.Join(flags, " ")
689
+ }
690
+
691
+ func (r *runner) uninstall() error {
692
+ mf := filepath.Join(r.target, ManifestName)
693
+ entries := readManifestList(mf)
694
+ if len(entries) == 0 {
695
+ return fmt.Errorf("no DevRites manifest at %s - nothing to uninstall", mf)
696
+ }
697
+ fmt.Fprintln(r.opts.Stdout, "DevRites uninstaller")
698
+ fmt.Fprintf(r.opts.Stdout, " target : %s\n", r.target)
699
+ fmt.Fprintf(r.opts.Stdout, " manifest: %s\n", mf)
700
+ if r.opts.DryRun {
701
+ fmt.Fprintln(r.opts.Stdout, " (dry run - no changes)")
702
+ }
703
+ fmt.Fprintln(r.opts.Stdout)
704
+
705
+ for _, rel := range entries {
706
+ merge, ok := hostpack.ManagedMergeForMarker(rel)
707
+ if !ok {
708
+ continue
709
+ }
710
+ if r.opts.DryRun {
711
+ fmt.Fprintf(r.opts.Stdout, " [merge-remove] %s\n", merge.DryRun)
712
+ continue
713
+ }
714
+ if merge.TargetRel == hostpack.CodexHooksMerge.TargetRel || merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
715
+ preserveEmpty := merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel
716
+ if err := r.stripHooksPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), preserveEmpty); err != nil {
717
+ return fmt.Errorf("strip hooks from %s: %w", merge.TargetRel, err)
718
+ }
719
+ continue
720
+ }
721
+ if err := stripMarkerPath(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), merge.Begin, merge.End); err != nil {
722
+ return fmt.Errorf("strip marker block from %s: %w", merge.TargetRel, err)
723
+ }
724
+ }
725
+
726
+ var dirs []string
727
+ for _, rel := range entries {
728
+ if !hostpack.ShouldRemoveOnUninstall(rel, entries) {
729
+ continue
730
+ }
731
+ dest := filepath.Join(r.target, filepath.FromSlash(rel))
732
+ if exists(dest) {
733
+ if r.opts.DryRun {
734
+ fmt.Fprintf(r.opts.Stdout, " [remove] %s\n", rel)
735
+ } else if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
736
+ return fmt.Errorf("remove %s: %w", rel, err)
737
+ }
738
+ dirs = append(dirs, filepath.Dir(dest))
739
+ r.stats.removed++
740
+ } else {
741
+ r.stats.missing++
742
+ }
743
+ }
744
+ if r.opts.DryRun {
745
+ fmt.Fprintf(r.opts.Stdout, " [remove] %s\n", ManifestName)
746
+ } else {
747
+ _ = os.Remove(mf)
748
+ dirs = append(dirs, filepath.Dir(mf))
749
+ for _, d := range dirs {
750
+ pruneEmptyDirs(d, r.target)
751
+ }
752
+ }
753
+ if err := r.removeBinary(); err != nil {
754
+ return fmt.Errorf("remove engine binary: %w", err)
755
+ }
756
+ fmt.Fprintln(r.opts.Stdout)
757
+ if r.opts.DryRun {
758
+ fmt.Fprintln(r.opts.Stdout, "DevRites uninstall plan complete (dry run)")
759
+ } else {
760
+ fmt.Fprintln(r.opts.Stdout, "DevRites uninstalled")
761
+ }
762
+ fmt.Fprintf(r.opts.Stdout, " removed: %d already-absent: %d\n", r.stats.removed, r.stats.missing)
763
+ if exists(filepath.Join(r.target, ".devrites", "work")) {
764
+ fmt.Fprintln(r.opts.Stdout, " kept .devrites/work/ (your feature data)")
765
+ }
766
+ if exists(filepath.Join(r.target, ".devrites", "ACTIVE")) {
767
+ fmt.Fprintln(r.opts.Stdout, " kept .devrites/ACTIVE (active-feature cursor)")
768
+ }
769
+ return nil
770
+ }
771
+
772
+ func (r *runner) stripHooksPath(path string, preserveEmpty bool) error {
773
+ current, err := readJSON(path)
774
+ if err != nil {
775
+ if errors.Is(err, fs.ErrNotExist) {
776
+ return nil
777
+ }
778
+ return fmt.Errorf("load hooks config: %w", err)
779
+ }
780
+ next := stripDevritesHooks(current)
781
+ if len(next) == 0 && !preserveEmpty {
782
+ return os.Remove(path)
783
+ }
784
+ data, err := json.MarshalIndent(next, "", " ")
785
+ if err != nil {
786
+ return fmt.Errorf("encode hooks config: %w", err)
787
+ }
788
+ data = append(data, '\n')
789
+ return fsutil.WriteFileAtomic(path, data, 0o644)
790
+ }
791
+
792
+ func runUpdate(opts Options) error {
793
+ target, err := filepath.Abs(opts.Target)
794
+ if err != nil {
795
+ return fmt.Errorf("resolve target %s: %w", opts.Target, err)
796
+ }
797
+ mf := filepath.Join(target, ManifestName)
798
+ if !exists(mf) {
799
+ return fmt.Errorf("no DevRites install found at %s (missing %s)", target, ManifestName)
800
+ }
801
+ installed := manifestHeader(mf, "devrites-version")
802
+ if installed == "" {
803
+ installed = "unknown"
804
+ }
805
+ latestTag, err := resolveUpdateTag(opts)
806
+ if err != nil {
807
+ return fmt.Errorf("resolve update tag: %w", err)
808
+ }
809
+ latest := strings.TrimPrefix(latestTag, "v")
810
+ fmt.Fprintln(opts.Stdout, "DevRites update")
811
+ fmt.Fprintf(opts.Stdout, " project: %s\n", target)
812
+ fmt.Fprintf(opts.Stdout, " installed: %s\n", installed)
813
+ fmt.Fprintf(opts.Stdout, " latest: %s\n", latest)
814
+ if opts.UpdateCheck {
815
+ if installed == latest || strings.TrimPrefix(installed, "v") == latest {
816
+ fmt.Fprintln(opts.Stdout, "up to date.")
817
+ return nil
818
+ }
819
+ return fmt.Errorf("update available: %s -> %s", installed, latest)
820
+ }
821
+ if !opts.Force && (installed == latest || strings.TrimPrefix(installed, "v") == latest) {
822
+ fmt.Fprintln(opts.Stdout, "already up to date (use --force to reinstall).")
823
+ return nil
824
+ }
825
+ source, cleanup, err := acquireUpdateBundle(latestTag, opts.Stdout)
826
+ if err != nil {
827
+ return fmt.Errorf("acquire update %s: %w", latestTag, err)
828
+ }
829
+ defer cleanup()
830
+ payload := filepath.Join(source, "pack", "generated")
831
+ if hostpack.ValidatePayload(os.DirFS(payload), true) != nil {
832
+ if err := buildHostArtifacts(source, payload); err != nil {
833
+ return fmt.Errorf("prepare update payload: %w", err)
834
+ }
835
+ }
836
+ installFlags := manifestHeader(mf, "devrites-flags")
837
+ next := DefaultOptions(ModeInstall)
838
+ next.Stdout = opts.Stdout
839
+ next.Stderr = opts.Stderr
840
+ if installFlags != "" {
841
+ if err := parseArgs(strings.Fields(installFlags), &next); err != nil {
842
+ return fmt.Errorf("parse manifest install flags: %w", err)
843
+ }
844
+ }
845
+ next.Target = target
846
+ next.PayloadDir = payload
847
+ next.SourceDir = source
848
+ next.Force = true
849
+ if opts.DryRun {
850
+ next.DryRun = true
851
+ }
852
+ return Apply(next)
853
+ }
854
+
855
+ func resolveUpdateTag(opts Options) (string, error) {
856
+ if opts.UpdateTo != "" {
857
+ return normalizeTag(opts.UpdateTo), nil
858
+ }
859
+ if os.Getenv("DEVRITES_UPDATE_BUNDLE") != "" && opts.SourceDir != "" {
860
+ if v := installedVersion(opts.SourceDir); v != "" && v != "unknown" {
861
+ return normalizeTag(v), nil
862
+ }
863
+ }
864
+ repo := os.Getenv("DEVRITES_REPO")
865
+ if repo == "" {
866
+ repo = defaultRepo
867
+ }
868
+ endpoint := "latest"
869
+ if opts.AllowPre {
870
+ endpoint = ""
871
+ }
872
+ url := fmt.Sprintf("https://api.github.com/repos/%s/releases/%s", repo, endpoint)
873
+ if opts.AllowPre {
874
+ var releases []struct {
875
+ TagName string `json:"tag_name"`
876
+ }
877
+ if err := iohooks.FetchJSON(url, &releases); err != nil {
878
+ return "", fmt.Errorf("resolve latest release: %w", err)
879
+ }
880
+ for _, rel := range releases {
881
+ if rel.TagName != "" {
882
+ return normalizeTag(rel.TagName), nil
883
+ }
884
+ }
885
+ return "", fmt.Errorf("resolve latest release: no releases found")
886
+ }
887
+ var release struct {
888
+ TagName string `json:"tag_name"`
889
+ }
890
+ if err := iohooks.FetchJSON(url, &release); err != nil {
891
+ return "", fmt.Errorf("resolve latest release: %w", err)
892
+ }
893
+ if release.TagName == "" {
894
+ return "", fmt.Errorf("resolve latest release: empty tag")
895
+ }
896
+ return normalizeTag(release.TagName), nil
897
+ }
898
+
899
+ func acquireUpdateBundle(tag string, stdout io.Writer) (string, func(), error) {
900
+ tmp, err := os.MkdirTemp("", "devrites-update-*")
901
+ if err != nil {
902
+ return "", func() {}, fmt.Errorf("create temp dir: %w", err)
903
+ }
904
+ cleanup := func() { _ = os.RemoveAll(tmp) }
905
+ artifact := fmt.Sprintf("devrites-%s.tar.gz", tag)
906
+ tarball := filepath.Join(tmp, artifact)
907
+ if bundle := os.Getenv("DEVRITES_UPDATE_BUNDLE"); bundle != "" {
908
+ if !exists(bundle) {
909
+ cleanup()
910
+ return "", func() {}, fmt.Errorf("DEVRITES_UPDATE_BUNDLE not readable: %s", bundle)
911
+ }
912
+ fmt.Fprintf(stdout, " bundle: %s\n", bundle)
913
+ return extractUpdateBundle(bundle, tmp, cleanup)
914
+ }
915
+
916
+ repo := os.Getenv("DEVRITES_REPO")
917
+ if repo == "" {
918
+ repo = defaultRepo
919
+ }
920
+ releaseURL := fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", repo, tag, artifact)
921
+ if err := iohooks.DownloadFile(releaseURL, tarball); err == nil {
922
+ sumPath := tarball + ".sha256"
923
+ if err := iohooks.DownloadFile(releaseURL+".sha256", sumPath); err != nil {
924
+ cleanup()
925
+ return "", func() {}, fmt.Errorf("could not verify release artifact %s: missing checksum: %w", artifact, err)
926
+ }
927
+ if err := verifySHA256(tarball, sumPath); err != nil {
928
+ cleanup()
929
+ return "", func() {}, fmt.Errorf("could not verify release artifact %s: %w", artifact, err)
930
+ }
931
+ fmt.Fprintf(stdout, " bundle: %s\n", releaseURL)
932
+ return extractUpdateBundle(tarball, tmp, cleanup)
933
+ }
934
+
935
+ sourceURL := fmt.Sprintf("https://github.com/%s/archive/refs/tags/%s.tar.gz", repo, tag)
936
+ sourceTarball := filepath.Join(tmp, "src-"+tag+".tar.gz")
937
+ if err := iohooks.DownloadFile(sourceURL, sourceTarball); err != nil {
938
+ cleanup()
939
+ return "", func() {}, fmt.Errorf("download update bundle: %w", err)
940
+ }
941
+ fmt.Fprintf(stdout, " bundle: %s\n", sourceURL)
942
+ return extractUpdateBundle(sourceTarball, tmp, cleanup)
943
+ }
944
+
945
+ func verifySHA256(path, sumPath string) error {
946
+ data, err := os.ReadFile(sumPath)
947
+ if err != nil {
948
+ return fmt.Errorf("read checksum %s: %w", sumPath, err)
949
+ }
950
+ want := strings.Fields(string(data))
951
+ if len(want) == 0 {
952
+ return fmt.Errorf("empty checksum file")
953
+ }
954
+ body, err := os.ReadFile(path)
955
+ if err != nil {
956
+ return fmt.Errorf("read artifact %s: %w", path, err)
957
+ }
958
+ got := fmt.Sprintf("%x", sha256.Sum256(body))
959
+ if got != want[0] {
960
+ return fmt.Errorf("checksum mismatch: got %s want %s", got, want[0])
961
+ }
962
+ return nil
963
+ }
964
+
965
+ func extractUpdateBundle(tarball, tmp string, cleanup func()) (string, func(), error) {
966
+ if err := extractTarGz(tarball, tmp); err != nil {
967
+ cleanup()
968
+ return "", func() {}, fmt.Errorf("extract update bundle: %w", err)
969
+ }
970
+ entries, err := os.ReadDir(tmp)
971
+ if err != nil {
972
+ cleanup()
973
+ return "", func() {}, fmt.Errorf("list extracted bundle: %w", err)
974
+ }
975
+ for _, entry := range entries {
976
+ if !entry.IsDir() {
977
+ continue
978
+ }
979
+ dir := filepath.Join(tmp, entry.Name())
980
+ if exists(filepath.Join(dir, "install.sh")) {
981
+ return dir, cleanup, nil
982
+ }
983
+ }
984
+ cleanup()
985
+ return "", func() {}, fmt.Errorf("extracted update bundle has no install.sh")
986
+ }
987
+
988
+ const (
989
+ maxUpdateEntries = 4096
990
+ maxUpdateEntryBytes = int64(1) << 30
991
+ maxUpdateTotalBytes = int64(2) << 30
992
+ )
993
+
994
+ func extractTarGz(tarball, dest string) error {
995
+ f, err := os.Open(tarball)
996
+ if err != nil {
997
+ return fmt.Errorf("open update bundle: %w", err)
998
+ }
999
+ defer f.Close()
1000
+ gz, err := gzip.NewReader(f)
1001
+ if err != nil {
1002
+ return fmt.Errorf("decompress %s: %w", tarball, err)
1003
+ }
1004
+ defer gz.Close()
1005
+ tr := tar.NewReader(gz)
1006
+ root, err := filepath.Abs(dest)
1007
+ if err != nil {
1008
+ return fmt.Errorf("resolve %s: %w", dest, err)
1009
+ }
1010
+ entryCount := 0
1011
+ var totalBytes int64
1012
+ for {
1013
+ hdr, err := tr.Next()
1014
+ if errors.Is(err, io.EOF) {
1015
+ return nil
1016
+ }
1017
+ if err != nil {
1018
+ return fmt.Errorf("read tar entry: %w", err)
1019
+ }
1020
+ entryCount++
1021
+ if entryCount > maxUpdateEntries {
1022
+ return fmt.Errorf("update bundle has too many entries (maximum %d)", maxUpdateEntries)
1023
+ }
1024
+ if hdr.Size < 0 {
1025
+ return fmt.Errorf("entry %s in update bundle has a negative size", hdr.Name)
1026
+ }
1027
+ if hdr.Size > maxUpdateEntryBytes {
1028
+ return fmt.Errorf("entry %s in update bundle exceeds %d bytes", hdr.Name, maxUpdateEntryBytes)
1029
+ }
1030
+ if hdr.Size > maxUpdateTotalBytes-totalBytes {
1031
+ return fmt.Errorf("update bundle exceeds %d total bytes", maxUpdateTotalBytes)
1032
+ }
1033
+ totalBytes += hdr.Size
1034
+ name := filepath.Clean(hdr.Name)
1035
+ if name == "." || filepath.IsAbs(name) || strings.HasPrefix(name, ".."+string(os.PathSeparator)) || name == ".." {
1036
+ return fmt.Errorf("unsafe path in update bundle: %s", hdr.Name)
1037
+ }
1038
+ target := filepath.Join(root, name)
1039
+ if !strings.HasPrefix(target, root+string(os.PathSeparator)) && target != root {
1040
+ return fmt.Errorf("unsafe path in update bundle: %s", hdr.Name)
1041
+ }
1042
+ switch hdr.Typeflag {
1043
+ case tar.TypeDir:
1044
+ if err := os.MkdirAll(target, fs.FileMode(hdr.Mode&0o777)); err != nil {
1045
+ return fmt.Errorf("create dir %s: %w", name, err)
1046
+ }
1047
+ case tar.TypeReg:
1048
+ if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
1049
+ return fmt.Errorf("create dir for %s: %w", name, err)
1050
+ }
1051
+ out, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fs.FileMode(hdr.Mode&0o777))
1052
+ if err != nil {
1053
+ return fmt.Errorf("create %s: %w", name, err)
1054
+ }
1055
+ // Keep the streaming cap even after validating the header so malformed
1056
+ // archives cannot make the extractor trust inconsistent metadata.
1057
+ written, copyErr := io.Copy(out, io.LimitReader(tr, maxUpdateEntryBytes+1))
1058
+ closeErr := out.Close()
1059
+ if copyErr != nil {
1060
+ return fmt.Errorf("extract %s: %w", name, copyErr)
1061
+ }
1062
+ if written > maxUpdateEntryBytes {
1063
+ return fmt.Errorf("entry %s in update bundle exceeds %d bytes", hdr.Name, maxUpdateEntryBytes)
1064
+ }
1065
+ if closeErr != nil {
1066
+ return fmt.Errorf("close %s: %w", name, closeErr)
1067
+ }
1068
+ default:
1069
+ continue
1070
+ }
1071
+ }
1072
+ }
1073
+
1074
+ func buildHostArtifacts(source, payload string) error {
1075
+ script := filepath.Join(source, "scripts", "build-host-artifacts.sh")
1076
+ if !exists(script) {
1077
+ return fmt.Errorf("generated install payload missing at %s and no builder found at %s", payload, script)
1078
+ }
1079
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
1080
+ defer cancel()
1081
+ cmd := exec.CommandContext(ctx, "bash", script)
1082
+ cmd.Dir = source
1083
+ cmd.Env = append(os.Environ(), "DEVRITES_HOST_ARTIFACT_DIR="+payload)
1084
+ if out, err := cmd.CombinedOutput(); err != nil {
1085
+ return fmt.Errorf("build generated install payload: %w: %s", err, strings.TrimSpace(string(out)))
1086
+ }
1087
+ return nil
1088
+ }
1089
+
1090
+ func normalizeTag(tag string) string {
1091
+ tag = strings.TrimSpace(tag)
1092
+ if tag == "" || strings.HasPrefix(tag, "v") {
1093
+ return tag
1094
+ }
1095
+ return "v" + tag
1096
+ }
1097
+
1098
+ func stripMarkerPath(path, begin, end string) error {
1099
+ data, err := os.ReadFile(path)
1100
+ if err != nil {
1101
+ if os.IsNotExist(err) {
1102
+ return nil
1103
+ }
1104
+ return fmt.Errorf("read %s: %w", path, err)
1105
+ }
1106
+ var out strings.Builder
1107
+ inBlock := false
1108
+ for _, line := range strings.SplitAfter(string(data), "\n") {
1109
+ trim := strings.TrimSuffix(line, "\n")
1110
+ trim = strings.TrimSuffix(trim, "\r")
1111
+ switch trim {
1112
+ case begin:
1113
+ inBlock = true
1114
+ continue
1115
+ case end:
1116
+ inBlock = false
1117
+ continue
1118
+ }
1119
+ if !inBlock {
1120
+ out.WriteString(line)
1121
+ }
1122
+ }
1123
+ if strings.TrimSpace(out.String()) == "" {
1124
+ return os.Remove(path)
1125
+ }
1126
+ return fsutil.WriteFileAtomic(path, []byte(out.String()), 0o644)
1127
+ }
1128
+
1129
+ func readJSON(path string) (map[string]any, error) {
1130
+ data, err := os.ReadFile(path)
1131
+ if err != nil {
1132
+ return nil, fmt.Errorf("read %s: %w", path, err)
1133
+ }
1134
+ return decodeJSON(data)
1135
+ }
1136
+
1137
+ func readJSONFS(src fs.FS, path string) (map[string]any, error) {
1138
+ data, err := fs.ReadFile(src, path)
1139
+ if err != nil {
1140
+ return nil, fmt.Errorf("read %s: %w", path, err)
1141
+ }
1142
+ return decodeJSON(data)
1143
+ }
1144
+
1145
+ func decodeJSON(data []byte) (map[string]any, error) {
1146
+ var out map[string]any
1147
+ if err := json.Unmarshal(data, &out); err != nil {
1148
+ return nil, fmt.Errorf("decode JSON: %w", err)
1149
+ }
1150
+ return out, nil
1151
+ }
1152
+
1153
+ func isDevritesHookCommand(v any) bool {
1154
+ hook, ok := v.(map[string]any)
1155
+ if !ok {
1156
+ return false
1157
+ }
1158
+ command, _ := hook["command"].(string)
1159
+ return strings.Contains(command, "devrites-engine hook ") ||
1160
+ strings.Contains(command, ".claude/hooks/devrites-") ||
1161
+ strings.Contains(command, ".codex/hooks/devrites-") ||
1162
+ (strings.Contains(command, "printf ") && strings.Contains(command, "DevRites:"))
1163
+ }
1164
+
1165
+ func containsDevritesHook(v any) bool {
1166
+ switch value := v.(type) {
1167
+ case map[string]any:
1168
+ if isDevritesHookCommand(value) {
1169
+ return true
1170
+ }
1171
+ for _, child := range value {
1172
+ if containsDevritesHook(child) {
1173
+ return true
1174
+ }
1175
+ }
1176
+ case []any:
1177
+ for _, child := range value {
1178
+ if containsDevritesHook(child) {
1179
+ return true
1180
+ }
1181
+ }
1182
+ }
1183
+ return false
1184
+ }
1185
+
1186
+ func isDevritesHooksComment(comment string) bool {
1187
+ return comment == "DevRites hooks" ||
1188
+ strings.HasPrefix(comment, "DevRites hooks — every event invokes the global `devrites-engine` engine binary") ||
1189
+ strings.HasPrefix(comment, "DevRites hooks — auto-approve the read-only orientation/gate scripts") ||
1190
+ strings.HasPrefix(comment, "DevRites hooks for Codex. Project hooks load only after")
1191
+ }
1192
+
1193
+ func stripDevritesHooks(config map[string]any) map[string]any {
1194
+ next := map[string]any{}
1195
+ for k, v := range config {
1196
+ if k == "$comment" {
1197
+ if s, ok := v.(string); ok && isDevritesHooksComment(s) {
1198
+ continue
1199
+ }
1200
+ }
1201
+ if k == "statusLine" && isDevritesHookCommand(v) {
1202
+ continue
1203
+ }
1204
+ next[k] = v
1205
+ }
1206
+ rawHooks, ok := next["hooks"].(map[string]any)
1207
+ if !ok {
1208
+ delete(next, "hooks")
1209
+ return next
1210
+ }
1211
+ hooks := map[string]any{}
1212
+ for event, entries := range rawHooks {
1213
+ arr, ok := entries.([]any)
1214
+ if !ok {
1215
+ hooks[event] = entries
1216
+ continue
1217
+ }
1218
+ var kept []any
1219
+ for _, entry := range arr {
1220
+ entryMap, ok := entry.(map[string]any)
1221
+ if !ok {
1222
+ kept = append(kept, entry)
1223
+ continue
1224
+ }
1225
+ commands, ok := entryMap["hooks"].([]any)
1226
+ if !ok {
1227
+ kept = append(kept, entry)
1228
+ continue
1229
+ }
1230
+ var keptCommands []any
1231
+ for _, command := range commands {
1232
+ if !isDevritesHookCommand(command) {
1233
+ keptCommands = append(keptCommands, command)
1234
+ }
1235
+ }
1236
+ if len(keptCommands) > 0 {
1237
+ nextEntry := make(map[string]any, len(entryMap))
1238
+ for k, v := range entryMap {
1239
+ nextEntry[k] = v
1240
+ }
1241
+ nextEntry["hooks"] = keptCommands
1242
+ kept = append(kept, nextEntry)
1243
+ }
1244
+ }
1245
+ if len(kept) > 0 {
1246
+ hooks[event] = kept
1247
+ }
1248
+ }
1249
+ if len(hooks) == 0 {
1250
+ delete(next, "hooks")
1251
+ } else {
1252
+ next["hooks"] = hooks
1253
+ }
1254
+ return next
1255
+ }
1256
+
1257
+ func mergeHooks(existing, devrites map[string]any) map[string]any {
1258
+ hooks, _ := existing["hooks"].(map[string]any)
1259
+ if hooks == nil {
1260
+ hooks = map[string]any{}
1261
+ }
1262
+ devHooks, _ := devrites["hooks"].(map[string]any)
1263
+ for event, entries := range devHooks {
1264
+ arr, _ := hooks[event].([]any)
1265
+ devArr, _ := entries.([]any)
1266
+ hooks[event] = append(arr, devArr...)
1267
+ }
1268
+ existing["hooks"] = hooks
1269
+ if statusLine, ok := devrites["statusLine"]; ok {
1270
+ if current, exists := existing["statusLine"]; !exists || isDevritesHookCommand(current) {
1271
+ existing["statusLine"] = statusLine
1272
+ }
1273
+ }
1274
+ return existing
1275
+ }
1276
+
1277
+ func (r *runner) installBinary() error {
1278
+ if !r.opts.WithBinary || os.Getenv("DEVRITES_NO_BINARY") == "1" {
1279
+ fmt.Fprintln(r.opts.Stdout, " engine binary: skipped (--no-binary); hooks fail open.")
1280
+ return nil
1281
+ }
1282
+ if r.opts.DryRun {
1283
+ fmt.Fprintln(r.opts.Stdout, " would install the global devrites-engine control-plane binary")
1284
+ return nil
1285
+ }
1286
+ tag := os.Getenv("DEVRITES_REF")
1287
+ if tag == "" {
1288
+ tag = "v" + strings.TrimPrefix(installedVersion(r.source), "v")
1289
+ }
1290
+ incoming := strings.TrimPrefix(tag, "v")
1291
+ dest := binaryDest()
1292
+ if exists(dest) {
1293
+ if ev := engineVersion(dest); semverLike(ev) && semverLike(incoming) && verGT(ev, incoming) {
1294
+ fmt.Fprintf(r.opts.Stderr, "warning: engine binary: installed %s is newer than %s - refusing to downgrade (kept).\n", ev, tag)
1295
+ return nil
1296
+ }
1297
+ }
1298
+ staged, cleanup, err := r.acquireBinary(tag)
1299
+ if err != nil {
1300
+ fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v - hooks fail open until you install it.\n", err)
1301
+ return nil
1302
+ }
1303
+ defer cleanup()
1304
+ if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
1305
+ fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v - hooks fail open until you install it.\n", err)
1306
+ return nil
1307
+ }
1308
+ data, err := os.ReadFile(staged)
1309
+ if err != nil {
1310
+ fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v - hooks fail open until you install it.\n", err)
1311
+ return nil
1312
+ }
1313
+ if err := fsutil.WriteFileAtomic(dest, data, 0o755); err != nil {
1314
+ fmt.Fprintf(r.opts.Stderr, "warning: engine binary: %v - hooks fail open until you install it.\n", err)
1315
+ return nil
1316
+ }
1317
+ _ = os.Chmod(dest, 0o755)
1318
+ fmt.Fprintf(r.opts.Stdout, " engine binary: installed %s\n", dest)
1319
+ if !binaryReachableFromPATH(dest) {
1320
+ 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)
1321
+ }
1322
+ return nil
1323
+ }
1324
+
1325
+ func (r *runner) acquireBinary(tag string) (string, func(), error) {
1326
+ if v := os.Getenv("DEVRITES_ENGINE_CLI"); v != "" {
1327
+ if exists(v) {
1328
+ return v, func() {}, nil
1329
+ }
1330
+ return "", func() {}, fmt.Errorf("DEVRITES_ENGINE_CLI points to a missing binary: %s", v)
1331
+ }
1332
+ tmp, err := os.MkdirTemp("", "devrites-engine-*")
1333
+ if err != nil {
1334
+ return "", func() {}, fmt.Errorf("create temp dir: %w", err)
1335
+ }
1336
+ cleanup := func() { _ = os.RemoveAll(tmp) }
1337
+ staged := filepath.Join(tmp, "devrites-engine")
1338
+ if r.source != "" && exists(filepath.Join(r.source, "engine", "go.mod")) {
1339
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
1340
+ defer cancel()
1341
+ cmd := exec.CommandContext(ctx, "go", "build", "-trimpath", "-ldflags", "-s -w -X github.com/devrites/devrites/internal/version.Version="+tag, "-o", staged, ".")
1342
+ cmd.Dir = filepath.Join(r.source, "engine")
1343
+ if out, err := cmd.CombinedOutput(); err != nil {
1344
+ return "", cleanup, fmt.Errorf("could not build from source: %w: %s", err, strings.TrimSpace(string(out)))
1345
+ }
1346
+ return staged, cleanup, nil
1347
+ }
1348
+ return "", cleanup, fmt.Errorf("could not obtain a binary (no DEVRITES_ENGINE_CLI handoff, no Go source)")
1349
+ }
1350
+
1351
+ func (r *runner) removeBinary() error {
1352
+ if r.opts.KeepBinary {
1353
+ fmt.Fprintln(r.opts.Stdout, " kept the global devrites-engine binary (--keep-binary).")
1354
+ return nil
1355
+ }
1356
+ for _, p := range binaryCandidates() {
1357
+ if p == "" || !exists(p) {
1358
+ continue
1359
+ }
1360
+ if r.opts.DryRun {
1361
+ fmt.Fprintf(r.opts.Stdout, " [remove] %s (global engine binary)\n", p)
1362
+ continue
1363
+ }
1364
+ if err := os.Remove(p); err == nil {
1365
+ fmt.Fprintf(r.opts.Stdout, " [remove] %s\n", p)
1366
+ return nil
1367
+ }
1368
+ }
1369
+ return nil
1370
+ }
1371
+
1372
+ func binaryDest() string {
1373
+ if dir := os.Getenv("DEVRITES_BIN_DIR"); dir != "" {
1374
+ return filepath.Join(dir, "devrites-engine")
1375
+ }
1376
+ if home, err := os.UserHomeDir(); err == nil {
1377
+ return filepath.Join(home, ".local", "bin", "devrites-engine")
1378
+ }
1379
+ return "/usr/local/bin/devrites-engine"
1380
+ }
1381
+
1382
+ func binaryCandidates() []string {
1383
+ candidates := []string{}
1384
+ if dir := os.Getenv("DEVRITES_BIN_DIR"); dir != "" {
1385
+ candidates = append(candidates, filepath.Join(dir, "devrites-engine"))
1386
+ }
1387
+ home, _ := os.UserHomeDir()
1388
+ if home != "" {
1389
+ candidates = append(candidates, filepath.Join(home, ".local", "bin", "devrites-engine"))
1390
+ }
1391
+ return append(candidates, "/usr/local/bin/devrites-engine")
1392
+ }
1393
+
1394
+ func binaryReachableFromPATH(dest string) bool {
1395
+ if _, err := exec.LookPath("devrites-engine"); err == nil {
1396
+ return true
1397
+ }
1398
+ return pathContainsDir(filepath.Dir(dest))
1399
+ }
1400
+
1401
+ func pathContainsDir(dir string) bool {
1402
+ if dir == "" {
1403
+ return false
1404
+ }
1405
+ want, err := filepath.Abs(dir)
1406
+ if err != nil {
1407
+ want = filepath.Clean(dir)
1408
+ }
1409
+ for _, entry := range filepath.SplitList(os.Getenv("PATH")) {
1410
+ if entry == "" {
1411
+ continue
1412
+ }
1413
+ got, err := filepath.Abs(entry)
1414
+ if err != nil {
1415
+ got = filepath.Clean(entry)
1416
+ }
1417
+ if got == want {
1418
+ return true
1419
+ }
1420
+ }
1421
+ return false
1422
+ }
1423
+
1424
+ func engineVersion(path string) string {
1425
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1426
+ defer cancel()
1427
+ out, err := exec.CommandContext(ctx, path, "version").Output()
1428
+ if err != nil {
1429
+ return ""
1430
+ }
1431
+ return strings.TrimSpace(string(out))
1432
+ }
1433
+
1434
+ func readManifest(path string) map[string]bool {
1435
+ out := map[string]bool{}
1436
+ for _, rel := range readManifestList(path) {
1437
+ out[rel] = true
1438
+ }
1439
+ return out
1440
+ }
1441
+
1442
+ func readManifestList(path string) []string {
1443
+ data, err := os.ReadFile(path)
1444
+ if err != nil {
1445
+ return nil
1446
+ }
1447
+ var out []string
1448
+ for _, line := range strings.Split(string(data), "\n") {
1449
+ line = strings.TrimSpace(line)
1450
+ if line == "" || strings.HasPrefix(line, "#") {
1451
+ continue
1452
+ }
1453
+ // The manifest is hand-editable; an entry that escapes the target
1454
+ // (absolute or ..-traversal) must never be overwritten or removed.
1455
+ if !filepath.IsLocal(filepath.FromSlash(line)) {
1456
+ continue
1457
+ }
1458
+ out = append(out, line)
1459
+ }
1460
+ return out
1461
+ }
1462
+
1463
+ func manifestHeader(path, key string) string {
1464
+ data, err := os.ReadFile(path)
1465
+ if err != nil {
1466
+ return ""
1467
+ }
1468
+ prefix := "# " + key + ":"
1469
+ for _, line := range strings.Split(string(data), "\n") {
1470
+ if strings.HasPrefix(line, prefix) {
1471
+ return strings.TrimSpace(strings.TrimPrefix(line, prefix))
1472
+ }
1473
+ }
1474
+ return ""
1475
+ }
1476
+
1477
+ func installedVersion(source string) string {
1478
+ if source != "" {
1479
+ data, err := os.ReadFile(filepath.Join(source, "package.json"))
1480
+ if err == nil {
1481
+ var pkg struct {
1482
+ Version string `json:"version"`
1483
+ }
1484
+ if json.Unmarshal(data, &pkg) == nil && pkg.Version != "" {
1485
+ return pkg.Version
1486
+ }
1487
+ }
1488
+ }
1489
+ if version.Version != "" && version.Version != "dev" {
1490
+ return strings.TrimPrefix(version.Version, "v")
1491
+ }
1492
+ return "unknown"
1493
+ }
1494
+
1495
+ func isGlobalTarget(target string) bool {
1496
+ home, _ := os.UserHomeDir()
1497
+ if target == home {
1498
+ return true
1499
+ }
1500
+ for _, global := range []string{filepath.Join(home, ".claude"), filepath.Join(home, ".codex")} {
1501
+ if target == global || strings.HasPrefix(target, global+string(os.PathSeparator)) {
1502
+ return true
1503
+ }
1504
+ }
1505
+ return false
1506
+ }
1507
+
1508
+ func pruneEmptyDirs(start, stop string) {
1509
+ for {
1510
+ if start == stop || start == filepath.Dir(start) || start == string(os.PathSeparator) {
1511
+ return
1512
+ }
1513
+ if err := os.Remove(start); err != nil {
1514
+ return
1515
+ }
1516
+ start = filepath.Dir(start)
1517
+ }
1518
+ }
1519
+
1520
+ func exists(path string) bool {
1521
+ _, err := os.Lstat(path)
1522
+ return err == nil
1523
+ }
1524
+
1525
+ func yesno(v bool) string {
1526
+ if v {
1527
+ return "yes"
1528
+ }
1529
+ return "no"
1530
+ }
1531
+
1532
+ func semverLike(v string) bool {
1533
+ v = strings.TrimPrefix(v, "v")
1534
+ return len(v) > 0 && v[0] >= '0' && v[0] <= '9'
1535
+ }
1536
+
1537
+ func verGT(a, b string) bool {
1538
+ a = strings.TrimPrefix(a, "v")
1539
+ b = strings.TrimPrefix(b, "v")
1540
+ as := strings.Split(strings.Split(a, "-")[0], ".")
1541
+ bs := strings.Split(strings.Split(b, "-")[0], ".")
1542
+ for len(as) < 3 {
1543
+ as = append(as, "0")
1544
+ }
1545
+ for len(bs) < 3 {
1546
+ bs = append(bs, "0")
1547
+ }
1548
+ for i := 0; i < 3; i++ {
1549
+ var ai, bi int
1550
+ _, _ = fmt.Sscanf(as[i], "%d", &ai)
1551
+ _, _ = fmt.Sscanf(bs[i], "%d", &bi)
1552
+ if ai > bi {
1553
+ return true
1554
+ }
1555
+ if ai < bi {
1556
+ return false
1557
+ }
1558
+ }
1559
+ return false
1560
+ }