claude-code-conductor 2.71.0__tar.gz → 2.72.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.
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/breaking-changes.txt +1 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/deletions.txt +6 -1
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/docs/config-policy.md +9 -22
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/settings.json +0 -13
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/ARCHITECTURE.md +0 -1
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/CHANGELOG.md +50 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/PKG-INFO +1 -3
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/pyproject.toml +0 -4
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/__init__.py +1 -1
- claude_code_conductor-2.72.0/src/c3/_terminal.py +118 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_ask.py +11 -3
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/question.py +22 -2
- claude_code_conductor-2.72.0/tests/test_cli_ask.py +793 -0
- claude_code_conductor-2.72.0/tests/test_permission_handler_removal.py +154 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_refgraph.py +6 -6
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_refgraph_query.py +1 -1
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_refgraph_structural.py +22 -15
- claude_code_conductor-2.71.0/.claude/hooks/permission_handler.py +0 -402
- claude_code_conductor-2.71.0/.claude/hooks/permission_handler_toast.py +0 -208
- claude_code_conductor-2.71.0/.claude/permission_rules.json +0 -41
- claude_code_conductor-2.71.0/src/c3/_terminal.py +0 -64
- claude_code_conductor-2.71.0/tests/hooks/test_permission_handler.py +0 -1495
- claude_code_conductor-2.71.0/tests/hooks/test_permission_handler_toast.py +0 -199
- claude_code_conductor-2.71.0/tests/test_cli_ask.py +0 -349
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/.gitignore +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/design-critic.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/docs/autonomous-mode-onboarding.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/docs/nul-boundary.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/_hook_utils.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/patterns_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/recall_autorebuild.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/report_contract_check.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/report_path_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/select_tier.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/session_mode_watch.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/tier_autoapply.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/tier_gap_check.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/rules/judgment-principles.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/autonomous-mode/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/autonomous-mode/scripts/mode_line.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/brainstorm/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/c3-omission-declaration.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/reachability-map.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/record-protocol.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/scripts/detect_execution_verification.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/skills/start/scripts/archive_reports.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/.gitignore +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/LICENSE +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/LICENSES/numpy-LICENSE +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/README.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/_db_params.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_metrics.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_run.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_tier.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/db.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/gitutil.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/006_review_decisions_severity.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/007_review_decisions_resolution.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/008_drop_agent_runs.sql +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/refgraph.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/_pre_compact_helpers.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_detect_execution_verification.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_hook_utils.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_recall_autorebuild.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_record_agent_outcome.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_report_path_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_select_tier.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_settings_tier_autoapply_registration.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_tier_autoapply.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_tier_gap_check.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/hooks/test_tier_security_fixes.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_c3_omission_declaration.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_e0_verification_exit.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_marker_compliance.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_parallel_agents_skill_tier.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_reachability_map.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_session_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_archive_reports.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_audit_review_decisions.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_check_deletions.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_ci_workflows.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_claude_gitignore.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_doctor.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_doctor_launcher.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_metrics.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_run.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_tier.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_db.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_db_connect_lint.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_gitutil.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_hooks_stdin_idiom.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_init_only_patterns.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_mode_line.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_no_bare_python_launcher.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_no_passonly_tests.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_nul_boundary_lint.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_patterns_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_report_contract_check.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_scripts_src_bootstrap.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_scripts_stdout_encoding.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_session_mode_watch.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_settings_hooks_registration.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_start_skill_archive_contract.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_stop_agent_memory_warn.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_worktree_guard.py +0 -0
- {claude_code_conductor-2.71.0 → claude_code_conductor-2.72.0}/tests/test_wt_agent_sync.py +0 -0
|
@@ -13,3 +13,4 @@ v2.58.0|c3 init and c3 update now place .claude/.gitignore in the target project
|
|
|
13
13
|
v2.60.0|c3 plan validate/waves now strictly validate po_plan_version: only "0.1" (parallel) or "sequential" (sequential TDD) quoted strings are accepted; unknown or non-string values fail with exit 2. Migrate existing plan-reports by setting po_plan_version: "0.1" or "sequential" (quoted).|po_plan_version の値を厳格検査("0.1"/"sequential" のみ受理・他は exit 2)。既存 plan-report は引用符付きの正値へ修正する。
|
|
14
14
|
v2.66.0|record_review_decision.py: --severity is now a required argument (omitting it exits 2 and records nothing; out-of-vocabulary values still fail-safe to NULL with a warning). --finding now rejects empty or whitespace-only values (exit 2). Update any custom invocations to pass --severity with one of critical/high/medium/low. Shipped SKILL.md call sites and docstrings are already updated.|
|
|
15
15
|
v2.68.0|New PreToolUse Write hook report_path_guard.py is registered in the distributed settings.json. It blocks (exit 2) writes of report-prefixed filenames (requirements-report-, architecture-report-, plan-report-, design-review-report-, code-review-report-, security-review-report-, test-report-, debug-analysis-, debug-needed-) outside .claude/reports/, overwrites of existing timestamp-named reports, and non-timestamp naming for the strict 4 report types. Ordinary files whose names start with those prefixes can no longer be written outside .claude/reports/. Set C3_REPORT_GUARD_DISABLE=1 to bypass, or remove the hook registration from settings.json.|レポート 9 prefix 名ファイルの .claude/reports/ 外への Write・タイムスタンプ形式既存レポートの上書き・strict-4 の非タイムスタンプ命名を exit 2 で block する report_path_guard hook を配布 settings.json に登録。対象 prefix 名の一般ファイルも block されうる。回避は C3_REPORT_GUARD_DISABLE=1 または settings.json から登録を外す。
|
|
16
|
+
v2.72.0|Removed the C3 PermissionRequest hook suite (permission_handler.py, permission_handler_toast.py, permission_rules.json) and the [notify] extra, superseded by upstream auto mode and permissions.allow. If you have not yet copied your auto_allow patterns from .claude/permission_rules.json, answer N (default) at the upcoming deletion prompt, copy them into permissions.allow in .claude/settings.local.json (personal, not the shared settings.json; same syntax; upstream checks each separator-delimited subcommand of compound commands independently, but command substitution handling is unverified -- narrow broad Bash wildcards to the minimum prefix; copying is a must where auto mode is unavailable), then rerun c3 update. windows-toasts can be uninstalled.|C3 の PermissionRequest hook 一式(permission_handler.py・permission_handler_toast.py・permission_rules.json)と [notify] extra を削除しました(上流 auto mode / permissions.allow へ委譲)。auto_allow パターンを未転記の場合は、この後の削除プロンプトで N(既定)を選び、.claude/permission_rules.json の内容を .claude/settings.local.json(個人設定・共有の settings.json ではない)の permissions.allow へ転記してから c3 update を再実行してください(書式は同一。上流は複合コマンドをセパレータ単位で独立照合しますが、コマンド置換〔$() 等〕の扱いは未確認のため、広い Bash ワイルドカードは必要最小のプレフィックスへ狭めること。auto mode が使えない環境では転記必須)。windows-toasts はアンインストール可。
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# - 絶対パス / ../ を含むパス / シンボリックリンク は無視
|
|
9
9
|
# - ディレクトリは削除しない(ファイルのみ)
|
|
10
10
|
#
|
|
11
|
-
# 詳細は .claude/docs/config-policy.md §3 #13 と §7 落とし穴
|
|
11
|
+
# 詳細は .claude/docs/config-policy.md §3 #13 と §7 落とし穴 4 を参照。
|
|
12
12
|
|
|
13
13
|
# ---- v2.1.0: tdd-develop / worktree-tdd-workflow 廃止 ----
|
|
14
14
|
agents/tdd-develop.md
|
|
@@ -87,3 +87,8 @@ hooks/po_heartbeat.py
|
|
|
87
87
|
|
|
88
88
|
# ---- v2.41.0: tier-routing 学習シグナル再設計(record_agent_outcome.py へ置換) ----
|
|
89
89
|
skills/dev-workflow/scripts/record_tier_outcome.py
|
|
90
|
+
|
|
91
|
+
# ---- v2.72.0: permission_handler 一式廃止(上流 permissions.allow へ委譲) ----
|
|
92
|
+
hooks/permission_handler.py
|
|
93
|
+
hooks/permission_handler_toast.py
|
|
94
|
+
permission_rules.json
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
.claude/CLAUDE.md 配布元・配布先共通(常時注入)
|
|
21
21
|
.claude/settings.json プロジェクト共通設定(配布される)
|
|
22
22
|
.claude/settings.local.json 個人 override(配布されない)
|
|
23
|
-
.claude/permission_rules.json C3 独自の自動承認パターン(配布される)
|
|
24
23
|
.claude/rules/ C3 配布デフォルトルール(配布される)
|
|
25
24
|
.claude/rules/promoted/ プロジェクト固有昇格ルール(配布される、update は触らない)
|
|
26
25
|
.claude/hooks/ Claude Code lifecycle hooks(配布される)
|
|
@@ -36,7 +35,6 @@ src/c3/migrations/ SQLite schema migration SQL ファイル
|
|
|
36
35
|
.claude/CLAUDE.md c3 init で配置・c3 update で更新
|
|
37
36
|
.claude/settings.json c3 init で配置・c3 update で更新
|
|
38
37
|
.claude/settings.local.json ユーザーが個別作成(c3 は触らない)
|
|
39
|
-
.claude/permission_rules.json c3 init で配置・c3 update で更新
|
|
40
38
|
.claude/rules/ c3 init で配置・c3 update で更新
|
|
41
39
|
.claude/rules/promoted/index.md c3 init で空雛形のみ配置(c3 update は触らない)
|
|
42
40
|
.claude/hooks/ c3 init で配置・c3 update で更新
|
|
@@ -128,7 +126,7 @@ src/c3/migrations/ SQLite schema migration SQL ファイル
|
|
|
128
126
|
|
|
129
127
|
## 2. 設定優先順位と書き込み権限
|
|
130
128
|
|
|
131
|
-
設定は性質の異なる **3
|
|
129
|
+
設定は性質の異なる **3 つのレイヤー**(レイヤー B は v2.72.0 で削除済み)に分かれる。1 列に並べると誤解を生むため分離して記述する。
|
|
132
130
|
|
|
133
131
|
### レイヤー A: ツール権限(Claude Code 公式)
|
|
134
132
|
|
|
@@ -146,15 +144,10 @@ src/c3/migrations/ SQLite schema migration SQL ファイル
|
|
|
146
144
|
|
|
147
145
|
### レイヤー B: 自動承認パターン(C3 独自拡張)
|
|
148
146
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
> **注意**: `permission_rules.json` は Claude Code 公式の `permissions.allow` とは **独立した別レイヤー**。
|
|
155
|
-
> 同列に並べると「どちらが優先されるか」で誤解が生じる。レイヤー A と B は並立している。
|
|
156
|
-
|
|
157
|
-
> **注意**: `auto_allow` パターンは最小限に留めること。広範なパターン(例: `Bash(*)`)は C3 の意図する動作範囲を超えた危険コマンドも自動承認する可能性がある。
|
|
147
|
+
(削除済み・v2.72.0)C3 独自の自動承認パターン(`.claude/permission_rules.json` の `auto_allow` 配列と
|
|
148
|
+
`permission_handler.py` hook)は v2.72.0 で削除された。自動承認は上流 Claude Code の
|
|
149
|
+
`permissions.allow`(レイヤー A)へ委譲する。移行手順は `.claude/breaking-changes.txt` の
|
|
150
|
+
v2.72.0 行を参照。
|
|
158
151
|
|
|
159
152
|
### レイヤー C: LLM 指示・知識(CLAUDE.md / rules)
|
|
160
153
|
|
|
@@ -173,7 +166,6 @@ src/c3/migrations/ SQLite schema migration SQL ファイル
|
|
|
173
166
|
|---|---|---|---|
|
|
174
167
|
| `.claude/settings.json` | ○ | ○ | △(チーム合意のもと) |
|
|
175
168
|
| `.claude/settings.local.json` | × | × | ○(個人 override / 秘匿情報) |
|
|
176
|
-
| `.claude/permission_rules.json` | ○ | ○ | △(ファイル全体を上書き編集する場合は注意) |
|
|
177
169
|
| `.claude/CLAUDE.md` | ○ | ○ | △(c3 update で上書きされる前提で) |
|
|
178
170
|
| `.claude/rules/*.md` | ○ | ○ | △(同上) |
|
|
179
171
|
| `.claude/rules/promoted/index.md` | ○(空雛形のみ) | × | ○(`/promote-pattern` が追記、手動編集も可) |
|
|
@@ -196,11 +188,11 @@ src/c3/migrations/ SQLite schema migration SQL ファイル
|
|
|
196
188
|
| 6 | `.claude/docs/*.md` | △(一部のみ) | ○ | 利用先向けリファレンス。配布対象は `autonomous-mode-onboarding.md` / `config-policy.md`(本ドキュメント)/ `nul-boundary.md` / `parallel-agents-setup.md` / `platform-adapters.md` / `settings.json.md` の 6 ファイル。配布元固有の設計メモ等は `_excludes.py` で個別除外。`taxonomy.md` は tracked(GitHub 公開)だが EXCLUDE 対象のため wheel 非配布(詳細は §7 落とし穴 2 参照)。 |
|
|
197
189
|
| 7 | `.claude/CLAUDE.md` | ○ | ○ | 配布先で常時注入される共通ルール |
|
|
198
190
|
| 8 | `.claude/settings.json` | ○ | ○ | プロジェクト共通設定(hooks 登録・permissions など) |
|
|
199
|
-
| 9 | `.claude/permission_rules.json` |
|
|
191
|
+
| 9 | `.claude/permission_rules.json` | ×(削除済み・v2.72.0) | — | C3 独自の自動承認パターンは v2.72.0 で削除された。上流 `permissions.allow` へ委譲(移行手順は `.claude/breaking-changes.txt` の v2.72.0 行を参照) |
|
|
200
192
|
| 10 | `.claude/settings.local.json` | × | — | 個人 override・秘匿情報。`_excludes.py` でも除外、`.gitignore` でも除外、c3 update も触らない |
|
|
201
193
|
| 11 | `.claude/reports/*` / `memory/*` / `agent-memory/*` / `state/*` / `tmp/*` / `worktrees/*` / `logs/*` | × (`.gitkeep` のみ ○) | — | 実行時生成領域。空ディレクトリのみ `KEEP_PATTERNS` の `.gitkeep` で配布。データ本体は除外。v2.19.0 で `.claude/state/c3_version.txt`(バージョン checkpoint)を追加(`state/*` 一括除外により自動非配布) |
|
|
202
194
|
| 12 | `.dev/*` / `/CLAUDE.md` / `/AGENTS.md` / `/.codex/` / `/.cursor/` / `/.agents/` | × | — | 配布元専用または adapter 生成物。wheel には構造的に含まれない(`src/c3/_template/.claude/` 配下のみ同梱)が、配布元 `.gitignore` で commit 混入も防ぐ |
|
|
203
|
-
| 13 | `.claude/deletions.txt` | ○ | ○ | `c3 update` が読み込み、利用先 `.claude/` から該当ファイルを削除候補として扱う。配布元の `.claude/deletions.txt` に追記したエントリは次回 pip install → `c3 update` で利用先に伝播。`KEEP_PATTERNS` で明示配布。`c3 update` 自体は本ファイルを削除しない(§7 落とし穴
|
|
195
|
+
| 13 | `.claude/deletions.txt` | ○ | ○ | `c3 update` が読み込み、利用先 `.claude/` から該当ファイルを削除候補として扱う。配布元の `.claude/deletions.txt` に追記したエントリは次回 pip install → `c3 update` で利用先に伝播。`KEEP_PATTERNS` で明示配布。`c3 update` 自体は本ファイルを削除しない(§7 落とし穴 4 参照) |
|
|
204
196
|
| 14 | `.claude/breaking-changes.txt` | ○ | ○ | `c3 update` が読み込み、利用先の `.claude/state/c3_version.txt`(バージョン checkpoint)と diff を計算して破壊的変更を表示する。MAJOR bump 時は y/N 承認プロンプトを発火。`KEEP_PATTERNS` で明示配布。配布元 `.claude/breaking-changes.txt` を更新すれば次回 pip install → `c3 update` で利用先に伝播。利用先 git 管理は tracked(上書きされる) |
|
|
205
197
|
| 15 | `.claude/.gitignore` | ○ | ×(INIT_ONLY) | 利用先の実行時生成領域のうち「再生成可能」「セッション一時」だけを除外する配布 `.gitignore`(§1-2 の方針の実装)。プロジェクトルートの `.gitignore` は編集しないため既存設定と衝突しない。`INIT_ONLY_PATTERNS` により初回配置のみで **c3 update は上書きしない**(利用先の追記を守る)。C3 側で分類を更新した場合はリリースノートで手動マージを案内する |
|
|
206
198
|
|
|
@@ -391,11 +383,8 @@ C3 side で `promoted/` の雛形を更新した場合は、リリースノー
|
|
|
391
383
|
|
|
392
384
|
### 落とし穴 5: `permission_rules.json` は `settings.json.permissions.deny` を覆せない(要検証)
|
|
393
385
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
`deny` を覆すことはできないはず。ただし Claude Code 公式仕様に明記なし。
|
|
397
|
-
|
|
398
|
-
**残課題**: v2.18.0 以降の検証タスクとして記録。現時点では「覆せない前提で設計する」ことを推奨。
|
|
386
|
+
対象機能は v2.72.0 で削除済み(C3 独自の自動承認パターン一式)。上流 `permissions.allow` へ委譲した。
|
|
387
|
+
移行手順は `.claude/breaking-changes.txt` の v2.72.0 行を参照。
|
|
399
388
|
|
|
400
389
|
### 落とし穴 6: `permissions.allow` のワイルドカードはプレフィックス一致のため `../` パストラバーサルに対応していない
|
|
401
390
|
|
|
@@ -471,11 +460,9 @@ CI のジョブ環境変数として設定する・親プロセスから子プ
|
|
|
471
460
|
| 除外パターン定義 | `src/c3/_excludes.py` | `EXCLUDE_PATTERNS` / `KEEP_PATTERNS` 定数 + `should_skip()` |
|
|
472
461
|
| ビルド時除外 | `hatch_build.py` | `_excludes.py` の重複定義(ビルドフック用) |
|
|
473
462
|
| 同期確認 hook | `.dev/hooks/_sync_check.py` | 3 ファイル変更時の警告(配布元専用) |
|
|
474
|
-
| 自動承認 hook | `.claude/hooks/permission_handler.py` | `permission_rules.json` を読んで自動承認 |
|
|
475
463
|
|
|
476
464
|
### 残課題リンク(v2.18.0 以降)
|
|
477
465
|
|
|
478
|
-
- `permission_rules.json` の `auto_allow` が `settings.json.permissions.deny` を覆せるか検証
|
|
479
466
|
- `~/.claude/settings.json` のグローバル設定と `.claude/settings.json` の同キー競合時のマージ範囲確認
|
|
480
467
|
- ~~`c3 update` の削除検出(`deletions.txt` 方式、v2.18.0 予定)~~ → v2.18.0 で実装
|
|
481
468
|
- `c3 update` 時の Breaking changes 警告(v2.19.0 予定)
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
"Bash(c3 run .claude/hooks/pre_tool.py*)",
|
|
6
6
|
"Bash(c3 run .claude/hooks/pre_compact.py*)",
|
|
7
7
|
"Bash(c3 run .claude/hooks/restore_session.py*)",
|
|
8
|
-
"Bash(c3 run .claude/hooks/permission_handler.py*)",
|
|
9
8
|
"Bash(c3 run .claude/hooks/statusline.py*)",
|
|
10
9
|
"Bash(c3 run .claude/hooks/recall_autorebuild.py*)",
|
|
11
10
|
"Bash(c3 run .claude/skills/dev-workflow/scripts/detect_execution_verification.py*)",
|
|
@@ -168,18 +167,6 @@
|
|
|
168
167
|
]
|
|
169
168
|
}
|
|
170
169
|
],
|
|
171
|
-
"PermissionRequest": [
|
|
172
|
-
{
|
|
173
|
-
"matcher": "",
|
|
174
|
-
"hooks": [
|
|
175
|
-
{
|
|
176
|
-
"type": "command",
|
|
177
|
-
"command": "c3",
|
|
178
|
-
"args": ["run", "${CLAUDE_PROJECT_DIR}/.claude/hooks/permission_handler.py"]
|
|
179
|
-
}
|
|
180
|
-
]
|
|
181
|
-
}
|
|
182
|
-
],
|
|
183
170
|
"UserPromptSubmit": [
|
|
184
171
|
{
|
|
185
172
|
"matcher": "",
|
|
@@ -122,7 +122,6 @@ A ヒアリング → B 設計 → C 計画 → D 実装(TDD) → E レビュー
|
|
|
122
122
|
| `PostToolUse` | `Write` / `Edit` | `planner_check.py` | plan-report の YAML/タイムスタンプ/reviewer 規約検査 |
|
|
123
123
|
| `PostToolUse` | `Write` | `report_contract_check.py` | reports 直下の命名契約逸脱を warn(strict-4〔requirements/architecture/plan/design-review〕はタイムスタンプ形式の厳密検査、loose-3〔code-review/security-review/test〕はタイムスタンプ形式または task_id 形式のどちらかを許容する緩検査。stderr + additionalContext) |
|
|
124
124
|
| `PostToolUse` | `Edit` | `session_mode_watch.py` | session.tmp のモード行挿入・plan= 差し替えを warn(stderr + additionalContext) |
|
|
125
|
-
| `PermissionRequest` | (全て) | `permission_handler.py` | `permission_rules.json` の `auto_allow` で自動承認([config-policy §2 レイヤー B](.claude/docs/config-policy.md)) |
|
|
126
125
|
| `PreCompact` | (全て) | `pre_compact.py` | compact 前にチェックポイントマーカー注入 |
|
|
127
126
|
| `Stop` | (全て) | `session_stop.py` | セッション終了処理のオーケストレータ |
|
|
128
127
|
|
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.72.0] - 2026-08-14
|
|
4
|
+
|
|
5
|
+
### 破壊的変更
|
|
6
|
+
|
|
7
|
+
- **C3 独自の `PermissionRequest` hook 一式を削除した**: `.claude/hooks/permission_handler.py` /
|
|
8
|
+
`.claude/hooks/permission_handler_toast.py` / `.claude/permission_rules.json`、および
|
|
9
|
+
`pyproject.toml` の `[notify]` extra(`windows-toasts` 依存)。失うものは auto_allow による
|
|
10
|
+
自動承認・承認待ちの OS 通知(Windows トースト承認 UX)・`[notify]` extra。代替は上流
|
|
11
|
+
Claude Code の auto mode(Pro/Max/Team の新規セッション既定。**Enterprise/API/cloud の展開前・
|
|
12
|
+
既存セッションは対象外**)と `permissions.allow`(書式互換)、上流の通知設定。
|
|
13
|
+
移行手順(`c3 update` 実行中の視点で記述):
|
|
14
|
+
1. `c3 update` 実行中にこの案内を見た時点では `settings.json` は新版へ更新済みだが、
|
|
15
|
+
旧ファイルの削除はまだ確定していない(直後の削除プロンプトは `[y/N]`・既定 N)
|
|
16
|
+
2. まだ転記していない場合: 削除プロンプトで N(既定)を選び、
|
|
17
|
+
`.claude/permission_rules.json` の `auto_allow` を `.claude/settings.local.json`
|
|
18
|
+
(個人設定・共有の `settings.json` ではない)の `permissions.allow` へ転記してから
|
|
19
|
+
`c3 update` を再実行して y で削除する。書式は同一のためそのまま転記できる
|
|
20
|
+
3. 但し書き: 上流はコマンドセパレータ(`&&` `||` `;` `|` `|&` `&` 改行)で分割した
|
|
21
|
+
各サブコマンドを独立に照合する(公式ドキュメント確認済み(2026-08-14))。
|
|
22
|
+
ただしコマンド置換(`$()` やバッククォート)の扱いは未確認のため、
|
|
23
|
+
`Bash(git *)` 等の広いワイルドカードはそのまま転記せず、必要最小のプレフィックスへ狭めること
|
|
24
|
+
4. auto mode が既定でない・使わない環境(Enterprise/API/cloud の展開前・既存セッション・
|
|
25
|
+
手動運用)では `permissions.allow` への転記が実質必須
|
|
26
|
+
5. `pip uninstall windows-toasts` 可。転記漏れは git 履歴から回収可能
|
|
27
|
+
詳細は `.claude/docs/config-policy.md` を参照。
|
|
28
|
+
|
|
29
|
+
## [2.71.1] - 2026-08-14
|
|
30
|
+
|
|
31
|
+
### 修正
|
|
32
|
+
|
|
33
|
+
- **`c3 ask` の Windows 非対話判定が機能せず無限ハングする欠陥を修正した**(v2.71.0
|
|
34
|
+
「既知の問題」の解消)。`sys.stdin.isatty()` が Windows で stdin を NUL / `/dev/null` へ
|
|
35
|
+
リダイレクトした場合にも True を誤返却し、`--response` 未指定で exit 1 にならず
|
|
36
|
+
`msvcrt.getwch()`(実コンソール直読み)で無限ハングしていた。判定を
|
|
37
|
+
`stdin_is_interactive_console()`(Windows は GetConsoleMode で実コンソール接続を判定・
|
|
38
|
+
POSIX は従来どおり `isatty()`)へ置換し、入口ゲートと対話パス直前の二重ゲートを
|
|
39
|
+
同一判定へ統一した
|
|
40
|
+
- 判定ヘルパーを共有モジュール `src/c3/_terminal.py` へ移設し、関数全体を一箇所集約で
|
|
41
|
+
fail-closed 化した(想定外例外でも非対話扱い=exit 1 契約を維持・KeyboardInterrupt /
|
|
42
|
+
SystemExit は伝播。`question.py` 側は re-export でシーム維持)
|
|
43
|
+
- `_read_key` の POSIX 分岐に空読み EOF ガードを追加した(EOF 時のビジーループを解消し
|
|
44
|
+
既存の exit 1 経路へ接続)
|
|
45
|
+
- `docs/cli-reference.md` の v2.71 既知問題注記を修正後の実挙動へ更新した
|
|
46
|
+
|
|
47
|
+
### 内部
|
|
48
|
+
|
|
49
|
+
- `tests/test_cli_ask.py` に回帰・凍結テスト計 17 件を追加した(修正前状態での赤化実証・
|
|
50
|
+
内部依存注入による fail-closed 凍結・Windows 限定 subprocess+timeout 統合テストを含む。
|
|
51
|
+
フルスイート 3322 緑・CI 全緑 run 31755508514)
|
|
52
|
+
|
|
3
53
|
## [2.71.0] - 2026-08-14
|
|
4
54
|
|
|
5
55
|
### 追加
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: claude-code-conductor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.72.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
|
|
@@ -41,8 +41,6 @@ Requires-Dist: duckdb>=0.10
|
|
|
41
41
|
Requires-Dist: fastembed>=0.8.0
|
|
42
42
|
Requires-Dist: numpy>=1.22
|
|
43
43
|
Requires-Dist: pyyaml>=6.0
|
|
44
|
-
Provides-Extra: notify
|
|
45
|
-
Requires-Dist: windows-toasts>=1.0; (sys_platform == 'win32') and extra == 'notify'
|
|
46
44
|
Description-Content-Type: text/markdown
|
|
47
45
|
|
|
48
46
|
# Claude Code Conductor (C3)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Shared terminal helpers for c3 CLI subcommands.
|
|
2
|
+
|
|
3
|
+
Used by cli_doctor.py, cli_tier.py, cli_ask.py, question.py, and future
|
|
4
|
+
cli_*.py to keep terminal-facing logic in one place:
|
|
5
|
+
|
|
6
|
+
- ``supports_color`` / ``strip_ansi`` / ``sanitize_terminal_text``: ANSI
|
|
7
|
+
color support detection, escape-sequence stripping, and sanitization of
|
|
8
|
+
untrusted text before printing.
|
|
9
|
+
- ``stdin_is_interactive_console``: console-attachment detection for stdin
|
|
10
|
+
(whether an interactive console is really attached, as opposed to a NUL /
|
|
11
|
+
pipe / file redirection).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# CSI sequences with final byte 'm' (color / SGR). Other escape sequences
|
|
22
|
+
# (e.g. \033c reset, OSC \033]0;...\007 title) are not handled by _strip_ansi
|
|
23
|
+
# and would skew column-width calculations if they appeared in cell text.
|
|
24
|
+
_CSI_M_RE = re.compile(r"\033\[[0-9;]*m")
|
|
25
|
+
|
|
26
|
+
# Control characters disallowed when printing untrusted text to the terminal.
|
|
27
|
+
# Allow newline (\n), tab (\t), carriage return (\r) but strip any other
|
|
28
|
+
# C0 control or escape character (\x1b) so that ANSI/title-injection cannot
|
|
29
|
+
# happen via DB-stored values such as ``current_step`` / ``error_message``.
|
|
30
|
+
# Also strip the Unicode line/paragraph separators U+2028 / U+2029 so they
|
|
31
|
+
# match the init-session SR L-2 sanitization spec (removes U+2028 / U+2029).
|
|
32
|
+
# NEL (U+0085) is intentionally NOT included per that spec.
|
|
33
|
+
_DISALLOWED_CONTROL_RE = re.compile("[\x00-\x08\x0b\x0c\x0e-\x1f\x7f" + chr(0x2028) + chr(0x2029) + "]")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def supports_color() -> bool:
|
|
37
|
+
"""Return True if stdout supports ANSI color sequences.
|
|
38
|
+
|
|
39
|
+
Honors the NO_COLOR environment variable (https://no-color.org/) and
|
|
40
|
+
requires stdout to be a TTY.
|
|
41
|
+
"""
|
|
42
|
+
if os.environ.get("NO_COLOR"):
|
|
43
|
+
return False
|
|
44
|
+
if not sys.stdout.isatty():
|
|
45
|
+
return False
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def strip_ansi(s: str) -> str:
|
|
50
|
+
"""Remove ANSI CSI 'm' (SGR / color) sequences for visible-width calc.
|
|
51
|
+
|
|
52
|
+
Note: handles only ``\\033[...m`` style sequences. Other escape sequences
|
|
53
|
+
such as cursor movement (``\\033[H``), screen clear (``\\033[2J``), or
|
|
54
|
+
OSC title-set are intentionally not stripped here because the cells we
|
|
55
|
+
measure should never contain them.
|
|
56
|
+
"""
|
|
57
|
+
return _CSI_M_RE.sub("", s)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def sanitize_terminal_text(s: str) -> str:
|
|
61
|
+
"""Strip control / escape characters from untrusted text before printing.
|
|
62
|
+
|
|
63
|
+
Used for DB-sourced strings (``current_step`` / ``error_message``) so
|
|
64
|
+
they cannot inject ANSI escape sequences (title, cursor, screen clear)
|
|
65
|
+
into the terminal. Newlines / tabs / carriage returns are preserved.
|
|
66
|
+
The Unicode line/paragraph separators U+2028 / U+2029 are also stripped
|
|
67
|
+
(init-session SR L-2 sanitization spec); NEL (U+0085) is out of scope.
|
|
68
|
+
"""
|
|
69
|
+
if not s:
|
|
70
|
+
return s
|
|
71
|
+
return _DISALLOWED_CONTROL_RE.sub("", s)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def stdin_is_interactive_console() -> bool:
|
|
75
|
+
"""Return True only when stdin is attached to a real interactive console.
|
|
76
|
+
|
|
77
|
+
Windows の CRT ``_isatty()``(``sys.stdin.isatty()`` の内部実装)は対象ハンドルが
|
|
78
|
+
``FILE_TYPE_CHAR`` かどうかしか見ておらず、NUL デバイス(``subprocess.DEVNULL`` /
|
|
79
|
+
``NUL`` リダイレクト)もコンソールと同じ ``FILE_TYPE_CHAR`` に分類されるため
|
|
80
|
+
誤って True を返す(実測: debug-analysis-20260814-011032.md)。
|
|
81
|
+
Windows では ``GetConsoleMode``(実コンソールハンドルに対してのみ成功する
|
|
82
|
+
Win32 API)で実際のコンソール接続を確認し、NUL・パイプ・ファイルいずれの
|
|
83
|
+
リダイレクトも False として扱う。POSIX は ``isatty()`` が ``/dev/null`` に
|
|
84
|
+
対して標準どおり正しく False を返すためそのまま使う。
|
|
85
|
+
|
|
86
|
+
関数全体を ``except Exception`` で包み、内部でどの分岐がどの例外型を投げても
|
|
87
|
+
送出せず False を返す (fail-closed)。POSIX 分岐の ``sys.stdin.isatty()`` は
|
|
88
|
+
``sys.stdin`` が None のケース (pythonw.exe / PyInstaller --windowed など) で
|
|
89
|
+
``AttributeError`` を投げうるが、これを呼び出し元へ伝播させると
|
|
90
|
+
``c3 ask`` の「exit 1」契約が破れる (SR-R-001 / CR-E-001)。呼び出し位置ごとに
|
|
91
|
+
try/except を足すのではなく、ここ一箇所へ集約して将来の呼び出し箇所追加にも
|
|
92
|
+
自動的に効かせる。``KeyboardInterrupt`` / ``SystemExit`` は ``BaseException``
|
|
93
|
+
直属で ``Exception`` を継承しないため握り潰さない (Ctrl-C は従来どおり伝播)。
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
if os.name == "nt":
|
|
97
|
+
return _windows_console_attached()
|
|
98
|
+
return sys.stdin.isatty()
|
|
99
|
+
except Exception:
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _windows_console_attached() -> bool:
|
|
104
|
+
try:
|
|
105
|
+
import ctypes
|
|
106
|
+
import msvcrt
|
|
107
|
+
from ctypes import wintypes
|
|
108
|
+
|
|
109
|
+
handle = msvcrt.get_osfhandle(0)
|
|
110
|
+
kernel32 = ctypes.windll.kernel32
|
|
111
|
+
kernel32.GetConsoleMode.argtypes = [wintypes.HANDLE, ctypes.POINTER(wintypes.DWORD)]
|
|
112
|
+
kernel32.GetConsoleMode.restype = wintypes.BOOL
|
|
113
|
+
mode = wintypes.DWORD()
|
|
114
|
+
return bool(kernel32.GetConsoleMode(wintypes.HANDLE(handle), ctypes.byref(mode)))
|
|
115
|
+
except Exception:
|
|
116
|
+
# 判定不能ならすべて非対話扱いに倒す (fail-closed)。answer_questions() 経由の
|
|
117
|
+
# 呼び出し位置も含め、想定外の例外型で対話ゲートを突破させない。
|
|
118
|
+
return False
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
import sys
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
from c3._terminal import stdin_is_interactive_console
|
|
10
11
|
from c3.question import answer_questions, load_questions
|
|
11
12
|
|
|
12
13
|
|
|
@@ -47,9 +48,16 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
def handle(args: argparse.Namespace) -> int:
|
|
50
|
-
if args.response is None
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
if args.response is None:
|
|
52
|
+
# 判定関数が想定外の例外を出しても traceback で落とさず「非対話」へ倒す
|
|
53
|
+
# (fail-closed)。--response 未指定時の exit 1 契約を例外型に依存させない。
|
|
54
|
+
try:
|
|
55
|
+
interactive = stdin_is_interactive_console()
|
|
56
|
+
except Exception:
|
|
57
|
+
interactive = False
|
|
58
|
+
if not interactive:
|
|
59
|
+
print("c3 ask: --response is required in non-interactive mode", file=sys.stderr)
|
|
60
|
+
return 1
|
|
53
61
|
|
|
54
62
|
try:
|
|
55
63
|
if args.file is not None:
|
|
@@ -13,6 +13,12 @@ from dataclasses import dataclass
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
16
|
+
# stdin_is_interactive_console() は共有ターミナルヘルパー _terminal.py が正本
|
|
17
|
+
# (CR-R-003)。ここでの import は question.py 内部の呼び出し元 (answer_questions /
|
|
18
|
+
# _raw_keyboard_supported) が使うと同時に、``question.stdin_is_interactive_console``
|
|
19
|
+
# という従来の参照名を後方互換で維持する re-export を兼ねる。
|
|
20
|
+
from c3._terminal import stdin_is_interactive_console
|
|
21
|
+
|
|
16
22
|
|
|
17
23
|
@dataclass(frozen=True)
|
|
18
24
|
class Option:
|
|
@@ -63,7 +69,7 @@ def answer_questions(
|
|
|
63
69
|
if current_response is not None:
|
|
64
70
|
selected = _select_from_response(question, current_response)
|
|
65
71
|
else:
|
|
66
|
-
use_interactive =
|
|
72
|
+
use_interactive = stdin_is_interactive_console() if interactive is None else interactive
|
|
67
73
|
selected = _select_interactively(question) if use_interactive else _select_default(question)
|
|
68
74
|
answers.append(_answer_payload(question, selected))
|
|
69
75
|
return {"answers": answers}
|
|
@@ -383,7 +389,16 @@ def _clear_screen() -> None:
|
|
|
383
389
|
|
|
384
390
|
|
|
385
391
|
def _raw_keyboard_supported() -> bool:
|
|
386
|
-
|
|
392
|
+
# OS が raw keyboard API を持つかではなく、今この stdin に実コンソールが
|
|
393
|
+
# 接続されているかを見る。旧実装(os.name in ("nt", "posix"))は事実上
|
|
394
|
+
# 常に True を返し、NUL/パイプ/ファイルへのリダイレクト時でも
|
|
395
|
+
# _select_with_line_input() の安全網(input() → EOFError)を恒久的に
|
|
396
|
+
# 迂回させ msvcrt.getwch() の無限ブロックを招いていた
|
|
397
|
+
# (debug-analysis-20260814-011032.md 欠陥B)。
|
|
398
|
+
# answer_questions() の外側ゲートと同じ判定をここでも呼ぶのは、interactive=True を
|
|
399
|
+
# 明示指定して外側ゲートを迂回する呼び出し元にも安全網を効かせるための意図的な
|
|
400
|
+
# 再検証であり、単純な重複ではない(削除・簡略化しないこと)。
|
|
401
|
+
return stdin_is_interactive_console()
|
|
387
402
|
|
|
388
403
|
|
|
389
404
|
def _read_key() -> str:
|
|
@@ -410,6 +425,11 @@ def _read_key() -> str:
|
|
|
410
425
|
try:
|
|
411
426
|
tty.setraw(fd)
|
|
412
427
|
ch = sys.stdin.read(1)
|
|
428
|
+
if ch == "":
|
|
429
|
+
# EOF(read が空文字列)。空文字列を返すと _select_interactively() の
|
|
430
|
+
# ループが再描画しながら回り続け CPU ビジーループになるため、
|
|
431
|
+
# EOFError を送出して呼び出し元(cli_ask.handle の except EOFError)へ委ねる。
|
|
432
|
+
raise EOFError("stdin reached EOF while reading a key")
|
|
413
433
|
if ch == "\x1b":
|
|
414
434
|
nxt = sys.stdin.read(2)
|
|
415
435
|
return {"[A": "up", "[B": "down", "[C": "right", "[D": "left"}.get(nxt, "escape")
|