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.
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/_hook_utils.py +25 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/check_agent_invocation.py +4 -3
- claude_code_conductor-2.55.1/.claude/hooks/patterns_guard.py +127 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/planner_check.py +4 -3
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/post_tool.py +1 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/pre_compact.py +17 -4
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/pre_tool.py +6 -2
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/recall_autorebuild.py +3 -1
- claude_code_conductor-2.55.1/.claude/hooks/report_contract_check.py +131 -0
- claude_code_conductor-2.55.1/.claude/hooks/session_mode_watch.py +276 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/tier_autoapply.py +5 -3
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/worktree_guard.py +6 -2
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/settings.json +20 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/promote-pattern/SKILL.md +7 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/ARCHITECTURE.md +4 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/CHANGELOG.md +38 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/PKG-INFO +21 -2
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/README.md +20 -1
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/__init__.py +1 -1
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_hook_utils.py +132 -0
- claude_code_conductor-2.55.1/tests/test_hooks_stdin_idiom.py +357 -0
- claude_code_conductor-2.55.1/tests/test_patterns_guard.py +598 -0
- claude_code_conductor-2.55.1/tests/test_report_contract_check.py +620 -0
- claude_code_conductor-2.55.1/tests/test_session_mode_watch.py +967 -0
- claude_code_conductor-2.55.1/tests/test_settings_hooks_registration.py +292 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/design-critic.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/autonomous-mode-onboarding.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/nul-boundary.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/select_tier.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/tier_gap_check.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/autonomous-mode/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/autonomous-mode/scripts/mode_line.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/brainstorm/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/init-session/scripts/session_guard.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.gitignore +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSE +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/numpy-LICENSE +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/hatch_build.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/pyproject.toml +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/_db_params.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_metrics.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_run.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_tier.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/db.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/gitutil.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/004_agent_outcomes.sql +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/006_review_decisions_severity.sql +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/question.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/__init__.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/_pre_compact_helpers.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/conftest.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_recall_autorebuild.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_record_agent_outcome.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_select_tier.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_settings_tier_autoapply_registration.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_tier_autoapply.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_tier_gap_check.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/hooks/test_tier_security_fixes.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_marker_compliance.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_parallel_agents_skill_tier.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_session_guard.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_check_deletions.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_doctor.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_doctor_launcher.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_metrics.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_run.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_tier.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_db.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_gitutil.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_mode_line.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_no_bare_python_launcher.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_nul_boundary_lint.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/tests/test_usage_ingester.py +0 -0
- {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
|
|
57
|
-
|
|
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(
|
|
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()
|
{claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/planner_check.py
RENAMED
|
@@ -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
|
-
|
|
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(
|
|
234
|
+
payload = json.loads(sys.stdin.read(_STDIN_READ_LIMIT_CHARS))
|
|
234
235
|
except (json.JSONDecodeError, ValueError):
|
|
235
236
|
sys.exit(0)
|
|
236
237
|
|
|
@@ -12,10 +12,23 @@ import re
|
|
|
12
12
|
import sys
|
|
13
13
|
from datetime import datetime, timedelta, timezone
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
sys.
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
11
|
-
sys.
|
|
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:
|
{claude_code_conductor-2.54.0 → claude_code_conductor-2.55.1}/.claude/hooks/recall_autorebuild.py
RENAMED
|
@@ -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()
|