claude-code-conductor 2.58.0__tar.gz → 2.59.0__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 (245) hide show
  1. claude_code_conductor-2.59.0/.claude/hooks/restore_session.py +361 -0
  2. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/SKILL.md +6 -0
  3. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/init-session/SKILL.md +2 -0
  4. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/CHANGELOG.md +18 -0
  5. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/PKG-INFO +1 -1
  6. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/__init__.py +1 -1
  7. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_restore_session.py +688 -0
  8. claude_code_conductor-2.58.0/.claude/hooks/restore_session.py +0 -198
  9. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/.gitignore +0 -0
  10. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/CLAUDE.md +0 -0
  11. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/architect.md +0 -0
  12. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/code-reviewer.md +0 -0
  13. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/design-critic.md +0 -0
  14. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/developer.md +0 -0
  15. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/doc-writer.md +0 -0
  16. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/interviewer.md +0 -0
  17. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/planner.md +0 -0
  18. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/project-setup.md +0 -0
  19. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/security-reviewer.md +0 -0
  20. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/systematic-debugger.md +0 -0
  21. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/tester.md +0 -0
  22. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/wt_developer.md +0 -0
  23. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/wt_systematic-debugger.md +0 -0
  24. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/agents/wt_tester.md +0 -0
  25. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/breaking-changes.txt +0 -0
  26. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/deletions.txt +0 -0
  27. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/docs/autonomous-mode-onboarding.md +0 -0
  28. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/docs/config-policy.md +0 -0
  29. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/docs/nul-boundary.md +0 -0
  30. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/docs/parallel-agents-setup.md +0 -0
  31. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/docs/platform-adapters.md +0 -0
  32. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/docs/settings.json.md +0 -0
  33. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/_hook_utils.py +0 -0
  34. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/check_agent_invocation.py +0 -0
  35. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/consolidate_memory.py +0 -0
  36. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/patterns_guard.py +0 -0
  37. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/permission_handler.py +0 -0
  38. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/permission_handler_toast.py +0 -0
  39. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/planner_check.py +0 -0
  40. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/post_tool.py +0 -0
  41. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/pre_compact.py +0 -0
  42. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/pre_tool.py +0 -0
  43. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/recall_autorebuild.py +0 -0
  44. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/recall_inject.py +0 -0
  45. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/report_contract_check.py +0 -0
  46. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/select_tier.py +0 -0
  47. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/session_mode_watch.py +0 -0
  48. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/session_start.py +0 -0
  49. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/session_stop.py +0 -0
  50. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/session_utils.py +0 -0
  51. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/statusline.py +0 -0
  52. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/stop.py +0 -0
  53. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/tier_autoapply.py +0 -0
  54. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/tier_gap_check.py +0 -0
  55. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/hooks/worktree_guard.py +0 -0
  56. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/memory/.gitkeep +0 -0
  57. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/permission_rules.json +0 -0
  58. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/rules/promoted/index.md +0 -0
  59. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/settings.json +0 -0
  60. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/autonomous-mode/SKILL.md +0 -0
  61. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/autonomous-mode/scripts/mode_line.py +0 -0
  62. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/brainstorm/SKILL.md +0 -0
  63. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/codex-review/SKILL.md +0 -0
  64. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
  65. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
  66. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
  67. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
  68. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
  69. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
  70. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
  71. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
  72. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
  73. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/develop/SKILL.md +0 -0
  74. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/doc/SKILL.md +0 -0
  75. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/extract-lib/SKILL.md +0 -0
  76. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
  77. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/mcp-config/SKILL.md +0 -0
  78. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
  79. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/pattern-status/SKILL.md +0 -0
  80. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
  81. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/recall/SKILL.md +0 -0
  82. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
  83. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
  84. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/review-phase/SKILL.md +0 -0
  85. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/setup/SKILL.md +0 -0
  86. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/setup/reference.md +0 -0
  87. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
  88. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
  89. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/skills/start/SKILL.md +0 -0
  90. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.claude/state/.gitkeep +0 -0
  91. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/.gitignore +0 -0
  92. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/ARCHITECTURE.md +0 -0
  93. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/LICENSE +0 -0
  94. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/LICENSES/fastembed-LICENSE +0 -0
  95. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/LICENSES/fastembed-NOTICE +0 -0
  96. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/LICENSES/numpy-LICENSE +0 -0
  97. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/LICENSES/onnxruntime-LICENSE +0 -0
  98. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
  99. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/README.md +0 -0
  100. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/hatch_build.py +0 -0
  101. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/pyproject.toml +0 -0
  102. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/__main__.py +0 -0
  103. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/_db_params.py +0 -0
  104. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/_excludes.py +0 -0
  105. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/_terminal.py +0 -0
  106. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/adapters.py +0 -0
  107. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli.py +0 -0
  108. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_ask.py +0 -0
  109. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_doctor.py +0 -0
  110. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_init.py +0 -0
  111. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_list.py +0 -0
  112. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_metrics.py +0 -0
  113. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_plan.py +0 -0
  114. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_recall.py +0 -0
  115. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_run.py +0 -0
  116. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_tier.py +0 -0
  117. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/cli_update.py +0 -0
  118. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/db.py +0 -0
  119. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/embedding.py +0 -0
  120. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/gitutil.py +0 -0
  121. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/mcp_server.py +0 -0
  122. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrate.py +0 -0
  123. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/001_initial.sql +0 -0
  124. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
  125. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/003_tier_cost.sql +0 -0
  126. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
  127. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
  128. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/006_review_decisions_severity.sql +0 -0
  129. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/README.md +0 -0
  130. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/migrations/__init__.py +0 -0
  131. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/paths.py +0 -0
  132. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/plan_validator.py +0 -0
  133. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/platforms.py +0 -0
  134. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/pricing.py +0 -0
  135. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/question.py +0 -0
  136. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/recall_chunker.py +0 -0
  137. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/recall_index.py +0 -0
  138. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/src/c3/usage_ingester.py +0 -0
  139. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/__init__.py +0 -0
  140. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/_pre_compact_helpers.py +0 -0
  141. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/conftest.py +0 -0
  142. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/fixtures/usage/README.md +0 -0
  143. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/fixtures/usage/mainline.jsonl +0 -0
  144. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
  145. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
  146. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/__init__.py +0 -0
  147. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_check_agent_invocation.py +0 -0
  148. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_consolidate_memory.py +0 -0
  149. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_hook_utils.py +0 -0
  150. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_permission_handler.py +0 -0
  151. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_permission_handler_toast.py +0 -0
  152. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
  153. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_planner_check.py +0 -0
  154. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_planner_check_dev.py +0 -0
  155. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_post_tool.py +0 -0
  156. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_pre_tool.py +0 -0
  157. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_recall_autorebuild.py +0 -0
  158. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_recall_inject.py +0 -0
  159. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_record_agent_outcome.py +0 -0
  160. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_record_review_decision.py +0 -0
  161. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_review_hint_inject.py +0 -0
  162. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_select_tier.py +0 -0
  163. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_select_tier_escalation.py +0 -0
  164. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_session_start.py +0 -0
  165. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_session_stop.py +0 -0
  166. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_session_utils.py +0 -0
  167. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
  168. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_settings_tier_autoapply_registration.py +0 -0
  169. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_similarity_boost.py +0 -0
  170. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_statusline.py +0 -0
  171. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_statusline_template_sync.py +0 -0
  172. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_sync_check.py +0 -0
  173. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_template_guard.py +0 -0
  174. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_tier_autoapply.py +0 -0
  175. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_tier_gap_check.py +0 -0
  176. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/hooks/test_tier_security_fixes.py +0 -0
  177. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/__init__.py +0 -0
  178. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/_skill_helpers.py +0 -0
  179. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
  180. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_init_session_no_task_type.py +0 -0
  181. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_marker_compliance.py +0 -0
  182. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_parallel_agents_skill_tier.py +0 -0
  183. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_planner_lightweight.py +0 -0
  184. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_recall_skill.py +0 -0
  185. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
  186. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_session_guard.py +0 -0
  187. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_setup_templates.py +0 -0
  188. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
  189. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_start_skill_new_flow.py +0 -0
  190. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
  191. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_adapters.py +0 -0
  192. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_check_deletions.py +0 -0
  193. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_claude_gitignore.py +0 -0
  194. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_ask.py +0 -0
  195. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_doctor.py +0 -0
  196. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_doctor_launcher.py +0 -0
  197. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_entry.py +0 -0
  198. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_init.py +0 -0
  199. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_list.py +0 -0
  200. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_metrics.py +0 -0
  201. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_plan.py +0 -0
  202. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_recall.py +0 -0
  203. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_run.py +0 -0
  204. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_tier.py +0 -0
  205. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_update_breaking_changes.py +0 -0
  206. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_cli_update_deletions.py +0 -0
  207. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_db.py +0 -0
  208. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_docstring_consistency.py +0 -0
  209. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_embedding.py +0 -0
  210. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_excludes.py +0 -0
  211. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_extract_breaking_changes.py +0 -0
  212. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_gitutil.py +0 -0
  213. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_hooks_stdin_idiom.py +0 -0
  214. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_init_only_patterns.py +0 -0
  215. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_mcp_server_elicit.py +0 -0
  216. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_migrate.py +0 -0
  217. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_mode_line.py +0 -0
  218. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_no_bare_python_launcher.py +0 -0
  219. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_nul_boundary_lint.py +0 -0
  220. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_paths.py +0 -0
  221. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_patterns_guard.py +0 -0
  222. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_plan_validator.py +0 -0
  223. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_pre_compact.py +0 -0
  224. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_pre_tool_hook.py +0 -0
  225. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_precompact_additional.py +0 -0
  226. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_precompact_toctou_fixes.py +0 -0
  227. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_pricing.py +0 -0
  228. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_recall_chunker.py +0 -0
  229. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_recall_index.py +0 -0
  230. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_references_migration.py +0 -0
  231. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_report_contract_check.py +0 -0
  232. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_scripts_stdout_encoding.py +0 -0
  233. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_session_mode_watch.py +0 -0
  234. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_session_utils_additional.py +0 -0
  235. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_settings_hooks_registration.py +0 -0
  236. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_skill_no_builtin_conflict.py +0 -0
  237. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_statusline.py +0 -0
  238. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_stop_additional.py +0 -0
  239. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_stop_agent_memory_warn.py +0 -0
  240. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_stop_hook.py +0 -0
  241. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_stop_precompact_fixes.py +0 -0
  242. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_sync_template_stop.py +0 -0
  243. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_template_pre_tool_hook.py +0 -0
  244. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_usage_ingester.py +0 -0
  245. {claude_code_conductor-2.58.0 → claude_code_conductor-2.59.0}/tests/test_worktree_guard.py +0 -0
