claude-code-conductor 2.54.0__tar.gz → 2.55.1__tar.gz

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 (239) hide show
  1. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/_hook_utils.py +25 -0
  2. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/check_agent_invocation.py +4 -3
  3. claude_code_conductor-2.55.1/.claude/hooks/patterns_guard.py +127 -0
  4. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/planner_check.py +4 -3
  5. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/post_tool.py +1 -0
  6. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/pre_compact.py +17 -4
  7. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/pre_tool.py +6 -2
  8. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/recall_autorebuild.py +3 -1
  9. claude_code_conductor-2.55.1/.claude/hooks/report_contract_check.py +131 -0
  10. claude_code_conductor-2.55.1/.claude/hooks/session_mode_watch.py +276 -0
  11. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/tier_autoapply.py +5 -3
  12. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/worktree_guard.py +6 -2
  13. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/settings.json +20 -0
  14. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/promote-pattern/SKILL.md +7 -0
  15. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/ARCHITECTURE.md +4 -0
  16. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/CHANGELOG.md +38 -0
  17. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/PKG-INFO +21 -2
  18. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/README.md +20 -1
  19. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/__init__.py +1 -1
  20. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_hook_utils.py +132 -0
  21. claude_code_conductor-2.55.1/tests/test_hooks_stdin_idiom.py +357 -0
  22. claude_code_conductor-2.55.1/tests/test_patterns_guard.py +598 -0
  23. claude_code_conductor-2.55.1/tests/test_report_contract_check.py +620 -0
  24. claude_code_conductor-2.55.1/tests/test_session_mode_watch.py +967 -0
  25. claude_code_conductor-2.55.1/tests/test_settings_hooks_registration.py +292 -0
  26. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/CLAUDE.md +0 -0
  27. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/architect.md +0 -0
  28. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/code-reviewer.md +0 -0
  29. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/design-critic.md +0 -0
  30. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/developer.md +0 -0
  31. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/doc-writer.md +0 -0
  32. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/interviewer.md +0 -0
  33. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/planner.md +0 -0
  34. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/project-setup.md +0 -0
  35. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/security-reviewer.md +0 -0
  36. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/systematic-debugger.md +0 -0
  37. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/tester.md +0 -0
  38. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/wt_developer.md +0 -0
  39. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/wt_systematic-debugger.md +0 -0
  40. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/wt_tester.md +0 -0
  41. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/breaking-changes.txt +0 -0
  42. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/deletions.txt +0 -0
  43. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/autonomous-mode-onboarding.md +0 -0
  44. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/config-policy.md +0 -0
  45. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/nul-boundary.md +0 -0
  46. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/parallel-agents-setup.md +0 -0
  47. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/platform-adapters.md +0 -0
  48. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/settings.json.md +0 -0
  49. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/consolidate_memory.py +0 -0
  50. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/permission_handler.py +0 -0
  51. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/permission_handler_toast.py +0 -0
  52. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/recall_inject.py +0 -0
  53. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/restore_session.py +0 -0
  54. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/select_tier.py +0 -0
  55. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/session_start.py +0 -0
  56. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/session_stop.py +0 -0
  57. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/session_utils.py +0 -0
  58. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/statusline.py +0 -0
  59. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/stop.py +0 -0
  60. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/tier_gap_check.py +0 -0
  61. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/memory/.gitkeep +0 -0
  62. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/permission_rules.json +0 -0
  63. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/rules/promoted/index.md +0 -0
  64. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/autonomous-mode/SKILL.md +0 -0
  65. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/autonomous-mode/scripts/mode_line.py +0 -0
  66. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/brainstorm/SKILL.md +0 -0
  67. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/codex-review/SKILL.md +0 -0
  68. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/SKILL.md +0 -0
  69. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
  70. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
  71. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
  72. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
  73. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
  74. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
  75. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
  76. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
  77. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
  78. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/develop/SKILL.md +0 -0
  79. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/doc/SKILL.md +0 -0
  80. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/extract-lib/SKILL.md +0 -0
  81. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/init-session/SKILL.md +0 -0
  82. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/init-session/scripts/session_guard.py +0 -0
  83. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/mcp-config/SKILL.md +0 -0
  84. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/parallel-agents/SKILL.md +0 -0
  85. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/pattern-status/SKILL.md +0 -0
  86. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/recall/SKILL.md +0 -0
  87. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/report-timestamp/SKILL.md +0 -0
  88. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
  89. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/review-phase/SKILL.md +0 -0
  90. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/SKILL.md +0 -0
  91. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/reference.md +0 -0
  92. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
  93. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
  94. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/start/SKILL.md +0 -0
  95. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/state/.gitkeep +0 -0
  96. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.gitignore +0 -0
  97. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSE +0 -0
  98. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/fastembed-LICENSE +0 -0
  99. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/fastembed-NOTICE +0 -0
  100. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/numpy-LICENSE +0 -0
  101. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/onnxruntime-LICENSE +0 -0
  102. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
  103. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/hatch_build.py +0 -0
  104. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/pyproject.toml +0 -0
  105. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/__main__.py +0 -0
  106. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/_db_params.py +0 -0
  107. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/_excludes.py +0 -0
  108. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/_terminal.py +0 -0
  109. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/adapters.py +0 -0
  110. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli.py +0 -0
  111. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_ask.py +0 -0
  112. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_doctor.py +0 -0
  113. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_init.py +0 -0
  114. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_list.py +0 -0
  115. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_metrics.py +0 -0
  116. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_plan.py +0 -0
  117. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_recall.py +0 -0
  118. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_run.py +0 -0
  119. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_tier.py +0 -0
  120. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_update.py +0 -0
  121. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/db.py +0 -0
  122. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/embedding.py +0 -0
  123. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/gitutil.py +0 -0
  124. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/mcp_server.py +0 -0
  125. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrate.py +0 -0
  126. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/001_initial.sql +0 -0
  127. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
  128. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/003_tier_cost.sql +0 -0
  129. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/004_agent_outcomes.sql +0 -0
  130. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
  131. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/006_review_decisions_severity.sql +0 -0
  132. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/README.md +0 -0
  133. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/__init__.py +0 -0
  134. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/paths.py +0 -0
  135. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/plan_validator.py +0 -0
  136. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/platforms.py +0 -0
  137. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/pricing.py +0 -0
  138. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/question.py +0 -0
  139. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/recall_chunker.py +0 -0
  140. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/recall_index.py +0 -0
  141. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/usage_ingester.py +0 -0
  142. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/__init__.py +0 -0
  143. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/_pre_compact_helpers.py +0 -0
  144. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/conftest.py +0 -0
  145. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/README.md +0 -0
  146. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/mainline.jsonl +0 -0
  147. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
  148. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
  149. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/__init__.py +0 -0
  150. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_check_agent_invocation.py +0 -0
  151. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_consolidate_memory.py +0 -0
  152. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_permission_handler.py +0 -0
  153. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_permission_handler_toast.py +0 -0
  154. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
  155. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_planner_check.py +0 -0
  156. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_planner_check_dev.py +0 -0
  157. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_post_tool.py +0 -0
  158. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_pre_tool.py +0 -0
  159. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_recall_autorebuild.py +0 -0
  160. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_recall_inject.py +0 -0
  161. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_record_agent_outcome.py +0 -0
  162. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_record_review_decision.py +0 -0
  163. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_restore_session.py +0 -0
  164. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_review_hint_inject.py +0 -0
  165. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_select_tier.py +0 -0
  166. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_select_tier_escalation.py +0 -0
  167. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_session_start.py +0 -0
  168. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_session_stop.py +0 -0
  169. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_session_utils.py +0 -0
  170. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
  171. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_settings_tier_autoapply_registration.py +0 -0
  172. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_similarity_boost.py +0 -0
  173. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_statusline.py +0 -0
  174. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_statusline_template_sync.py +0 -0
  175. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_sync_check.py +0 -0
  176. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_template_guard.py +0 -0
  177. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_tier_autoapply.py +0 -0
  178. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_tier_gap_check.py +0 -0
  179. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_tier_security_fixes.py +0 -0
  180. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/__init__.py +0 -0
  181. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/_skill_helpers.py +0 -0
  182. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
  183. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_init_session_no_task_type.py +0 -0
  184. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_marker_compliance.py +0 -0
  185. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_parallel_agents_skill_tier.py +0 -0
  186. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_planner_lightweight.py +0 -0
  187. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_recall_skill.py +0 -0
  188. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_session_backlog_reconciliation.py +0 -0
  189. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_session_guard.py +0 -0
  190. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_setup_templates.py +0 -0
  191. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
  192. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_start_skill_new_flow.py +0 -0
  193. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
  194. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_adapters.py +0 -0
  195. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_check_deletions.py +0 -0
  196. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_ask.py +0 -0
  197. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_doctor.py +0 -0
  198. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_doctor_launcher.py +0 -0
  199. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_entry.py +0 -0
  200. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_init.py +0 -0
  201. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_list.py +0 -0
  202. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_metrics.py +0 -0
  203. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_plan.py +0 -0
  204. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_recall.py +0 -0
  205. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_run.py +0 -0
  206. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_tier.py +0 -0
  207. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_update_breaking_changes.py +0 -0
  208. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_update_deletions.py +0 -0
  209. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_db.py +0 -0
  210. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_docstring_consistency.py +0 -0
  211. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_embedding.py +0 -0
  212. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_excludes.py +0 -0
  213. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_extract_breaking_changes.py +0 -0
  214. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_gitutil.py +0 -0
  215. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_mcp_server_elicit.py +0 -0
  216. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_migrate.py +0 -0
  217. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_mode_line.py +0 -0
  218. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_no_bare_python_launcher.py +0 -0
  219. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_nul_boundary_lint.py +0 -0
  220. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_paths.py +0 -0
  221. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_plan_validator.py +0 -0
  222. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_pre_compact.py +0 -0
  223. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_pre_tool_hook.py +0 -0
  224. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_precompact_additional.py +0 -0
  225. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_precompact_toctou_fixes.py +0 -0
  226. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_pricing.py +0 -0
  227. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_recall_chunker.py +0 -0
  228. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_recall_index.py +0 -0
  229. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_references_migration.py +0 -0
  230. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_session_utils_additional.py +0 -0
  231. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_skill_no_builtin_conflict.py +0 -0
  232. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_statusline.py +0 -0
  233. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_stop_additional.py +0 -0
  234. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_stop_hook.py +0 -0
  235. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_stop_precompact_fixes.py +0 -0
  236. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_sync_template_stop.py +0 -0
  237. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_template_pre_tool_hook.py +0 -0
  238. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_usage_ingester.py +0 -0
  239. {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_worktree_guard.py +0 -0
@@ -7,8 +7,12 @@ PYTHONPATH に追加してから `from _hook_utils import ...` する経路を
7
7
 
8
8
  ## Exports
9
9
 
10
+ - ``norm_component(s)`` — パス成分を正規化する(`.lower().rstrip('. ')`)。
11
+ ディレクトリ比較・拡張子判定の際にケース非依存化・末尾ドット/スペース除去を行う。
10
12
  - ``write_debug_log(log_path, line)`` — ``C3_HOOK_DEBUG=1`` のときのみログを追記する
11
13
  fail-safe な書き込みヘルパー。
14
+ - ``sanitize_for_terminal(text)`` — 端末出力(stderr / stdout JSON)へ載せる前に
15
+ C0 + DEL + C1 制御文字を除去するヘルパー。
12
16
  """
13
17
 
14
18
  from __future__ import annotations
@@ -32,6 +36,27 @@ DEBUG_ENV = "C3_HOOK_DEBUG"
32
36
  _CONTROL_CHARS_RE = re.compile(r"[\x00-\x1f\x7f-\x9f]")
33
37
 
34
38
 
39
+ def norm_component(s: str) -> str:
40
+ """パス成分を正規化する(ケース非依存化・末尾ドット/スペース除去)。
41
+
42
+ Windows・macOS の既定(ケース非依存 FS)では大文字・小文字の違い、
43
+ 末尾のドットやスペースは同一実体を示す。複数ディレクトリ・拡張子判定の際に
44
+ `.lower().rstrip('. ')` で正規化してから比較する。
45
+ """
46
+ return str(s).lower().rstrip('. ')
47
+
48
+
49
+ def sanitize_for_terminal(text: str) -> str:
50
+ """端末インジェクション対策: C0 制御文字 / DEL / C1 制御文字を除去する。
51
+
52
+ - 除去範囲は ``_CONTROL_CHARS_RE``(C0 + DEL + C1)と共通。ANSI エスケープの
53
+ ESC (``\\x1b``) と CSI (``\\x9b``) の双方をカバーする。
54
+ - hook が stderr / stdout JSON(additionalContext)へ外部由来の文字列
55
+ (ファイル名など)を載せる前に通す。
56
+ """
57
+ return _CONTROL_CHARS_RE.sub("", str(text))
58
+
59
+
35
60
  def write_debug_log(log_path: Path, line: str) -> None:
36
61
  """``C3_HOOK_DEBUG=1`` のとき、ログファイルに ``ISO8601 line`` を 1 行追記する。
37
62
 
@@ -41,6 +41,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
41
41
  from _hook_utils import write_debug_log # noqa: E402
42
42
 
43
43
  try:
44
+ sys.stdin.reconfigure(encoding="utf-8")
44
45
  sys.stdout.reconfigure(encoding="utf-8")
45
46
  sys.stderr.reconfigure(encoding="utf-8")
46
47
  except AttributeError:
@@ -53,8 +54,8 @@ REVIEWER_TYPES = frozenset({"code-reviewer", "security-reviewer"})
53
54
  PROJECT_ROOT = Path(__file__).resolve().parents[2]
54
55
  DEBUG_LOG_PATH = PROJECT_ROOT / ".claude" / "tmp" / "agent_hook_debug.log"
55
56
 
56
- # DoS 防御の読み取り上限(hook はローカル前提だが多層防御として明示する)。
57
- _STDIN_READ_LIMIT_BYTES = 1 * 1024 * 1024 # 1 MiB
57
+ # DoS 防御の読み取り上限(文字数ベース)。hook はローカル前提だが多層防御として明示する。
58
+ _STDIN_READ_LIMIT_CHARS = 1 * 1024 * 1024 # 1M chars
58
59
 
59
60
 
60
61
  def _escape_for_log(value: str) -> str:
@@ -64,7 +65,7 @@ def _escape_for_log(value: str) -> str:
64
65
 
65
66
  def main() -> None:
66
67
  try:
67
- payload = json.loads(sys.stdin.read(_STDIN_READ_LIMIT_BYTES))
68
+ payload = json.loads(sys.stdin.read(_STDIN_READ_LIMIT_CHARS))
68
69
  except (json.JSONDecodeError, ValueError):
69
70
  sys.exit(0)
70
71
 
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ """PreToolUse hook: patterns.json への直接編集を事故防止ガード(P1・配布対象)。
3
+
4
+ Write / Edit ツールで `.claude/memory/patterns.json` への書き込みを検出し、
5
+ 許可フラグがなければブロックする(TTL 型・600 秒)。正規経路は session.tmp 経由の
6
+ promote-pattern skill。意図的な編集は `.claude/state/patterns_guard_allow.flag` で
7
+ 一時許可できる。
8
+
9
+ ## 射程の明記(設計の限界)
10
+
11
+ - 対象: Write / Edit ツール呼び出しのみ
12
+ - 対象外: Bash(python -c・sed 等)/ NotebookEdit 経由の書き込み
13
+ - worktree 実行時: 基準が main/worktree のどちらになるかは環境解決に依存
14
+ (本 hook の基準は main の patterns.json 本体の事故防止。main 側保護が
15
+ 保護されない場合は検知が働かない限界あり)
16
+ - resolve() による大小文字の正準化は実在ファイルのみ(Windows nt._getfinalpathname)。
17
+ patterns.json が未作成の初期環境(c3 init 直後)ではケース違い指定が素通りする
18
+ - Windows の末尾ドット・スペース正規化も実在パス限定。非実在パスでは本 hook が検知できない限界
19
+ """
20
+
21
+ import json
22
+ import os
23
+ import sys
24
+ import time
25
+ from pathlib import Path
26
+
27
+ # 共通ヘルパー (_hook_utils.sanitize_for_terminal) を hooks/ 経由で import するため、
28
+ # このスクリプトのディレクトリを PYTHONPATH に追加する。
29
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
30
+ from _hook_utils import sanitize_for_terminal # noqa: E402
31
+
32
+ try:
33
+ sys.stdin.reconfigure(encoding='utf-8')
34
+ sys.stdout.reconfigure(encoding='utf-8')
35
+ sys.stderr.reconfigure(encoding='utf-8')
36
+ except AttributeError:
37
+ pass
38
+
39
+ # 許可フラグの有効期間(秒)。判定式はこの定数を参照する。
40
+ TTL_SECONDS = 600
41
+
42
+
43
+ def main():
44
+ # bypass: 恒久 disable 環境変数の確認
45
+ if os.environ.get('C3_PATTERNS_GUARD_DISABLE') == '1':
46
+ sys.exit(0)
47
+
48
+ # JSON payload の読み込み(fail-open)
49
+ try:
50
+ payload = json.loads(sys.stdin.read())
51
+ except (json.JSONDecodeError, ValueError):
52
+ sys.exit(0)
53
+
54
+ # tool_name の確認(Write/Edit 以外は対象外)
55
+ tool_name = payload.get('tool_name', '')
56
+ if tool_name not in ('Write', 'Edit'):
57
+ sys.exit(0)
58
+
59
+ # file_path の抽出(キー欠落は fail-open)
60
+ file_path = payload.get('tool_input', {}).get('file_path', '')
61
+ if not file_path:
62
+ sys.exit(0)
63
+
64
+ # プロジェクトルートと保護対象パスの導出(Path(__file__) ベース)
65
+ hook_dir = Path(__file__).resolve().parent
66
+ project_root = hook_dir.parent.parent
67
+ flag_path = project_root / ".claude" / "state" / "patterns_guard_allow.flag"
68
+
69
+ # file_path と保護対象を同じ正規化(realpath)にそろえて比較する。
70
+ # NUL byte 混入など不正なパス文字列は OSError / ValueError になるため fail-open。
71
+ try:
72
+ protected_path = (
73
+ project_root / ".claude" / "memory" / "patterns.json"
74
+ ).resolve()
75
+ if os.path.isabs(file_path):
76
+ resolved = Path(file_path).resolve()
77
+ else:
78
+ # 相対パスは CWD を基準に解決(worktree 実行時は worktree CWD)
79
+ resolved = (Path.cwd() / file_path).resolve()
80
+ except (OSError, ValueError):
81
+ sys.exit(0)
82
+
83
+ # realpath 一致チェック
84
+ if resolved != protected_path:
85
+ sys.exit(0)
86
+
87
+ # patterns.json への書き込みが検出された → フラグチェック
88
+ # フラグが存在し TTL 内なら allow(削除しない=複数操作可)
89
+ #
90
+ # TOCTOU 免責: exists() / getmtime() / unlink() の間にフラグが差し替えられる
91
+ # 競合は原理的に残る。本 hook は「事故防止ガード」であり悪意ある同時操作に対する
92
+ # セキュリティ境界ではないため、この競合は許容する。
93
+ if flag_path.exists():
94
+ mtime = os.path.getmtime(flag_path)
95
+ age = time.time() - mtime
96
+ if age <= TTL_SECONDS:
97
+ # TTL 内: 許可
98
+ sys.exit(0)
99
+ else:
100
+ # TTL 超過: フラグを削除して block
101
+ try:
102
+ flag_path.unlink()
103
+ except OSError:
104
+ pass
105
+
106
+ # ブロック: stderr と exit 2
107
+ flag_create_instruction = (
108
+ f".claude/state/patterns_guard_allow.flag を作成してください(空ファイルで可)。\n"
109
+ f" 例 Bash: touch {sanitize_for_terminal(str(flag_path))}\n"
110
+ f" 例 PowerShell: New-Item -ItemType File -Force "
111
+ f"{sanitize_for_terminal(str(flag_path))}"
112
+ )
113
+
114
+ message = (
115
+ f"[PatternGuard BLOCK] patterns.json への直接編集がブロックされました。\n"
116
+ f"正規経路: session.tmp の「##残タスク」へ記載後、promote-pattern skill を実行してください\n"
117
+ f" または LLM が patterns.json を直接編集する場合は以下を実行:\n"
118
+ f"{flag_create_instruction}\n"
119
+ f"恒久 bypass: C3_PATTERNS_GUARD_DISABLE=1 環境変数を設定してください"
120
+ )
121
+
122
+ print(message, file=sys.stderr)
123
+ sys.exit(2)
124
+
125
+
126
+ if __name__ == '__main__':
127
+ main()
@@ -41,6 +41,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
41
41
  from _hook_utils import write_debug_log # noqa: E402
42
42
 
43
43
  try:
44
+ sys.stdin.reconfigure(encoding="utf-8")
44
45
  sys.stdout.reconfigure(encoding="utf-8")
45
46
  sys.stderr.reconfigure(encoding="utf-8")
46
47
  except AttributeError:
@@ -58,8 +59,8 @@ except ImportError:
58
59
  PROJECT_ROOT = Path(__file__).resolve().parents[2]
59
60
  DEBUG_LOG_PATH = PROJECT_ROOT / ".claude" / "tmp" / "planner_check_debug.log"
60
61
 
61
- # DoS 防御の読み取り上限(hook はローカル前提だが多層防御として明示する)。
62
- _STDIN_READ_LIMIT_BYTES = 1 * 1024 * 1024 # 1 MiB
62
+ # DoS 防御の読み取り上限(stdin は文字数ベース)。hook はローカル前提だが多層防御として明示する。
63
+ _STDIN_READ_LIMIT_CHARS = 1 * 1024 * 1024 # 1M chars
63
64
  _FILE_READ_LIMIT_BYTES = 512 * 1024 # 512 KiB
64
65
 
65
66
 
@@ -230,7 +231,7 @@ def _check_r6_reviewer_absence(tasks: list) -> list[str]:
230
231
 
231
232
  def main() -> None:
232
233
  try:
233
- payload = json.loads(sys.stdin.read(_STDIN_READ_LIMIT_BYTES))
234
+ payload = json.loads(sys.stdin.read(_STDIN_READ_LIMIT_CHARS))
234
235
  except (json.JSONDecodeError, ValueError):
235
236
  sys.exit(0)
236
237
 
@@ -21,6 +21,7 @@ import re
21
21
  import sys
22
22
 
23
23
  try:
24
+ sys.stdin.reconfigure(encoding='utf-8')
24
25
  sys.stdout.reconfigure(encoding='utf-8')
25
26
  sys.stderr.reconfigure(encoding='utf-8')
26
27
  except AttributeError:
@@ -12,10 +12,23 @@ import re
12
12
  import sys
13
13
  from datetime import datetime, timedelta, timezone
14
14
 
15
- sys.stdout.reconfigure(encoding='utf-8')
16
- sys.stderr.reconfigure(encoding='utf-8')
17
-
18
- from session_utils import SESSION_JSON_MARKER, append_checkpoint, is_worktree, SESSIONS_DIR
15
+ try:
16
+ sys.stdin.reconfigure(encoding='utf-8')
17
+ sys.stdout.reconfigure(encoding='utf-8')
18
+ sys.stderr.reconfigure(encoding='utf-8')
19
+ except AttributeError:
20
+ pass
21
+
22
+ from session_utils import append_checkpoint, is_worktree, SESSIONS_DIR
23
+ # SESSION_JSON_MARKER は本ファイルのロジックでは未参照だが、過去レビュー(Code Medium-3a)の
24
+ # 契約として module-level 定数の存在を tests/test_stop_precompact_fixes.py が要求する再エクスポート。
25
+ # 未使用に見えても削除しないこと。
26
+ # `# noqa: F401` は flake8/ruff 固有の規約で生の pyflakes は解釈しないため、
27
+ # 代わりに __all__ へ列挙して re-export であることを明示する(pyflakes は
28
+ # __all__ 掲載名を使用済みとみなすため、警告が実際に抑制される)。
29
+ from session_utils import SESSION_JSON_MARKER
30
+
31
+ __all__ = ["SESSION_JSON_MARKER"]
19
32
 
20
33
 
21
34
  # デバウンス窓幅(秒)。直近の PreCompact checkpoint からこの秒数以内の再実行は
@@ -7,8 +7,12 @@ import re
7
7
  import shlex
8
8
  import sys
9
9
 
10
- sys.stdout.reconfigure(encoding='utf-8')
11
- sys.stderr.reconfigure(encoding='utf-8')
10
+ try:
11
+ sys.stdin.reconfigure(encoding='utf-8')
12
+ sys.stdout.reconfigure(encoding='utf-8')
13
+ sys.stderr.reconfigure(encoding='utf-8')
14
+ except AttributeError:
15
+ pass
12
16
 
13
17
 
14
18
  def _is_rm_rf(tokens: list[str], rm_idx: int) -> bool:
@@ -23,8 +23,10 @@ import sys
23
23
  import time
24
24
  from pathlib import Path
25
25
 
26
- # CLAUDE.md §9-3: reconfigure stdout/stderr to UTF-8 on Windows.
26
+ # CLAUDE.md §9-3: reconfigure stdin/stdout/stderr to UTF-8 on Windows.
27
27
  try:
28
+ if sys.stdin and hasattr(sys.stdin, "reconfigure"):
29
+ sys.stdin.reconfigure(encoding="utf-8")
28
30
  if sys.stdout and hasattr(sys.stdout, "reconfigure"):
29
31
  sys.stdout.reconfigure(encoding="utf-8")
30
32
  if sys.stderr and hasattr(sys.stderr, "reconfigure"):
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env python3
2
+ """PostToolUse hook: タイムスタンプ報告契約の検査(P2・配布対象)。
3
+
4
+ `.claude/reports/` 直下への Write で、以下 4 prefix のタイムスタンプ契約逸脱を warn する:
5
+ - requirements-report-
6
+ - architecture-report-
7
+ - plan-report-
8
+ - design-review-report-
9
+
10
+ 判定は basename 全体のフルマッチ `^{prefix}\\d{8}-\\d{6}\\.md$` で行う
11
+ (prefix 一致と suffix 一致の独立 2 条件ではない。`plan-report-badname-extra-20260725-180252.md`
12
+ のように prefix と タイムスタンプの間へ任意文字列を挟む名前も逸脱として検出するため)。
13
+ 不一致の場合は 2 経路で警告: stderr(人間向け)+ stdout JSON(LLM向け)。
14
+
15
+ ## ディレクトリ判定とケース非依存化
16
+
17
+ reports ディレクトリの判定は `.claude/reports/` 直下かどうかを名前ベースで確認する。
18
+ Windows・macOS の既定(ケース非依存 FS)では `.CLAUDE` や `REPORTS` は `.claude` / `reports` と
19
+ 同一実体の別名であり、名前比較は `.lower()` を使用してケース非依存で行う。
20
+ basename(prefix・タイムスタンプ・`.md`)の判定は厳密に行う(`re.ASCII` で Unicode 偽装を防止)。
21
+
22
+ ## 出力契約(PostToolUse warn の標準形)
23
+
24
+ exit 0 でも stdout JSON が LLM コンテキストへ additionalContext として注入される。
25
+ stderr は人間にのみ届く。詳細は Claude Code 公式仕様を参照:
26
+ https://code.claude.com/docs/en/hooks
27
+ """
28
+
29
+ import json
30
+ import os
31
+ import re
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ # 共通ヘルパー (_hook_utils.sanitize_for_terminal, norm_component) を hooks/ 経由で import するため、
36
+ # このスクリプトのディレクトリを PYTHONPATH に追加する。
37
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
38
+ from _hook_utils import norm_component, sanitize_for_terminal # noqa: E402
39
+
40
+ try:
41
+ sys.stdin.reconfigure(encoding='utf-8')
42
+ sys.stdout.reconfigure(encoding='utf-8')
43
+ sys.stderr.reconfigure(encoding='utf-8')
44
+ except AttributeError:
45
+ pass
46
+
47
+
48
+ def main():
49
+ # JSON payload の読み込み(fail-open)
50
+ try:
51
+ payload = json.loads(sys.stdin.read())
52
+ except (json.JSONDecodeError, ValueError):
53
+ sys.exit(0)
54
+
55
+ # tool_name の確認(Write 以外は対象外=新規命名は Write でのみ発生)
56
+ tool_name = payload.get('tool_name', '')
57
+ if tool_name != 'Write':
58
+ sys.exit(0)
59
+
60
+ # file_path の抽出(キー欠落は fail-open)
61
+ file_path = payload.get('tool_input', {}).get('file_path', '')
62
+ if not file_path:
63
+ sys.exit(0)
64
+
65
+ # `.claude/reports/` 直下かどうかの判定
66
+ path_obj = Path(file_path)
67
+ basename = path_obj.name
68
+ parent = path_obj.parent
69
+
70
+ # 構造判定: 親が `reports` かつ祖父が `.claude`(= `.claude/reports/` 直下)。
71
+ # 上流に同名の `reports/` があっても誤アンカーしない(resolve は使わない)。
72
+ # ケース非依存 FS(Windows・macOS)では `.CLAUDE` や `REPORTS` は `.claude` / `reports` と
73
+ # 同一実体の別名のため、比較は norm_component() で正規化してから行う。
74
+ # Windows の末尾ドット・スペースも norm_component() が除去する。
75
+ # 例: `/foo/.claude/reports/plan-report-20260725-180252.md` → 対象
76
+ # 例: `/foo/.claude/reports/archive/plan-report-final.md` → 対象外(直下でない)
77
+ if norm_component(parent.name) != 'reports' or norm_component(parent.parent.name) != '.claude':
78
+ sys.exit(0)
79
+
80
+ # strict-4: 対象 prefix の確認
81
+ strict4_prefixes = [
82
+ 'requirements-report-',
83
+ 'architecture-report-',
84
+ 'plan-report-',
85
+ 'design-review-report-',
86
+ ]
87
+
88
+ matched_prefix = None
89
+ for prefix in strict4_prefixes:
90
+ if basename.startswith(prefix):
91
+ matched_prefix = prefix
92
+ break
93
+ if matched_prefix is None:
94
+ sys.exit(0)
95
+
96
+ # basename 全体のフルマッチ確認(`{prefix}YYYYMMDD-HHMMSS.md`)
97
+ # re.ASCII を指定してタイムスタンプを ASCII 数字に限定し、全角数字による偽装を防止する。
98
+ contract_pattern = re.compile(re.escape(matched_prefix) + r'\d{8}-\d{6}\.md', re.ASCII)
99
+ if contract_pattern.fullmatch(basename):
100
+ # 正しい形式
101
+ sys.exit(0)
102
+
103
+ # 違反: 警告を出す(2 経路)
104
+ # stderr(人間向け)
105
+ print(
106
+ f'[ReportContract WARN] {sanitize_for_terminal(basename)} '
107
+ f'のファイル名形式が違反しています。'
108
+ f'期待形式: {{prefix}}YYYYMMDD-HHMMSS.md\n'
109
+ f'採番は report-timestamp skill を使用してください。',
110
+ file=sys.stderr
111
+ )
112
+
113
+ # stdout JSON(LLM向け)
114
+ context = (
115
+ f'ファイル名: {sanitize_for_terminal(basename)}\n'
116
+ f'期待形式: {{prefix}}YYYYMMDD-HHMMSS.md\n'
117
+ f'採番方法: report-timestamp skill で取得したタイムスタンプを使用してください。'
118
+ )
119
+ output = {
120
+ 'hookSpecificOutput': {
121
+ 'hookEventName': 'PostToolUse',
122
+ 'additionalContext': context
123
+ }
124
+ }
125
+ print(json.dumps(output), file=sys.stdout)
126
+
127
+ sys.exit(0)
128
+
129
+
130
+ if __name__ == '__main__':
131
+ main()