claude-dev-env 2.4.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/CLAUDE.md +26 -59
  2. package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
  3. package/_shared/pr-loop/scripts/code_rules_gate.py +6 -3
  4. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +12 -2
  5. package/_shared/pr-loop/scripts/code_rules_gate_parts/baseline_import_isolation.py +309 -0
  6. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +540 -0
  7. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +206 -70
  8. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/__init__.py +1 -0
  9. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/_repo_test_helpers.py +76 -0
  10. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_baseline_import_isolation.py +248 -0
  11. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_regression.py +309 -0
  12. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +91 -58
  13. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
  14. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
  15. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +202 -0
  16. package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
  17. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
  18. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
  19. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
  20. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
  21. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
  22. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
  23. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
  24. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
  25. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
  26. package/_shared/pr-loop/worker-spawn.md +1 -1
  27. package/agents/CLAUDE.md +3 -2
  28. package/agents/caveman.md +0 -1
  29. package/agents/clasp-deployment-orchestrator.md +0 -1
  30. package/agents/clean-coder.md +0 -1
  31. package/agents/code-advisor.md +0 -1
  32. package/agents/code-quality-agent.md +1 -2
  33. package/agents/code-verifier.md +36 -8
  34. package/agents/deep-research.md +0 -1
  35. package/agents/docs-agent.md +0 -1
  36. package/agents/git-commit-crafter.md +0 -1
  37. package/agents/issue-tracker.md +42 -0
  38. package/agents/plan-packet-validator.md +0 -1
  39. package/agents/pr-description-writer.md +0 -1
  40. package/agents/test_agent_frontmatter.py +67 -18
  41. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
  42. package/bin/CLAUDE.md +68 -5
  43. package/bin/codex-compat.mjs +104 -0
  44. package/bin/codex-compat.test.mjs +51 -0
  45. package/bin/ever-shipped-skills.mjs +1 -0
  46. package/bin/install-constants.mjs +88 -0
  47. package/bin/install.mjs +1138 -114
  48. package/bin/install.prune.test.mjs +869 -19
  49. package/bin/install.test.mjs +906 -2
  50. package/codex-capability-map.json +13 -0
  51. package/commands/implement.md +1 -1
  52. package/commands/right-size.md +1 -1
  53. package/docs/CLAUDE.md +1 -0
  54. package/docs/CODE_RULES.md +2 -0
  55. package/docs/codex-compatibility.md +25 -0
  56. package/docs/host-pool-health-monitor.md +102 -0
  57. package/docs/nas-ssh-invocation.md +96 -12
  58. package/docs/references/CLAUDE.md +4 -2
  59. package/docs/references/advisor-tool.md +13 -0
  60. package/docs/references/code-review-enforcement.md +35 -0
  61. package/docs/references/team-advisor-skill.md +14 -0
  62. package/hooks/blocking/CLAUDE.md +4 -0
  63. package/hooks/blocking/code_review_pr_create_gate.py +7 -3
  64. package/hooks/blocking/code_review_push_gate.py +9 -4
  65. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
  66. package/hooks/blocking/config/__init__.py +5 -5
  67. package/hooks/blocking/config/code_review_enforcement_constants.py +40 -7
  68. package/hooks/blocking/config/test_code_review_enforcement_constants.py +58 -0
  69. package/hooks/blocking/config/verified_commit_constants.py +160 -159
  70. package/hooks/blocking/eli11_reply_enforcer.py +479 -0
  71. package/hooks/blocking/gh_body_arg_blocker.py +1 -1
  72. package/hooks/blocking/nas_ssh_binary_enforcer.py +8 -46
  73. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
  74. package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
  75. package/hooks/blocking/shell_substitution_blocker.py +129 -0
  76. package/hooks/blocking/state_description_blocker.py +1 -1
  77. package/hooks/blocking/stop_dispatcher.py +1 -1
  78. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +2 -3
  79. package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
  80. package/hooks/blocking/test_code_review_push_gate.py +16 -0
  81. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
  82. package/hooks/blocking/test_eli11_reply_enforcer.py +457 -0
  83. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
  84. package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
  85. package/hooks/blocking/test_shell_substitution_blocker.py +124 -0
  86. package/hooks/blocking/test_stop_dispatcher.py +23 -0
  87. package/hooks/blocking/test_unscoped_search_blocker.py +102 -0
  88. package/hooks/blocking/test_verdict_directory_write_blocker.py +804 -808
  89. package/hooks/blocking/test_verification_verdict_store.py +54 -0
  90. package/hooks/blocking/test_verified_commit_gate.py +581 -581
  91. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
  92. package/hooks/blocking/unscoped_search_blocker.py +391 -0
  93. package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
  94. package/hooks/blocking/verification_verdict_store.py +1039 -1036
  95. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
  96. package/hooks/blocking/verifier_verdict_minter.py +280 -280
  97. package/hooks/git-hooks/CLAUDE.md +3 -0
  98. package/hooks/git-hooks/conftest.py +30 -0
  99. package/hooks/git-hooks/gate_utils.py +2 -2
  100. package/hooks/git-hooks/git_hooks_constants/__init__.py +41 -2
  101. package/hooks/git-hooks/pre_push.py +75 -4
  102. package/hooks/git-hooks/pre_push_base_reference.py +166 -0
  103. package/hooks/git-hooks/test_config.py +0 -15
  104. package/hooks/git-hooks/test_gate_utils.py +3 -15
  105. package/hooks/git-hooks/test_pre_commit.py +1 -15
  106. package/hooks/git-hooks/test_pre_push.py +257 -23
  107. package/hooks/git-hooks/test_pre_push_base_reference.py +339 -0
  108. package/hooks/hooks.json +10 -12
  109. package/hooks/hooks_constants/CLAUDE.md +7 -2
  110. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +4 -4
  111. package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +101 -0
  112. package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
  113. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +2 -8
  114. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
  115. package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
  116. package/hooks/hooks_constants/shell_command_segments.py +82 -0
  117. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +67 -0
  118. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -0
  119. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +5 -6
  120. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +1 -0
  121. package/hooks/hooks_constants/unscoped_search_blocker_constants.py +153 -0
  122. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
  123. package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
  124. package/hooks/pyproject.toml +1 -0
  125. package/hooks/validators/CLAUDE.md +1 -0
  126. package/hooks/validators/config/__init__.py +0 -0
  127. package/hooks/validators/config/directory_exemption_constants.py +183 -0
  128. package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
  129. package/hooks/validators/conftest.py +4 -0
  130. package/hooks/validators/ruff_integration.py +49 -5
  131. package/hooks/validators/run_all_validators.py +206 -9
  132. package/hooks/validators/test_directory_exemption_constants.py +185 -0
  133. package/hooks/validators/test_python_antipattern_checks.py +110 -5
  134. package/hooks/validators/test_ruff_integration.py +92 -1
  135. package/hooks/validators/test_run_all_validators.py +115 -68
  136. package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
  137. package/package.json +13 -3
  138. package/rules/CLAUDE.md +17 -22
  139. package/rules/agent-spawn-protocol.md +6 -6
  140. package/rules/anti-corollary-tests.md +1 -1
  141. package/rules/bdd.md +1 -1
  142. package/rules/cleanup-temp-files.md +10 -4
  143. package/rules/code-standards.md +7 -0
  144. package/rules/conservative-action.md +1 -5
  145. package/rules/context7.md +0 -4
  146. package/rules/destructive-commands.md +47 -0
  147. package/rules/doc-inventory-integrity.md +48 -0
  148. package/rules/doc-prose-cuts.md +58 -0
  149. package/rules/docstring-prose-matches-implementation.md +53 -44
  150. package/rules/durable-post-artifacts.md +0 -4
  151. package/rules/eli11-replies.md +31 -0
  152. package/rules/explore-thoroughly.md +4 -4
  153. package/rules/falsify-before-green.md +68 -0
  154. package/rules/file-global-constants.md +1 -1
  155. package/rules/filesystem-search.md +51 -0
  156. package/rules/gh-cli-conventions.md +27 -0
  157. package/rules/git-workflow.md +26 -0
  158. package/rules/hedging-claims.md +9 -0
  159. package/rules/long-horizon-autonomy.md +0 -4
  160. package/rules/measurement-denominators.md +48 -0
  161. package/rules/nas-ssh-invocation.md +23 -5
  162. package/rules/parallel-tools.md +2 -2
  163. package/rules/plain-illustrative-docstrings.md +3 -7
  164. package/rules/plain-language.md +2 -0
  165. package/rules/proof-of-work-pr-comments.md +0 -4
  166. package/rules/re-stage-before-commit.md +2 -0
  167. package/rules/research-mode.md +10 -0
  168. package/rules/shell-invocation.md +21 -0
  169. package/rules/testing.md +4 -0
  170. package/rules/verified-commit-gate-skip.md +3 -27
  171. package/rules/verify-before-asking.md +5 -0
  172. package/rules/windows-filesystem-safe.md +1 -1
  173. package/rules/workers-done-before-complete.md +4 -0
  174. package/scripts/CLAUDE.md +1 -0
  175. package/scripts/Capture-PoolHealth.ps1 +410 -0
  176. package/scripts/Migrate-ShellPolicy.ps1 +1 -1
  177. package/scripts/_code_review_test_support.py +404 -0
  178. package/scripts/claude_chain_runner.py +141 -1
  179. package/scripts/codex_capability_bridge.py +171 -0
  180. package/scripts/codex_compat_materializer.py +1087 -0
  181. package/scripts/codex_compat_watcher.py +502 -0
  182. package/scripts/conftest.py +16 -1
  183. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
  184. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
  185. package/scripts/dev_env_scripts_constants/code_review_constants.py +37 -0
  186. package/scripts/invoke_code_review.py +11 -4
  187. package/scripts/resolve_worker_spawn.py +626 -626
  188. package/scripts/spawn_grok_batch.py +672 -672
  189. package/scripts/sync_to_cursor/rules.py +0 -10
  190. package/scripts/test_claude_chain_runner.py +131 -0
  191. package/scripts/test_invoke_code_review.py +85 -908
  192. package/scripts/test_invoke_code_review_chain.py +70 -0
  193. package/scripts/test_invoke_code_review_cli.py +192 -0
  194. package/scripts/test_invoke_code_review_contract.py +256 -0
  195. package/scripts/test_invoke_code_review_git.py +123 -0
  196. package/scripts/test_invoke_code_review_mode.py +99 -0
  197. package/scripts/test_resolve_worker_spawn.py +1014 -1014
  198. package/scripts/tests/test_code_review_constants.py +80 -0
  199. package/scripts/tests/test_codex_capability_bridge.py +91 -0
  200. package/scripts/tests/test_codex_compat_materializer.py +632 -0
  201. package/scripts/tests/test_codex_compat_watcher.py +599 -0
  202. package/scripts/tests/test_sync_to_cursor.py +0 -1
  203. package/skills/CLAUDE.md +2 -0
  204. package/skills/auditing-claude-config/SKILL.md +114 -114
  205. package/skills/autoconverge/SKILL.md +427 -427
  206. package/skills/autoconverge/reference/convergence.md +24 -3
  207. package/skills/autoconverge/workflow/CLAUDE.md +1 -0
  208. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
  209. package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
  210. package/skills/autoconverge/workflow/converge.mjs +168 -1
  211. package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
  212. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
  213. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
  214. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
  215. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
  216. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
  217. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
  218. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
  219. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
  220. package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
  221. package/skills/bugteam/reference/copilot-gap-analysis.md +1 -1
  222. package/skills/closeout/SKILL.md +33 -50
  223. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
  224. package/skills/codex-review/scripts/run_codex_review.py +233 -1
  225. package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
  226. package/skills/condensing-instructions/SKILL.md +72 -0
  227. package/skills/copilot-review/SKILL.md +119 -119
  228. package/skills/e-code-review/SKILL.md +52 -0
  229. package/skills/e-code-review/reference/fix.md +54 -0
  230. package/skills/e-code-review/reference/loop.md +43 -0
  231. package/skills/e-code-review/reference/low.md +57 -0
  232. package/skills/e-code-review/reference/medium.md +153 -0
  233. package/skills/e-code-review/reference/xhigh.md +182 -0
  234. package/skills/e-simplify/SKILL.md +97 -0
  235. package/skills/fresh-branch/CLAUDE.md +1 -1
  236. package/skills/fresh-branch/SKILL.md +5 -6
  237. package/skills/fresh-branch/scripts/create_fresh_branch.py +42 -24
  238. package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -3
  239. package/skills/fresh-branch/scripts/test_create_fresh_branch.py +30 -126
  240. package/skills/issue-tracker/SKILL.md +92 -0
  241. package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
  242. package/skills/issue-tracker/reference/handoff-schema.md +64 -0
  243. package/skills/issue-tracker/reference/operation-matrix.md +41 -0
  244. package/skills/orchestrator/SKILL.md +177 -22
  245. package/skills/orchestrator/scripts/status_gate.py +625 -0
  246. package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
  247. package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
  248. package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
  249. package/skills/orchestrator/scripts/test_status_gate.py +439 -0
  250. package/skills/orchestrator-refresh/SKILL.md +129 -35
  251. package/skills/plan-to-pr/SKILL.md +155 -0
  252. package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
  253. package/skills/plan-to-pr/reference/model-routing.md +36 -0
  254. package/skills/plan-to-pr/reference/packet-contract.md +43 -0
  255. package/skills/plan-to-pr/reference/packet-schema.json +57 -0
  256. package/skills/plan-to-pr/reference/process-inventory.md +22 -0
  257. package/skills/plan-to-pr/reference/review-loop.md +33 -0
  258. package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
  259. package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
  260. package/skills/plan-to-pr/reference/task-seeds.md +14 -0
  261. package/skills/plan-to-pr/reference/task-ticket.md +38 -0
  262. package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
  263. package/skills/plan-to-pr/scripts/config/constants.py +193 -0
  264. package/skills/plan-to-pr/scripts/create_packet.py +173 -0
  265. package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
  266. package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
  267. package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
  268. package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
  269. package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
  270. package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
  271. package/skills/plan-to-pr/scripts/validate_run.py +173 -0
  272. package/skills/plan-to-pr/test_skill_contract.py +207 -0
  273. package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
  274. package/skills/pr-converge/SKILL.md +472 -469
  275. package/skills/pr-converge/reference/examples.md +3 -3
  276. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  277. package/skills/pr-converge/reference/ground-rules.md +7 -4
  278. package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
  279. package/skills/pr-converge/reference/per-tick.md +5 -5
  280. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  281. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
  282. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
  283. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
  284. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  285. package/skills/pr-fix-protocol/SKILL.md +1 -1
  286. package/skills/privacy-hygiene/SKILL.md +68 -68
  287. package/skills/privacy-hygiene/reference/sweep-procedure.md +1 -1
  288. package/skills/prototype/workflows/promotion.md +1 -1
  289. package/skills/release-notes-html/SKILL.md +164 -0
  290. package/skills/session-log/SKILL.md +1 -1
  291. package/skills/task-build/CLAUDE.md +8 -7
  292. package/skills/task-build/SKILL.md +16 -8
  293. package/skills/task-build/reference/tool-routing.md +19 -0
  294. package/rules/claude-md-orphan-file.md +0 -28
  295. package/rules/cleanup-command-forms.md +0 -23
  296. package/rules/code-reviews.md +0 -11
  297. package/rules/env-var-table-code-drift.md +0 -10
  298. package/rules/gh-body-file.md +0 -5
  299. package/rules/gh-paginate.md +0 -3
  300. package/rules/hook-prose-matches-detector.md +0 -15
  301. package/rules/no-historical-clutter.md +0 -26
  302. package/rules/no-inline-destructive-literals.md +0 -9
  303. package/rules/no-justification-noise.md +0 -61
  304. package/rules/package-inventory-stale-entry.md +0 -25
  305. package/rules/right-sized-engineering.md +0 -28
  306. package/rules/self-contained-docs.md +0 -17
  307. package/rules/shell-invocation-policy.md +0 -5
  308. package/rules/tdd.md +0 -7
  309. package/skills/closeout/reference/issue-body-templates.md +0 -108
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ import { spawn, spawnSync } from "node:child_process";
3
+ import { fileURLToPath } from "node:url";
4
+ import path from "node:path";
5
+
6
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
+ const materializerPath = path.join(packageRoot, "scripts", "codex_compat_materializer.py");
8
+ const bridgePath = path.join(packageRoot, "scripts", "codex_capability_bridge.py");
9
+ const pythonNames = process.platform === "win32" ? ["py", "python"] : ["python3", "python"];
10
+
11
+ export function createHelpText() {
12
+ return [
13
+ "Usage: codex-compat <command> [options]",
14
+ "",
15
+ "Commands:",
16
+ " materialize Plan or apply Claude agents into an explicit Codex root",
17
+ " bridge Translate a Claude capability request to a declarative record",
18
+ "",
19
+ "Materialize options:",
20
+ " --source-root <path> Claude source root (required)",
21
+ " --target-root <path> Codex target root (required)",
22
+ " --apply Write changes; default is dry-run",
23
+ " --allow-prune-all Permit an empty plan to delete every managed file",
24
+ " --python <command> Python interpreter override",
25
+ "",
26
+ "Bridge options:",
27
+ " --surface <name> Capability surface (required)",
28
+ " --payload <json> JSON object payload (required)",
29
+ " --python <command> Python interpreter override",
30
+ "",
31
+ "Python may also be selected with CODEX_COMPAT_PYTHON. Roots are never inferred or written automatically.",
32
+ ].join("\n");
33
+ }
34
+
35
+ function readOption(argumentsList, optionName) {
36
+ const optionIndex = argumentsList.indexOf(optionName);
37
+ if (optionIndex < 0) return undefined;
38
+ return argumentsList[optionIndex + 1];
39
+ }
40
+
41
+ function requireOption(argumentsList, optionName) {
42
+ const optionValue = readOption(argumentsList, optionName);
43
+ if (!optionValue || optionValue.startsWith("--")) throw new Error(`Missing required option: ${optionName}`);
44
+ return optionValue;
45
+ }
46
+
47
+ function writeErrorRecord(errorCode, errorMessage) {
48
+ process.stderr.write(`${JSON.stringify({ error: { code: errorCode, message: errorMessage } })}\n`);
49
+ }
50
+
51
+ export function buildCommand(commandName, argumentsList, interpreter) {
52
+ if (commandName === "materialize") {
53
+ const sourceRoot = requireOption(argumentsList, "--source-root");
54
+ const targetRoot = requireOption(argumentsList, "--target-root");
55
+ const materializerArguments = [materializerPath, sourceRoot, targetRoot];
56
+ if (argumentsList.includes("--apply")) materializerArguments.push("--apply");
57
+ if (argumentsList.includes("--allow-prune-all")) materializerArguments.push("--allow-prune-all");
58
+ return { executable: interpreter, arguments: materializerArguments };
59
+ }
60
+ if (commandName === "bridge") {
61
+ const surface = requireOption(argumentsList, "--surface");
62
+ const payload = requireOption(argumentsList, "--payload");
63
+ return { executable: interpreter, arguments: [bridgePath, surface, payload] };
64
+ }
65
+ throw new Error(`Unknown command: ${commandName}`);
66
+ }
67
+
68
+ export function findPython(commandNames = pythonNames, canRun = (command) => spawnSync(command, ["--version"], { stdio: "ignore" }).status === 0) {
69
+ for (const eachCommand of commandNames) {
70
+ if (canRun(eachCommand)) return eachCommand;
71
+ }
72
+ return undefined;
73
+ }
74
+
75
+ export function runCommand(command, runChild = spawn) {
76
+ return new Promise((resolve) => {
77
+ const childProcess = runChild(command.executable, command.arguments, { stdio: "inherit" });
78
+ childProcess.on("close", (exitCode, signal) => resolve(exitCode ?? 1 + (signal ? 1 : 0)));
79
+ childProcess.on("error", () => resolve(1));
80
+ });
81
+ }
82
+
83
+ export async function main(argumentsList = process.argv.slice(2), dependencies = {}) {
84
+ if (argumentsList.length === 0 || argumentsList.includes("--help") || argumentsList.includes("-h")) {
85
+ process.stdout.write(`${createHelpText()}\n`);
86
+ return 0;
87
+ }
88
+ const [commandName, ...commandArguments] = argumentsList;
89
+ try {
90
+ const interpreter = readOption(commandArguments, "--python") ?? process.env.CODEX_COMPAT_PYTHON ?? await (dependencies.findPython ?? findPython)();
91
+ if (!interpreter) {
92
+ writeErrorRecord("python_unavailable", "No usable Python interpreter found. Use --python or CODEX_COMPAT_PYTHON.");
93
+ return 1;
94
+ }
95
+ const command = buildCommand(commandName, commandArguments, interpreter);
96
+ return await (dependencies.runCommand ?? runCommand)(command, dependencies.runChild);
97
+ } catch (error) {
98
+ const errorMessage = error instanceof Error ? error.message : "Unknown compatibility command error";
99
+ writeErrorRecord("invalid_command", errorMessage);
100
+ return 2;
101
+ }
102
+ }
103
+
104
+ if (fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) process.exitCode = await main();
@@ -0,0 +1,51 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { buildCommand, createHelpText, main } from "./codex-compat.mjs";
4
+
5
+ test("help describes both compatibility commands", () => {
6
+ assert.match(createHelpText(), /materialize/);
7
+ assert.match(createHelpText(), /bridge/);
8
+ });
9
+
10
+ test("missing required arguments returns a usage error", async () => {
11
+ const messages = [];
12
+ const originalWrite = process.stderr.write;
13
+ process.stderr.write = (message) => messages.push(message);
14
+ try { assert.equal(await main(["materialize"], { findPython: async () => "python" }), 2); } finally { process.stderr.write = originalWrite; }
15
+ assert.equal(JSON.parse(messages.join("")).error.code, "invalid_command");
16
+ assert.match(messages.join(""), /--source-root/);
17
+ });
18
+
19
+ test("unknown command returns structured error without running a child", async () => {
20
+ const messages = [];
21
+ let wasRun = false;
22
+ const originalWrite = process.stderr.write;
23
+ process.stderr.write = (message) => messages.push(message);
24
+ try {
25
+ const exitCode = await main(["unknown"], {
26
+ findPython: async () => "python",
27
+ runCommand: async () => { wasRun = true; return 0; },
28
+ });
29
+ assert.equal(exitCode, 2);
30
+ } finally { process.stderr.write = originalWrite; }
31
+ assert.equal(JSON.parse(messages.join("")).error.code, "invalid_command");
32
+ assert.equal(wasRun, false);
33
+ });
34
+
35
+ test("commands use safe argv and forward dry-run and apply", () => {
36
+ const dryRun = buildCommand("materialize", ["--source-root", "source root", "--target-root", "target;root"], "python");
37
+ const apply = buildCommand("materialize", ["--source-root", "source", "--target-root", "target", "--apply"], "python");
38
+ assert.equal(dryRun.arguments.at(-1), "target;root");
39
+ assert.equal(apply.arguments.at(-1), "--apply");
40
+ });
41
+
42
+ test("bridge forwarding and child status use injected runners", async () => {
43
+ let receivedCommand;
44
+ const exitCode = await main(["bridge", "--surface", "TaskCreate", "--payload", '{"name":"ship","status":"pending"}'], {
45
+ findPython: async () => "python-test",
46
+ runCommand: async (command) => { receivedCommand = command; return 7; },
47
+ });
48
+ assert.equal(exitCode, 7);
49
+ assert.equal(receivedCommand.executable, "python-test");
50
+ assert.equal(receivedCommand.arguments.at(-1), '{"name":"ship","status":"pending"}');
51
+ });
@@ -39,6 +39,7 @@ export const EVER_SHIPPED_SKILL_NAMES = new Set([
39
39
  'grok-spawn',
40
40
  'grokify',
41
41
  'implement',
42
+ 'issue-tracker',
42
43
  'log-audit',
43
44
  'logifix',
44
45
  'monitor-open-prs',
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Named values the installer reads, kept out of the code that uses them.
3
+ *
4
+ * `install.mjs` holds the install pipeline; the tables below hold the data that
5
+ * pipeline compares against, so a value has one home and one spelling.
6
+ */
7
+
8
+ /**
9
+ * Entry names the source walk leaves behind, whatever directory they sit in.
10
+ *
11
+ * Each name belongs to a tool that writes beside the source it reads: Python
12
+ * bytecode caches, the ruff, pytest, and mypy caches, an installed
13
+ * `node_modules` tree, and the macOS Finder's `.DS_Store` marker. A contributor
14
+ * who runs the test suites and then runs `node bin/install.mjs` copies from a
15
+ * source tree carrying all of them, so skipping the names at the walk keeps them
16
+ * out of `~/.claude` and out of the install manifest.
17
+ */
18
+ export const SKIPPED_SOURCE_ENTRY_NAMES = new Set([
19
+ '__pycache__',
20
+ '.ruff_cache',
21
+ '.pytest_cache',
22
+ '.mypy_cache',
23
+ 'node_modules',
24
+ '.DS_Store',
25
+ ]);
26
+
27
+ /**
28
+ * File extensions the source walk leaves behind, compared in lower case.
29
+ *
30
+ * Python writes a `.pyc` beside a module inside `__pycache__` and a `.pyo` under
31
+ * an optimized run, and either can land outside a cache directory, so the
32
+ * extension check stands alongside the name check.
33
+ */
34
+ export const SKIPPED_SOURCE_FILE_EXTENSIONS = new Set(['.pyc', '.pyo']);
35
+
36
+ /**
37
+ * The name shape a run backup directory carries under
38
+ * `~/.claude/.claude-dev-env-pruned/`.
39
+ *
40
+ * The installer names each run backup from an ISO timestamp with every `:` and
41
+ * `.` rewritten as `-`, for example `2026-07-25T18-04-11-923Z`. The retention
42
+ * sweep removes only a direct child matching this shape, so a directory any
43
+ * other tool or person left in the backup directory stays where it is.
44
+ */
45
+ export const RUN_BACKUP_DIRECTORY_NAME_PATTERN =
46
+ /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z$/;
47
+
48
+ /**
49
+ * The directory name skill directories carry in a package source and under
50
+ * `~/.claude`.
51
+ *
52
+ * The copy loop reads `<package-root>/skills` and writes `~/.claude/skills`, the
53
+ * dependency walk reads `<dependency-root>/skills`, the retired-skill prune reads
54
+ * the installed directory, and the per-root stale-file prune names it as one of
55
+ * its roots. One spelling serves every one of them, so the copy destination and
56
+ * the prune target stay the same directory.
57
+ */
58
+ export const MANAGED_SKILLS_DIRECTORY_NAME = 'skills';
59
+
60
+ /**
61
+ * The directory name hook scripts carry in a package source and under
62
+ * `~/.claude`.
63
+ *
64
+ * The copy loop reads `<package-root>/hooks` and writes `~/.claude/hooks`, the
65
+ * hooks.json reads sit under the same name in each package source, the git-hook
66
+ * shims and the mypy configuration point at the installed directory, the
67
+ * retired-hook diff takes its relative paths against it, and the per-root
68
+ * stale-file prune names it as one of its roots.
69
+ */
70
+ export const MANAGED_HOOKS_DIRECTORY_NAME = 'hooks';
71
+
72
+ /**
73
+ * The `~/.claude` file name that holds the user's harness settings.
74
+ *
75
+ * The hook merge writes it, the retired-hook prune rewrites it, and the uninstall
76
+ * purge rewrites it, so all three reach the file through one name.
77
+ */
78
+ export const SETTINGS_FILE_NAME = 'settings.json';
79
+
80
+ /**
81
+ * The home-directory file name `install_mypy_ini.mjs` writes.
82
+ *
83
+ * mypy reads its configuration from the home directory, so this is the one file
84
+ * the installer writes outside `~/.claude`. The install records the path and the
85
+ * uninstall containment guard names it as a permitted location, so `--uninstall`
86
+ * removes the file the install created.
87
+ */
88
+ export const MYPY_INI_FILE_NAME = '.mypy.ini';