@@ -0,0 +1,361 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ restore_session.py: SessionStart(compact) hook.
4
+ コンテキスト圧縮後に現在のセッション状態を再注入する。
5
+ """
6
+
7
+ import os
8
+ import re
9
+ import sys
10
+
11
+ try:
12
+ sys.stdin.reconfigure(encoding='utf-8')
13
+ sys.stdout.reconfigure(encoding='utf-8', newline='\n')
14
+ sys.stderr.reconfigure(encoding='utf-8')
15
+ except AttributeError:
16
+ pass
17
+
18
+ _HOOKS_DIR = os.path.dirname(os.path.abspath(__file__))
19
+ _CLAUDE_DIR = os.path.dirname(_HOOKS_DIR)
20
+ SESSIONS_DIR = os.path.join(_CLAUDE_DIR, 'memory', 'sessions')
21
+
22
+ # 定数(architecture §3.5 / plan T4)
23
+ # GENBA = 「現在地」(現場)の音写命名。architecture §2.3 で確定した用語(CR M-01)。
24
+ # 他 Python consumer(init-session 等)が生まれたら session_utils へ移動すること(CR L-06)。
25
+ APPROACH_TAIL_LINES = 15
26
+ # GENBA_DONE: 現在地値が「完了」状態を表す定数(GENBA = 現在地 の音写)(CR M-01)。
27
+ # 他 Python consumer が生まれたら session_utils へ移動すること(CR L-06)。
28
+ GENBA_DONE = '完了'
29
+
30
+ # stdout の文字数上限(DC-AS-001)
31
+ MAX_OUTPUT_CHARS = 10000
32
+ OUTPUT_SAFETY_MARGIN = 200
33
+
34
+ # 残タスク切り詰め通知文言テンプレート(architecture §2-4)
35
+ TODO_TRUNCATION_NOTICE = (
36
+ '⚠️ 残タスクは全 {total} 件のうち先頭 {shown} 件のみ表示している'
37
+ '(C3 が stdout {limit} 文字上限に収めるため末尾側を切り詰めた)。'
38
+ '表示されていない項目があるため、全文は `{path}` を Read して確認すること。'
39
+ )
40
+
41
+ # 現在地(genba)値の表示上限(characters)。
42
+ # ①はマーカーより前方に出力されるため、①の長さがマーカーの生存位置を直接決める。
43
+ # 現在地は運用規約上「フェーズD 実装中」等の短い定型値であり(dev-workflow SKILL.md)、
44
+ # 実値は 50 文字程度。200 は正常系に一切影響しない。値の根拠は plan §1-5。
45
+ MAX_GENBA_CHARS = 200
46
+
47
+ GENBA_TRUNCATION_SUFFIX = (
48
+ '…[現在地は全 {total} 文字中 先頭 {shown} 文字のみ表示。全文は `{path}` を Read]'
49
+ )
50
+
51
+ # 現在地フィールドの読み取り用 regex(architecture §2.3)
52
+ _GENBA_RE = re.compile(r'^現在地:[ \t]*(.*)$', re.MULTILINE)
53
+
54
+ # date_str(ファイル名由来)の YYYYMMDD 形式検証用(SR L-3 / CR M-03)
55
+ # ファイル名経由で任意文字列が混入するのを防ぐ。他の regex 定数と同じモジュールレベルに配置。
56
+ _DATE_STR_RE = re.compile(r'^\d{8}$')
57
+
58
+
59
+ def _load_session_utils():
60
+ """session_utils モジュールを動的にロードして返す(同階層)。"""
61
+ import importlib.util
62
+
63
+ util_path = os.path.join(_HOOKS_DIR, "session_utils.py")
64
+ spec = importlib.util.spec_from_file_location("session_utils", util_path)
65
+ if spec is None or spec.loader is None:
66
+ raise ImportError(f"session_utils が見つかりません: {util_path}")
67
+ module = importlib.util.module_from_spec(spec)
68
+ spec.loader.exec_module(module) # type: ignore[attr-defined]
69
+ return module
70
+
71
+
72
+ def find_latest_session() -> str | None:
73
+ if not os.path.isdir(SESSIONS_DIR):
74
+ return None
75
+ files = [f for f in os.listdir(SESSIONS_DIR) if f.endswith('.tmp')]
76
+ if not files:
77
+ return None
78
+ return os.path.join(SESSIONS_DIR, max(files))
79
+
80
+
81
+ def extract_section(content: str, heading: str) -> str:
82
+ """``session_utils.extract_section`` への薄いラッパー(後方互換用)。
83
+
84
+ 過去にこのモジュール直下にあった ``extract_section`` を呼び出すテスト・スクリプトとの
85
+ 互換維持のため、モジュールレベルで公開する。実体は :mod:`session_utils` 側にある。
86
+ """
87
+ return _load_session_utils().extract_section(content, heading)
88
+
89
+
90
+ def extract_genba(content: str) -> str:
91
+ """セッション本文から「現在地:」行の値を抽出する(architecture §2.3)。
92
+
93
+ 行正規表現 ``^現在地:[ \\t]*(.*)$`` (MULTILINE) でマッチし、
94
+ トリム済みの値を返す。行が存在しない場合は空文字列(後方互換)。
95
+
96
+ Args:
97
+ content: セッションファイル全体のテキスト。
98
+
99
+ Returns:
100
+ 現在地の値(trim 済み)、または空文字列(行なし・値なし)。
101
+ """
102
+ m = _GENBA_RE.search(content)
103
+ return m.group(1).strip() if m else ''
104
+
105
+
106
+ def _tail(text: str, n: int) -> str:
107
+ """テキストの末尾 n 行を返す(architecture §3.5)。
108
+
109
+ 行数が n 以下の場合はテキストをそのまま返す(切り詰めない)。
110
+
111
+ Args:
112
+ text: 対象テキスト。
113
+ n: 末尾から取得する行数。
114
+
115
+ Returns:
116
+ 末尾 n 行(n 以下の場合は全体)。
117
+
118
+ Note:
119
+ n=0 のとき ``lines[-0:]`` は ``lines[0:]``(全体)と等価なため、
120
+ 全行を返す(切り詰めない)。Python の ``-0 == 0`` による反直感挙動(CR M-04)。
121
+ 呼び出し元は ``APPROACH_TAIL_LINES=15``(固定定数)のため実害はないが、
122
+ 仕様として明記・固定する。
123
+ """
124
+ lines = text.splitlines()
125
+ return '\n'.join(lines[-n:]) if len(lines) > n else text # nul-boundary: allow(末尾 N 行を人間可読テキストへ戻す。呼び出し元は表示のため再 splitlines するだけ)
126
+
127
+
128
+ def _sanitize_genba(value: str) -> str:
129
+ """現在地の値を出力に埋め込む前にサニタイズする(architecture §10 / SR 観点)。
130
+
131
+ ``session_utils.sanitize_value`` への薄いラッパー。
132
+ サニタイズ範囲を ``stop.py::_INHERIT_SANITIZE_RE`` と同等以上に保つため、
133
+ 共通関数に委譲する(SR M-1 / CR M-02):
134
+ - 改行文字(\\n / \\r)を除去する
135
+ - C0 制御文字・DEL(\\x7f)・C1 制御文字(\\x80-\\x9f)・U+2028/U+2029 を除去する
136
+ - ``-->`` を ``-- >`` に置換して HTML コメントブロックの破壊を防ぐ
137
+ - タブ(\\t)は保持する(SR L-1・session_utils.sanitize_value 参照)
138
+ """
139
+ return _load_session_utils().sanitize_value(value)
140
+
141
+
142
+ def _cap_genba(value: str, path: str, limit: int = MAX_GENBA_CHARS) -> str:
143
+ """現在地の値を limit 文字で切り詰め、切り詰めた場合のみ fail-loud 表示を付ける。
144
+
145
+ ①ワークフロー復帰指示は fail-loud マーカーより前方に出力されるため、
146
+ 現在地の値が肥大するとマーカーが上限境界の外へ押し出されて静かに消える。
147
+ それを防ぐためのキャップ(SR-NEW)。
148
+
149
+ 判定は ``len(value) <= limit`` で「ちょうどは切り詰めない」側に寄せる
150
+ (``_fit_items`` と同じ規約)。切り詰めは末尾を落として先頭を残す
151
+ (現在地の値は先頭にフェーズ名が来るため)。
152
+
153
+ Note:
154
+ ``value[:limit]`` は末尾を削るだけなので、サニタイズ済み文字列に存在しなかった
155
+ ``-->`` が新たに出現することはない(削除操作は新しい部分列を作らない)。
156
+ grapheme cluster(絵文字の ZWJ 連結など)の途中で切れる可能性はあるが、
157
+ 表示の乱れのみで安全性には影響しないため許容する。
158
+ """
159
+ if len(value) <= limit:
160
+ return value
161
+ return value[:limit] + GENBA_TRUNCATION_SUFFIX.format(
162
+ total=len(value), shown=limit, path=path
163
+ )
164
+
165
+
166
+ def _fit_items(items: list[str], budget: int) -> list[str]:
167
+ """予算に収まる項目を先頭から順に返す(architecture §2-2)。
168
+
169
+ 項目の途中では絶対に切らない(意味の破壊とサニタイズ済みトークン `-- >` の分断を防ぐ)。
170
+ `budget <= 0` のとき必ず空リストを返す。
171
+ `_tail` の `n=0` が全体を返す反直感挙動とは意味が逆である点に注意。
172
+
173
+ Args:
174
+ items: サニタイズ済みの項目文字列リスト。
175
+ budget: 総文字数の上限。
176
+
177
+ Returns:
178
+ 予算に収まるまでの先頭連続部分。budget <= 0 のときは空リスト。
179
+ """
180
+ if budget <= 0:
181
+ return []
182
+
183
+ kept = []
184
+ used = 0
185
+ for item in items:
186
+ # 2 件目以降は '\n' の 1 文字が挿入されるコスト
187
+ cost = len(item) + (1 if kept else 0)
188
+ if used + cost > budget:
189
+ break # continue ではない(前置の連続性を保つ)
190
+ kept.append(item)
191
+ used += cost
192
+
193
+ return kept
194
+
195
+
196
+ def _truncation_notice(total: int, shown: int, path: str) -> str:
197
+ """残タスク切り詰め通知文言を生成する(architecture §2-4)。
198
+
199
+ Args:
200
+ total: 全体の項目数。
201
+ shown: 実際に表示される項目数。
202
+ path: セッションファイルの絶対パス。
203
+
204
+ Returns:
205
+ フォーマットされた通知文字列。
206
+ """
207
+ return TODO_TRUNCATION_NOTICE.format(
208
+ total=total,
209
+ shown=shown,
210
+ limit=MAX_OUTPUT_CHARS,
211
+ path=path
212
+ )
213
+
214
+
215
+ def main():
216
+ path = find_latest_session()
217
+ if not path or not os.path.exists(path):
218
+ sys.exit(0)
219
+
220
+ with open(path, 'r', encoding='utf-8') as f:
221
+ content = f.read()
222
+
223
+ session_utils = _load_session_utils()
224
+ _extract_section = session_utils.extract_section
225
+ _sanitize = session_utils.sanitize_value
226
+
227
+ date_str = os.path.basename(path).replace('.tmp', '')
228
+
229
+ # date_str の YYYYMMDD 形式検証(SR L-3):
230
+ # ファイル名経由で任意文字列が date_str に混入するのを防ぐ。
231
+ # 8桁数字以外のファイル名はヘッダへの注入を避けるため exit 0 でスキップする。
232
+ if not _DATE_STR_RE.match(date_str):
233
+ sys.exit(0)
234
+
235
+ # 現在地フィールドを読み取る(architecture §3.2 step2)
236
+ genba = extract_genba(content)
237
+ genba_in_progress = genba != '' and genba != GENBA_DONE
238
+
239
+ todos = _extract_section(content, '残タスク')
240
+ successes = _extract_section(content, 'うまくいったアプローチ')
241
+ failures = _extract_section(content, '試みたが失敗したアプローチ')
242
+
243
+ # - [ ] 行のみにフィルタ(architecture §3.4)
244
+ # フィルタ判定は元行の lstrip() で行い(- [ ] プレフィックス保持)、
245
+ # 出力時に sanitize_value でサニタイズする(SR M-2)。
246
+ pending_todos = [
247
+ _sanitize(line) for line in todos.splitlines()
248
+ if line.lstrip().startswith('- [ ]')
249
+ ]
250
+
251
+ # early-exit 判定(architecture §3.2 step4)
252
+ if not genba_in_progress and not pending_todos and not successes and not failures:
253
+ sys.exit(0)
254
+
255
+ # ========== 2 段構成・段階 1: head_parts と tail_parts を事前組立 ==========
256
+
257
+ head_parts = []
258
+
259
+ # safe_path: セッションファイルの絶対パスをサニタイズして通知に使う
260
+ # (①の切り詰め表示と残タスク切り詰めマーカーの両方で使うため冒頭で 1 回だけ算出する)
261
+ safe_path = _sanitize(path)
262
+
263
+ # ①ワークフロー復帰指示(現在地が進行中のときのみ・出力冒頭)
264
+ if genba_in_progress:
265
+ # キャップは必ず _sanitize_genba の後に掛ける:
266
+ # sanitize_value は '-->' → '-- >' で 1 出現あたり 1 文字増えるため、
267
+ # サニタイズ前に長さを測ると過小評価になる。
268
+ safe_genba = _cap_genba(_sanitize_genba(genba), safe_path)
269
+ head_parts.append(
270
+ f'⚠️ dev-workflow 進行中(現在地: {safe_genba})。\n'
271
+ f'残作業に直接着手せず、対応 skill(develop / review-phase / start)経由で再開し、\n'
272
+ f'各エージェント出力後の Approval Flow を守ること。'
273
+ )
274
+
275
+ # ②ヘッダ
276
+ head_parts.append(f'[C3 セッション復元: {date_str} / 圧縮後リマインダー]')
277
+
278
+ # tail_parts: ④⑤を先に組み立てて fixed_len 計算に使う
279
+ tail_parts = []
280
+
281
+ # ④うまくいったアプローチ(末尾 N 行に切り詰め・行単位でサニタイズ)(SR M-2)
282
+ if successes:
283
+ tail_text = _tail(successes, APPROACH_TAIL_LINES)
284
+ n = len(successes.splitlines())
285
+ # 切り詰めが起きたときだけサフィックスを付ける(DC-GP-005)
286
+ if n > APPROACH_TAIL_LINES:
287
+ heading = f'\n## うまくいったアプローチ(末尾 {APPROACH_TAIL_LINES} 行のみ・全 {n} 行)'
288
+ else:
289
+ heading = '\n## うまくいったアプローチ'
290
+ tail_parts.append(heading)
291
+ sanitized_lines = [_sanitize(line) for line in tail_text.splitlines()]
292
+ tail_parts.append('\n'.join(sanitized_lines)) # nul-boundary: allow(stdout へ出す復元メッセージの成功アプローチ段落。表示専用)
293
+
294
+ # ⑤試みたが失敗したアプローチ(末尾 N 行に切り詰め・行単位でサニタイズ)(SR M-2)
295
+ if failures:
296
+ tail_text = _tail(failures, APPROACH_TAIL_LINES)
297
+ n = len(failures.splitlines())
298
+ # 切り詰めが起きたときだけサフィックスを付ける(DC-GP-005)
299
+ if n > APPROACH_TAIL_LINES:
300
+ heading = f'\n## 試みたが失敗したアプローチ(末尾 {APPROACH_TAIL_LINES} 行のみ・全 {n} 行)'
301
+ else:
302
+ heading = '\n## 試みたが失敗したアプローチ'
303
+ tail_parts.append(heading)
304
+ sanitized_lines = [_sanitize(line) for line in tail_text.splitlines()]
305
+ tail_parts.append('\n'.join(sanitized_lines)) # nul-boundary: allow(stdout へ出す復元メッセージの失敗アプローチ段落。表示専用)
306
+
307
+ # ========== 段階 2: 固定部長から残タスク予算を逆算し、切り詰め通知を判定 ==========
308
+
309
+ # fixed_len は head_parts + tail_parts + print() の末尾改行による実効文字数
310
+ fixed_len = len('\n'.join(head_parts + tail_parts)) # nul-boundary: allow(固定部の長さを測るための一時的な連結で、結果の文字列は len() 計測後に捨てられるため、機械的な分割消費者がない)
311
+
312
+ parts = []
313
+ if pending_todos:
314
+ heading = '\n## 残タスク'
315
+ # -3 の内訳: heading 挿入で増える '\n' 1 + 本文挿入で増える '\n' 1 + print() の末尾改行 1
316
+ budget_body = MAX_OUTPUT_CHARS - OUTPUT_SAFETY_MARGIN - fixed_len - len(heading) - 3
317
+ body = '\n'.join(pending_todos) # nul-boundary: allow(stdout へ出力する残タスク本文の構成で、読み手は表示または全文 Read のみであり、機械的な分割消費者がない)
318
+
319
+ if len(body) <= budget_body:
320
+ # パス 1: 全件がマーカーなしで収まる
321
+ parts = [heading, body]
322
+ else:
323
+ # パス 2: 先頭部分に切り詰め・マーカーを付ける
324
+ total = len(pending_todos)
325
+ # reserve: 最悪ケース(全 total 件を表示する場合のマーカー長)から上界を得る
326
+ reserve = _truncation_notice(total, total, safe_path)
327
+ budget2 = budget_body - len(reserve) - 1 # -1 はマーカー挿入で増える '\n'
328
+ kept = _fit_items(pending_todos, budget2)
329
+ notice = _truncation_notice(total, len(kept), safe_path)
330
+ # kept が空のときだけ本文パートを足さない(マーカーは必ず出す・fail-loud)
331
+ parts = [heading, notice] + (['\n'.join(kept)] if kept else []) # nul-boundary: allow(stdout へ出力するための出力部分リストの構成で、読み手は表示または全文 Read のみであり、機械的な分割消費者がない)
332
+
333
+ # 先頭優先を採る理由は 2 点(DC-AS-002):
334
+ # (1) ハーネスの truncate preview は先頭側を残すため、C3 側の予算計算にズレがあっても
335
+ # 優先度の高い内容が生き残る(二重防御)
336
+ # (2) 落とした項目は fail-loud マーカーと全文ポインタで必ず通知される
337
+
338
+ # 上限保証コメント(DC-AS-003):
339
+ # 上限 `MAX_OUTPUT_CHARS` を満たす保証条件は **`budget2 >= 0`** であること。
340
+ # `kept` が空のときの総長は `fixed_len + len(heading) + len(notice) + 3` で、
341
+ # `len(notice) <= len(reserve)` かつ `budget2 >= 0` ⟹ `len(reserve) <= budget_body - 1`
342
+ # より上限内に収まる。
343
+ # 固定部①②④⑤の性質はマーカーとの位置関係で 3 つに分かれる:
344
+ # - ①現在地(マーカーより前): `MAX_GENBA_CHARS` でキャップ済み。
345
+ # したがって①が `budget2 < 0` の原因になることはなく、マーカーは常に
346
+ # `MAX_OUTPUT_CHARS` 境界の内側に留まる。
347
+ # - ②ヘッダ(マーカーより前): 固定長(`date_str` は `^\d{8}$` 検証済み)で変動しない。
348
+ # - ④⑤アプローチ(マーカーより後): `_tail` の行数上限のみで文字数上限がないため、
349
+ # 総長が上限を超え得る(`budget2 < 0` になり得るのはこの肥大時のみ)。
350
+ # ただしマーカーは④⑤より前方にあるため、マーカーの生存は④⑤の大きさに依存しない。
351
+ # よってマーカー生存を脅かすのは①の肥大だけであり、それは `MAX_GENBA_CHARS` で
352
+ # キャップ済みである。④⑤による総長超過は requirements §6-4 が明示的に
353
+ # スコープ外宣言した領域であること(クラッシュはせず、マーカーは生存するが
354
+ # 上限は保証されない)。
355
+
356
+ lines = head_parts + parts + tail_parts
357
+ print('\n'.join(lines)) # nul-boundary: allow(stdout へ出力する復元メッセージ全体。読み手は Claude Code の表示でリポジトリ内に split 側がない)
358
+
359
+
360
+ if __name__ == '__main__':
361
+ main()
@@ -20,6 +20,12 @@ user-invocable: false
20
20
 
21
21
  `現在地:` は行フィールド形式(`現在地: {値}` 1行)で維持する。`## 現在地` 見出しにしない。
22
22
 
23
+ `現在地:` に書くのは「**今どのフェーズにいるか**」と「**次にどこへ向かうか**」の 2 点だけとし、値は **200 文字以内**に収めること(`.claude/hooks/restore_session.py` の `MAX_GENBA_CHARS = 200` に対応する)。経緯・確定事項・コミットハッシュ・再開手順などは `現在地:` に書かず、`## 残タスク` / `## うまくいったアプローチ` / `## 試みたが失敗したアプローチ` へ振り分けること。
24
+
25
+ > **なぜ 200 文字なのか(美観の問題ではない)**: `現在地:` の値は `SessionStart:compact` hook(`restore_session.py`)が圧縮後に再注入する出力の中で、**fail-loud マーカーより前方**に置かれる。ハーネスは hook の stdout を 10,000 文字で cap する(設定変更不可)ため、`現在地:` が肥大するとマーカーが 10,000 文字境界の外へ押し出され、**切り詰めに気付けないまま復元されたつもりになる**。
26
+ >
27
+ > 200 文字を超えた値は `_cap_genba` が先頭 200 文字へ切り詰め、末尾に `…[現在地は全 N 文字中 先頭 200 文字のみ表示。全文は {セッションファイルのパス} を Read]` を付けて知らせるため、気付けないまま失う事故そのものは防がれる。ただし**切り詰められた内容は復元に載らない**。長い記述は上記の各セクションへ書くこと。
28
+
23
29
  > 注: 「フェーズ承認後」が原則だが、フェーズ完了直後に更新するケースもある(D-1・D-4 等の tester 完了トリガーなど)。承認とタスク完了が同一ステップになる場合は完了時点で即 Edit する(CR L-02)。
24
30
 
25
31
  ---
@@ -122,6 +122,8 @@ Glob で `.claude/memory/sessions/*.tmp` を検索し、ファイル名(YYYYMM
122
122
  {promotion_candidate: true のパターン一覧。なければ「なし」}
123
123
  ```
124
124
 
125
+ > 注: `現在地:` の値は dev-workflow SKILL.md の運用総則で 200 文字以内と定められている。ただし**この長さ規約を機械的に強制する仕組みは無い**(`restore_session.py` の `MAX_GENBA_CHARS` / `_cap_genba` は同 hook の stdout を組み立てるときだけ働くもので、本 skill が session.tmp を直接 Read する経路には関与しない。書き込み時点で長さを検証する hook も存在しない)。したがって規約に反する長大な値が実在し得る。要約・省略はせず、上記のサニタイズを施したうえでそのまま表示すること。
126
+
125
127
  Step 1.5 で「完了している可能性のあるタスク」が検出された場合は、Step 4 の前に以下の AskUserQuestion を提示する(**`from-start` 起動時はスキップ**・G-3 参照):
126
128
 
127
129
  ```json
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.59.0] - 2026-07-31
4
+
5
+ ### 修正
6
+
7
+ - **`SessionStart:compact` hook の stdout がハーネスの上限を超え、コンパクト後のセッション復元が警告なく骨抜きになっていた**: ハーネスは hook の `additionalContext` / `systemMessage` / plain stdout を **10,000 文字**(バイトではない)で cap し、超過分はファイル保存+プレビュー差し替えに置き換える(公式ドキュメント https://code.claude.com/docs/en/hooks で確認・**設定変更は不可**)。`SessionStart` はこの上限が適用されつつ stdout がそのまま context 化される 3 例外イベントの 1 つである。`.claude/hooks/restore_session.py` は出力 3 セクションのうち「うまくいったアプローチ」「試みたが失敗したアプローチ」にのみ `_tail(…, APPROACH_TAIL_LINES=15)` の行数上限を持ち、**残タスクだけが件数・長さとも無制限**だった。実測で 10,266 文字(うまくいった/失敗したアプローチが**空**の状態で)と上限を超過しており、切り詰めは「成功」扱いで警告されないため、**LLM が気付かなければ復元されたつもりで残タスクの大半と失敗パターンを失ったまま作業を再開する**状態だった
8
+ - **`現在地:` の値に長さ上限が無く、fail-loud マーカーを 10,000 文字境界の外へ押し出せた**: `現在地:` を埋め込むワークフロー復帰指示は出力の**先頭側**(マーカーより前方)に置かれるため、値が肥大するとマーカー自体が境界外へ出て、**ハーネスの truncate でマーカーごと静かに消える**。security-review が実機で再現し `MARKER_IDX = 15167`(境界外)を実測した。残タスク(文字数予算)・アプローチ 2 セクション(行数上限)はいずれもキャップを持つのに、**`現在地:` だけがどのキャップにも入っていない**という非対称が原因だった。これは仮説ではなく、本リリースの開発中に実セッションファイルの `現在地:` が 1,524 文字まで肥大した実インシデントとして発生している
9
+
10
+ ### 追加
11
+
12
+ - **残タスクセクションの文字数予算と fail-loud マーカー**: 出力全体が 10,000 文字に収まるよう、固定部(復帰指示・ヘッダ・アプローチ 2 セクション)の実長を先に測って残りを残タスクへ配分する。切り詰めが発生した場合は**必ず**「全 N 件のうち先頭 M 件のみ表示」+**セッションファイルの絶対パス**を出力し、LLM が「復元されたつもり」にならないようにする。マーカーは `## 残タスク` 見出しの**直後・本文より前**に置く(末尾に置くと、万一ハーネス側 truncate が発火したときマーカー自体が溢れて消え、元の問題がそのまま再現するため)。切り詰めが発生しない場合はマーカーを出さない(過検知しない)
13
+ - **`現在地:` 値のキャップ(`MAX_GENBA_CHARS = 200`)と fail-loud 表示**: 超過分は先頭 200 文字へ切り詰め、末尾に「全 N 文字中 先頭 200 文字のみ表示。全文は `{path}` を Read」を付ける。**これによりマーカーの生存が無条件保証になる**。マーカー位置の上界は「約 415 + パス長」で OS のパス長上限に抑えられるため(Windows 260 / Linux 4096)、残タスクやアプローチ 2 セクションがどれだけ肥大してもマーカーは 10,000 の内側に留まる。実測で `MARKER_IDX = 15167` → **505**(現在地 15,000 文字+アプローチ 15 万文字超の複合ケースでも境界内)。「総長が 10,000 を超え得る」ことと「マーカーが消える」ことを別問題として切り離し、後者だけを完全に閉じた設計である
14
+ - **アプローチ 2 セクションの切り詰め通知**: 従来 `_tail` は 16 行目以降を**無言で**捨てていた。実際に切り詰めたときだけ見出しへ `(末尾 15 行のみ・全 N 行)` を付ける(`_tail` 本体のロジックは不変)。「切り詰めたら必ず知らせる」という原則を 3 セクションすべてに揃えた
15
+ - **`現在地:` の運用規約を明文化**: `.claude/skills/dev-workflow/SKILL.md` の運用総則に、`現在地:` へ書くのは「今どのフェーズか」「次にどこへ向かうか」の 2 点だけとし **200 文字以内**に収めること、経緯・確定事項・コミットハッシュ・再開手順は `## 残タスク` / `## うまくいったアプローチ` / `## 試みたが失敗したアプローチ` へ振り分けることを追記した。**「なぜ 200 文字なのか」も併記**している(上限だけを書くと美観の規則に見え、「引き継ぎのためだから例外」という自己正当化の余地が残るため)。あわせて `.claude/skills/init-session/SKILL.md` には、**この長さ規約を機械的に強制する仕組みは無い**こと(`MAX_GENBA_CHARS` / `_cap_genba` は `restore_session.py` が stdout を組み立てるときだけ働き、session.tmp を直接 Read する経路には関与しない。書き込み時点の長さ検証 hook も存在しない)を明記し、規約に反する長大な値が実在し得る前提で読むよう注記した
16
+
17
+ ### 変更
18
+
19
+ - **`restore_session.py` の stdout を `newline='\n'` で reconfigure**: `TextIOWrapper.reconfigure` は `newline` 未指定だと既存設定(変換あり)を保つため、Windows では `print` の `\n` が `\r\n` になる。ハーネスは**文字数**で cap するため CRLF は行数分だけ実効文字数を増やし、上限保証が「出力行数 ≦ 約 200 行」という**書かれていない条件付き**に退化していた。変換を止めて `len(出力文字列)` = ハーネスが数える文字数 を 3 OS で一致させる。`/CLAUDE.md` §9-3 の canonical 順序(stdin → stdout → stderr)は不変で、キーワード引数の追加のみ
20
+
3
21
  ## [2.58.0] - 2026-07-29
4
22
 
5
23
  ### 破壊的変更
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-conductor
3
- Version: 2.58.0
3
+ Version: 2.59.0
4
4
  Summary: Multi-agent orchestration framework for Claude Code with Codex/Cursor adapters (C3)
5
5
  Project-URL: Homepage, https://github.com/satoh-y-0323/claude-code-conductor
6
6
  Project-URL: Repository, https://github.com/satoh-y-0323/claude-code-conductor
@@ -1,3 +1,3 @@
1
1
  """Claude Code Conductor (C3) - multi-agent orchestration framework for Claude Code."""
2
2
 
3
- __version__ = "2.58.0"
3
+ __version__ = "2.59.0"