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,193 @@
1
+ """Named values for deterministic skill protocol validation."""
2
+
3
+ import re
4
+
5
+ ALLOWED_WORKER_ROLE: str = "implementation worker"
6
+ ALLOWED_WORKER_EFFORT: str = "low"
7
+ ALLOWED_REVIEW_MODEL: str = "Luna"
8
+ NATIVE_REVIEW_COMMAND: str = "/e-code-review low"
9
+ PASSED_EVIDENCE_PREFIX: str = "passed:"
10
+ SURFACE_HASH_PATTERN: re.Pattern[str] = re.compile(r"^[0-9a-f]{64}$")
11
+ PATH_SEPARATOR_PATTERN: re.Pattern[str] = re.compile(r"[/\\]+")
12
+ DOCS_PLANS_PATH_PATTERN: re.Pattern[str] = re.compile(r"^docs/plans(?:/|$)")
13
+ SLUG_PATTERN: re.Pattern[str] = re.compile(r"[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
14
+ SOURCE_LOCATOR_PATTERN: re.Pattern[str] = re.compile(
15
+ r"^(?:line|lines|section)\s+\S(?:.*\S)?$", re.IGNORECASE
16
+ )
17
+ PATH_TRAVERSAL_TOKEN: str = ".."
18
+ REPRODUCIBILITY_BLOCK_PATTERN: re.Pattern[str] = re.compile(
19
+ r"\b(?:date|random|uuidgen|pwsh\s+-c)\b", re.IGNORECASE
20
+ )
21
+ UNRESOLVED_TEXT_PATTERN: re.Pattern[str] = re.compile(
22
+ r"\b(?:todo|tbd|fixme|placeholder|open questions|to be decided|fill this in)\b|\{\{[^}]+\}\}|<[A-Za-z][^>]*>",
23
+ re.IGNORECASE,
24
+ )
25
+ COMMIT_RANGE_SEPARATOR: str = ".."
26
+ EXIT_CODE_USAGE_ERROR: int = 2
27
+ PACKET_CREATION_ERROR_EXIT_CODE: int = 2
28
+ PACKET_VALIDATION_ERROR_EXIT_CODE: int = 2
29
+ EXIT_CODE_VALID_SET: int = 0
30
+ EXIT_CODE_INVALID_SET: int = 2
31
+ VALIDATION_PASSED: str = "run validation passed"
32
+ SET_VALIDATION_PASSED: str = "run set validation passed"
33
+ WORKTREE_OPTION: str = "--worktree"
34
+ BASE_HEAD_OPTION: str = "--base-head"
35
+ COMMIT_SET_OPTION: str = "--commits"
36
+ FINDING_STATUS_CONFIRMED: str = "confirmed"
37
+ FINDING_STATUS_DISMISSED: str = "dismissed"
38
+ REPAIR_STATUS_COMPLETE: str = "complete"
39
+ REPAIR_STATUS_NOT_REQUIRED: str = "not-required"
40
+ REVIEW_STATUS_CLEAN: str = "clean"
41
+ GIT_COMMAND: str = "git"
42
+ GIT_DIFF_FORMAT: str = "--format="
43
+ GIT_DIFF_SEPARATOR: str = "\n"
44
+ SHA256_ALGORITHM: str = "sha256"
45
+ ALL_REQUIRED_PACKET_FILES: tuple[str, ...] = (
46
+ "packet.json",
47
+ "context.md",
48
+ "plan.md",
49
+ "tasks.md",
50
+ "handoff.md",
51
+ )
52
+ ALL_SOURCE_FIELDS: tuple[str, ...] = ("path", "locator", "fact")
53
+ ALL_TASK_FIELDS: tuple[str, ...] = (
54
+ "deliverable",
55
+ "acceptance_command",
56
+ "test_command",
57
+ "verification_command",
58
+ )
59
+ ALL_TASK_COMMAND_FIELDS: tuple[str, ...] = (
60
+ "acceptance_command",
61
+ "test_command",
62
+ "verification_command",
63
+ )
64
+ HANDOFF_APPROVAL_LINE: str = "Approval: approved"
65
+ HANDOFF_TASK_ORDER_PREFIX: str = "Task Order: "
66
+ HANDOFF_PACKET_PREFIX: str = "Packet: "
67
+ HANDOFF_ALLOWED_FILES_PREFIX: str = "Allowed files: "
68
+ HANDOFF_ACCEPTANCE_PREFIX: str = "Acceptance command: "
69
+ HANDOFF_TEST_PREFIX: str = "Test command: "
70
+ HANDOFF_VERIFICATION_PREFIX: str = "Verification command: "
71
+ TASK_LINE_PREFIX: str = "Task "
72
+ HANDOFF_TASK_LINE_PATTERN: re.Pattern[str] = re.compile(r"^Task (task-[0-9]+):")
73
+ LINE_SEPARATOR: str = "\n"
74
+ COMMAND_SEPARATOR: str = ", "
75
+ MARKDOWN_FIELD_SEPARATOR: str = ";"
76
+ MARKDOWN_HEADING_PATTERN: re.Pattern[str] = re.compile(
77
+ r"^#+\s+\S.*\s*$", re.MULTILINE | re.IGNORECASE
78
+ )
79
+ MARKDOWN_HEADING_TEMPLATE: str = r"^#+\s+{}\s*$"
80
+ PACKET_DIRECTORY_MINIMUM_PARTS: int = 3
81
+ PACKET_JSON_INDENT_LEVEL: int = 2
82
+ DOCS_DIRECTORY_NAME: str = "docs"
83
+ NEWLINE_SEPARATOR: str = "\n"
84
+ PACKET_PARENT_PARTS_SLICE_START: int = -3
85
+ PACKET_SLUG_PARTS_SLICE_END: int = -1
86
+ ALL_PACKET_FIELDS: tuple[str, ...] = (
87
+ "schema_version",
88
+ "slug",
89
+ "status",
90
+ "request",
91
+ "allowed_files",
92
+ "sources",
93
+ "decisions",
94
+ "open_questions",
95
+ "tasks",
96
+ "validation",
97
+ )
98
+ ALL_PACKET_REQUIRED_FIELDS: tuple[str, ...] = ALL_PACKET_FIELDS
99
+ ALL_VALIDATION_FIELDS: tuple[str, ...] = (
100
+ "schema_valid",
101
+ "boundary_valid",
102
+ "markdown_matches",
103
+ "validated_by",
104
+ )
105
+ ALL_TASK_CONTRACT_FIELDS: tuple[str, ...] = (
106
+ "id",
107
+ "deliverable",
108
+ "allowed_files",
109
+ "acceptance_command",
110
+ "test_command",
111
+ "verification_command",
112
+ "commit",
113
+ "dependencies",
114
+ )
115
+
116
+ ARGUMENT_COUNT_REQUIRED: int = 2
117
+ ARGUMENT_COUNT_WITH_WORKTREE: int = 4
118
+ RUN_ARGUMENT_COUNT_REQUIRED: int = 4
119
+ RUN_ARGUMENT_COUNT_WITH_WORKTREE: int = 5
120
+ OPTION_ARGUMENT_INDEX: int = 2
121
+ COMMIT_RANGE_ARGUMENT_INDEX: int = 3
122
+ WORKTREE_OPTION_ARGUMENT_INDEX: int = 4
123
+ WORKTREE_PATH_ARGUMENT_INDEX: int = 5
124
+ COMMIT_HASH_PATTERN: re.Pattern[str] = re.compile(r"^[0-9a-f]{7,40}$")
125
+ COMMIT_HASH_ERROR_TEMPLATE: str = (
126
+ "record.commit must be a %d-%d character lowercase commit hash"
127
+ )
128
+ EXIT_CODE_INVALID_RECORD: int = 2
129
+ EXIT_CODE_VALID_RECORD: int = 0
130
+ JSON_ENCODING: str = "utf-8"
131
+ MAXIMUM_COMMIT_HASH_LENGTH: int = 40
132
+ MINIMUM_COMMIT_HASH_LENGTH: int = 7
133
+ MISSING_FIELD_SEPARATOR: str = ", "
134
+ PROTOCOL_VALIDATION_PASSED: str = "protocol validation passed"
135
+ RECORD_SCHEMA_FILENAME: str = "run-record.schema.json"
136
+ ALL_SUPPORTED_SCHEMA_TYPES: tuple[str, ...] = ("object", "array", "string", "boolean")
137
+ ALL_RECORD_FIELDS: tuple[str, ...] = (
138
+ "task_identity",
139
+ "deliverable",
140
+ "allowed_files",
141
+ "acceptance_check",
142
+ "baseline",
143
+ "worker_route",
144
+ "commit",
145
+ "review_record",
146
+ "repair_record",
147
+ "reverification_record",
148
+ "verification_record",
149
+ )
150
+ ALL_ALLOWED_RECORD_FIELDS: tuple[str, ...] = ALL_RECORD_FIELDS + ("worktree",)
151
+ ALL_TOP_LEVEL_TEXT_FIELDS: tuple[str, ...] = (
152
+ "task_identity",
153
+ "deliverable",
154
+ "acceptance_check",
155
+ "baseline",
156
+ "worker_route",
157
+ )
158
+ ALL_REVIEW_FIELDS: tuple[str, ...] = (
159
+ "resolved_model",
160
+ "effort",
161
+ "command",
162
+ "findings",
163
+ "repair_status",
164
+ "surface_hash",
165
+ "findings_only",
166
+ "has_repair_flag",
167
+ )
168
+ ALL_REPAIR_FIELDS: tuple[str, ...] = (
169
+ "resolved_model",
170
+ "effort",
171
+ "confirmed_findings",
172
+ "repair_status",
173
+ "surface_hash",
174
+ )
175
+ ALL_VERIFICATION_FIELDS: tuple[str, ...] = (
176
+ "acceptance_output",
177
+ "verifier_output",
178
+ "verified_commit_gate",
179
+ "surface_hash",
180
+ )
181
+ ALL_REVIEW_TEXT_FIELDS: tuple[str, ...] = (
182
+ "resolved_model",
183
+ "effort",
184
+ "command",
185
+ "repair_status",
186
+ "surface_hash",
187
+ )
188
+ ALL_REPAIR_TEXT_FIELDS: tuple[str, ...] = (
189
+ "resolved_model",
190
+ "effort",
191
+ "repair_status",
192
+ "surface_hash",
193
+ )
@@ -0,0 +1,173 @@
1
+ """Create a deterministic native plan-to-PR packet skeleton."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ if str(Path(__file__).parent) not in sys.path:
12
+ sys.path.insert(0, str(Path(__file__).parent))
13
+
14
+ from config.constants import (
15
+ PACKET_CREATION_ERROR_EXIT_CODE,
16
+ PACKET_JSON_INDENT_LEVEL,
17
+ SLUG_PATTERN,
18
+ )
19
+
20
+
21
+ def _parse_arguments() -> argparse.Namespace:
22
+ """Parse packet creation arguments."""
23
+ parser = argparse.ArgumentParser(
24
+ description="Create a native planning packet skeleton."
25
+ )
26
+ parser.add_argument("--repo-root", required=True, type=Path)
27
+ parser.add_argument("--slug", required=True)
28
+ parser.add_argument("--base-ref", required=True)
29
+ return parser.parse_args()
30
+
31
+
32
+ def _validate_slug(slug: str) -> None:
33
+ """Raise an error when a slug cannot name one plan directory."""
34
+ if not SLUG_PATTERN.fullmatch(slug):
35
+ raise ValueError(
36
+ "slug must contain lowercase letters, numbers, and single hyphens"
37
+ )
38
+
39
+
40
+ def _resolve_base_commit(repo_root: Path, base_ref: str) -> str:
41
+ """Resolve a base ref to its full commit hash."""
42
+ if base_ref.startswith("-"):
43
+ raise ValueError("base-ref must not start with a hyphen")
44
+ completed_command = subprocess.run(
45
+ [
46
+ "git",
47
+ "-C",
48
+ str(repo_root),
49
+ "rev-parse",
50
+ "--verify",
51
+ f"{base_ref}^{{commit}}",
52
+ ],
53
+ check=False,
54
+ capture_output=True,
55
+ text=True,
56
+ )
57
+ if completed_command.returncode != 0:
58
+ error_message = completed_command.stderr.strip() or "unable to resolve base-ref"
59
+ raise ValueError(error_message)
60
+ return completed_command.stdout.strip()
61
+
62
+
63
+ def _build_packet_payload(
64
+ slug: str, base_ref: str, base_commit: str
65
+ ) -> dict[str, object]:
66
+ """Build the schema-shaped packet manifest without repository assumptions."""
67
+ return {
68
+ "schema_version": 1,
69
+ "slug": slug,
70
+ "status": "draft",
71
+ "request": f"Plan work for {slug}.",
72
+ "allowed_files": [],
73
+ "sources": [],
74
+ "decisions": [
75
+ f"Base ref {base_ref} resolves to commit {base_commit}.",
76
+ "Scope is pending planner review.",
77
+ ],
78
+ "open_questions": [
79
+ "What repository behavior and files does the request require?"
80
+ ],
81
+ "tasks": [
82
+ {
83
+ "id": "task-1",
84
+ "deliverable": "Planner-defined deliverable",
85
+ "allowed_files": ["docs/plans/" + slug],
86
+ "acceptance_command": "Planner-defined acceptance command",
87
+ "test_command": "Planner-defined test command",
88
+ "verification_command": "Planner-defined verification command",
89
+ "commit": 1,
90
+ }
91
+ ],
92
+ "validation": {
93
+ "schema_valid": False,
94
+ "boundary_valid": False,
95
+ "markdown_matches": False,
96
+ "validated_by": "native-plan-to-pr",
97
+ },
98
+ }
99
+
100
+
101
+ def _write_packet_file(packet_file: Path, packet_text: str) -> None:
102
+ """Write one packet file using stable UTF-8 content."""
103
+ packet_file.write_text(packet_text, encoding="utf-8", newline="\n")
104
+
105
+
106
+ def create_packet(repo_root: Path, slug: str, base_ref: str) -> Path:
107
+ """Create and return a new packet directory under the repository plans path.
108
+
109
+ Args:
110
+ repo_root: Repository containing the plans directory.
111
+ slug: Packet directory slug.
112
+ base_ref: Git reference used for the packet decision record.
113
+ Returns:
114
+ The created packet directory.
115
+ Raises:
116
+ ValueError: If the slug or base reference is invalid.
117
+ FileExistsError: If the packet directory already exists.
118
+ """
119
+ _validate_slug(slug)
120
+ resolved_repo_root = repo_root.resolve()
121
+ base_commit = _resolve_base_commit(resolved_repo_root, base_ref)
122
+ packet_directory = resolved_repo_root / "docs" / "plans" / slug
123
+ if packet_directory.exists():
124
+ raise FileExistsError(f"packet already exists: {packet_directory}")
125
+
126
+ for each_directory in (
127
+ packet_directory,
128
+ packet_directory / "requirements",
129
+ packet_directory / "tasks",
130
+ ):
131
+ each_directory.mkdir(parents=True, exist_ok=True)
132
+
133
+ packet_payload = _build_packet_payload(slug, base_ref, base_commit)
134
+ packet_json = (
135
+ json.dumps(packet_payload, indent=PACKET_JSON_INDENT_LEVEL, sort_keys=True)
136
+ + "\n"
137
+ )
138
+ packet_files = {
139
+ "packet.json": packet_json,
140
+ "context.md": "# Context\n\nThe planner records repository facts and source references here.\n",
141
+ "plan.md": "# Plan\n\nThe planner records ordered implementation decisions here.\n",
142
+ "tasks.md": "# Tasks\n\nThe planner records one deliverable per task here.\n",
143
+ "handoff.md": "# Handoff\n\nThe planner records the approved host task seed here.\n",
144
+ "requirements/README.md": "# Requirements\n\nThe planner records confirmed requirements here.\n",
145
+ "tasks/README.md": "# Task Records\n\nThe planner records task details here.\n",
146
+ }
147
+ for each_relative_path, each_file_text in packet_files.items():
148
+ _write_packet_file(packet_directory / each_relative_path, each_file_text)
149
+ return packet_directory
150
+
151
+
152
+ def main() -> int:
153
+ """Create a packet and print its repository-relative path.
154
+
155
+ Returns:
156
+ The process status code.
157
+ """
158
+ parsed_arguments = _parse_arguments()
159
+ try:
160
+ packet_directory = create_packet(
161
+ parsed_arguments.repo_root,
162
+ parsed_arguments.slug,
163
+ parsed_arguments.base_ref,
164
+ )
165
+ except (FileExistsError, OSError, ValueError) as packet_error:
166
+ print(str(packet_error), file=sys.stderr)
167
+ return PACKET_CREATION_ERROR_EXIT_CODE
168
+ print(packet_directory)
169
+ return 0
170
+
171
+
172
+ if __name__ == "__main__":
173
+ raise SystemExit(main())
@@ -0,0 +1,102 @@
1
+ """Behavior tests for the native packet skeleton CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import importlib.util
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ import pytest
12
+
13
+ script_path = Path(__file__).with_name("create_packet.py")
14
+ script_spec = importlib.util.spec_from_file_location("create_packet", script_path)
15
+ assert script_spec is not None
16
+ assert script_spec.loader is not None
17
+ script_module = importlib.util.module_from_spec(script_spec)
18
+ sys.modules["create_packet"] = script_module
19
+ script_spec.loader.exec_module(script_module)
20
+ create_packet = script_module.create_packet
21
+
22
+
23
+ def initialize_repository(repository_path: Path) -> str:
24
+ """Create a temporary Git repository and return its commit hash."""
25
+ subprocess.run(
26
+ ["git", "-C", str(repository_path), "init"], check=True, capture_output=True
27
+ )
28
+ subprocess.run(
29
+ [
30
+ "git",
31
+ "-C",
32
+ str(repository_path),
33
+ "config",
34
+ "user.email",
35
+ "test@example.invalid",
36
+ ],
37
+ check=True,
38
+ )
39
+ subprocess.run(
40
+ ["git", "-C", str(repository_path), "config", "user.name", "Test User"],
41
+ check=True,
42
+ )
43
+ (repository_path / "README.md").write_text("repository\n", encoding="utf-8")
44
+ subprocess.run(["git", "-C", str(repository_path), "add", "README.md"], check=True)
45
+ subprocess.run(
46
+ ["git", "-C", str(repository_path), "commit", "-m", "initial"],
47
+ check=True,
48
+ capture_output=True,
49
+ )
50
+ return subprocess.check_output(
51
+ ["git", "-C", str(repository_path), "rev-parse", "HEAD"], text=True
52
+ ).strip()
53
+
54
+
55
+ def test_create_packet_writes_deterministic_skeleton(tmp_path: Path) -> None:
56
+ """A packet contains the schema fields, base commit, and stable files."""
57
+ base_commit = initialize_repository(tmp_path)
58
+
59
+ packet_directory = create_packet(tmp_path, "sample-plan", "HEAD")
60
+
61
+ packet_payload = json.loads(
62
+ (packet_directory / "packet.json").read_text(encoding="utf-8")
63
+ )
64
+ assert packet_payload["status"] == "draft"
65
+ assert base_commit in packet_payload["decisions"][0]
66
+ assert (packet_directory / "requirements" / "README.md").is_file()
67
+ assert (packet_directory / "tasks" / "README.md").is_file()
68
+ assert sorted(path.name for path in packet_directory.glob("*.md")) == [
69
+ "context.md",
70
+ "handoff.md",
71
+ "plan.md",
72
+ "tasks.md",
73
+ ]
74
+
75
+
76
+ @pytest.mark.parametrize(
77
+ "unsafe_slug", ["../escape", "Upper", "with space", "-leading"]
78
+ )
79
+ def test_create_packet_rejects_unsafe_slug(tmp_path: Path, unsafe_slug: str) -> None:
80
+ """Unsafe slugs fail before a plan directory is created."""
81
+ initialize_repository(tmp_path)
82
+
83
+ with pytest.raises(ValueError):
84
+ create_packet(tmp_path, unsafe_slug, "HEAD")
85
+
86
+ assert not (tmp_path / "docs" / "plans").exists()
87
+
88
+
89
+ def test_create_packet_rejects_existing_packet(tmp_path: Path) -> None:
90
+ """Existing packet directories remain untouched."""
91
+ initialize_repository(tmp_path)
92
+ packet_directory = create_packet(tmp_path, "sample-plan", "HEAD")
93
+ packet_json_before_retry = (packet_directory / "packet.json").read_text(
94
+ encoding="utf-8"
95
+ )
96
+
97
+ with pytest.raises(FileExistsError):
98
+ create_packet(tmp_path, "sample-plan", "HEAD")
99
+
100
+ assert (packet_directory / "packet.json").read_text(
101
+ encoding="utf-8"
102
+ ) == packet_json_before_retry