claude-code-conductor 2.48.1__tar.gz → 2.50.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.48.1 → claude_code_conductor-2.50.0}/.claude/agents/architect.md +1 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/planner.md +1 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/docs/platform-adapters.md +12 -1
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/SKILL.md +15 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/init-session/SKILL.md +35 -6
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/report-timestamp/SKILL.md +10 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.gitignore +1 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/CHANGELOG.md +34 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/PKG-INFO +1 -1
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/hatch_build.py +2 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/__init__.py +1 -1
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/_excludes.py +2 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/adapters.py +2 -2
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_doctor.py +77 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_adapters.py +44 -1
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_doctor.py +168 -0
- claude_code_conductor-2.50.0/tests/test_excludes.py +473 -0
- claude_code_conductor-2.48.1/tests/test_excludes.py +0 -65
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/design-critic.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/_hook_utils.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/recall_autorebuild.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/select_tier.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/tier_autoapply.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/tier_gap_check.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/settings.json +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/brainstorm/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/ARCHITECTURE.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/LICENSE +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/LICENSES/numpy-LICENSE +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/README.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/_db_params.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_metrics.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_tier.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/db.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/gitutil.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/006_review_decisions_severity.sql +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/_pre_compact_helpers.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_hook_utils.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_recall_autorebuild.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_record_agent_outcome.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_select_tier.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_settings_tier_autoapply_registration.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_tier_autoapply.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_tier_gap_check.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/hooks/test_tier_security_fixes.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_parallel_agents_skill_tier.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_session_guard.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_check_deletions.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_metrics.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_tier.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_db.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_gitutil.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/tests/test_worktree_guard.py +0 -0
{claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/docs/platform-adapters.md
RENAMED
|
@@ -11,7 +11,7 @@ C3 は **Claude Code** を canonical platform として設計されているが
|
|
|
11
11
|
- 動作差分のまとめ
|
|
12
12
|
|
|
13
13
|
を一箇所にまとめたリファレンス。実装は `src/c3/adapters.py` / `src/c3/mcp_server.py` /
|
|
14
|
-
`src/c3/cli_ask.py` / `src/c3/platforms.py` を参照。
|
|
14
|
+
`src/c3/cli_ask.py` / `src/c3/platforms.py` / `src/c3/cli_doctor.py` を参照。
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -92,6 +92,9 @@ python -m c3.mcp_server
|
|
|
92
92
|
`C3_PROJECT_ROOT` 環境変数でプロジェクトルートを指定可能(adapter 生成時に自動設定)。
|
|
93
93
|
ソースインストール時は `PYTHONPATH` も adapter が `<repo>/src` に設定する。
|
|
94
94
|
|
|
95
|
+
生成される `.codex/config.toml` / `.cursor/mcp.json` の `command` は `python` ではなく、
|
|
96
|
+
adapter 生成時点の `sys.executable`(絶対パス)を書き込む。理由は「7. 既知の制限」を参照。
|
|
97
|
+
|
|
95
98
|
---
|
|
96
99
|
|
|
97
100
|
## 4. `c3 ask` CLI fallback
|
|
@@ -178,3 +181,11 @@ Claude Code / Codex / Cursor / OpenCode が同じプロジェクトを行き来
|
|
|
178
181
|
- **OpenCode の `AskUserQuestion`**: OpenCode adapter は MCP 設定を生成しないため、`c3_ask_user_question` ツールは使わず `AGENTS.md` の指示に従ってユーザーへ直接確認する。`multiSelect: true` は「複数選択の質問」として維持するよう指示済み
|
|
179
182
|
- **OpenCode の `tools` 制限**: 生成される agent には一律で `bash/read/edit/write/websearch` を付与する。Claude 側 frontmatter の細かな `tools` 制限は反映されない(C3 の `code-reviewer` / `security-reviewer` は Claude 側でもレポート出力のため `write` を持つ)
|
|
180
183
|
- **改行コード**: adapter 生成ファイルは LF 改行で書き出す(`newline="\n"`)。`.gitattributes` で `eol=lf` を固定済み
|
|
184
|
+
- **Codex の trust 要件**: プロジェクトが Codex に trust されていない場合、`.codex/config.toml` は**エラーなしで無視**される(Codex 公式仕様)。MCP サーバーが読み込まれず `AskUserQuestion` が動かない場合は、まず対象プロジェクトを Codex 側で trust 済みか確認する(Codex CLI: 初回起動時のプロンプトで trust するか、設定で許可済みディレクトリに追加する)
|
|
185
|
+
- **MCP `command` の絶対パス化**: adapter が書き込む `command` は `python` ではなく `sys.executable`(adapter 生成時の絶対パス)を使う。理由:
|
|
186
|
+
- macOS 12.3 以降、無印 `python` は標準に存在しない(`python3` のみ。Homebrew も同様)。`command: "python"` のままだと Codex が MCP spawn 時に program not found となり、「起動時にエラーで読み込まれない」症状になる
|
|
187
|
+
- `python` が PATH 上にあっても、pipx / venv などの隔離環境では `No module named c3` で起動失敗することがある
|
|
188
|
+
- GUI 起動の Codex(Desktop / IDE 拡張)はシェルの PATH を継承しないことがある(最小 PATH)
|
|
189
|
+
- 代案の「POSIX では `python3` にする」は不採用: 無印 python 不在の問題しか救えず、pipx 隔離や GUI PATH 最小の問題には無力なため
|
|
190
|
+
- `c3 doctor --platform codex|cursor` は生成済み config から `command` を読み取り、実行可能か・`<command> -c "import c3"` が成功するかを自己診断する
|
|
191
|
+
- **adapter 生成物はマシン固有**: `command` に埋め込む `sys.executable` は生成したマシン・環境(venv / pipx / システム Python)に依存する絶対パスであるため、adapter 生成物(`.codex/config.toml` / `.cursor/mcp.json` など)はチームで共有せず、各マシンで `c3 init --platform <p>` または `c3 update --platform <p>` を実行して再生成する運用とする
|
{claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/dev-workflow/SKILL.md
RENAMED
|
@@ -24,6 +24,21 @@ user-invocable: false
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
+
**運転モードによるゲート挙動の切替(既定は HITL・非破壊)**: 各フェーズの承認ゲート
|
|
28
|
+
(**A-4 / B-3 / C-1 / C-2 / C-3 / D-2.5 / D-3 / D-5 / E-1 / E-2 の 10 個**。D-0 は AskUserQuestion を
|
|
29
|
+
持たない実行モード判別ステップのため承認ゲートに含めない=DC-AS-003)に到達したら、まず
|
|
30
|
+
session.tmp の `モード:` 行を確認する。**有効な自律宣言**(§3-3)でなければ**以下の各ゲートを
|
|
31
|
+
記載どおり AskUserQuestion で実行する(従来動作)**。有効な自律宣言のときのみ、
|
|
32
|
+
`.claude/skills/autonomous-mode/SKILL.md` の「ゲート対応表」に従って承認ゲートを客観収束条件に
|
|
33
|
+
付け替える。**非可逆操作の関所・情報不足の質問・重量フェーズ着手前の usage 確認は常に人間**
|
|
34
|
+
(自律でも停止して確認する=人間の 3 関所・§4-1 / requirements §7)。
|
|
35
|
+
|
|
36
|
+
> **skill 未 Read でも『モード行を見て skill を Read せよ』の 1 段で復帰できる** — develop / review-phase を「直接開始」した場合(init-session を経ない起動)、autonomous-mode skill の明示 Read が挟まらない可能性がある。ただし本ブロックが
|
|
37
|
+
> 各ゲートで `モード:` 行の確認を要求するため、skill 未 Read でも「モード行を見て skill を Read せよ」
|
|
38
|
+
> の 1 段で復帰できる(`.claude/skills/autonomous-mode/SKILL.md` 参照・復元の単一障害点を作らない)。
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
27
42
|
## tier-routing 結果記録の運用
|
|
28
43
|
|
|
29
44
|
各フェーズの承認ゲート・タスク単位で `record_agent_outcome.py` を呼び、role 別に実際に使われた tier の成功/失敗を記録する(architecture-report-20260702-214748.md §3-4)。全記録ブロック共通のルール:
|
{claude_code_conductor-2.48.1 → claude_code_conductor-2.50.0}/.claude/skills/init-session/SKILL.md
RENAMED
|
@@ -54,11 +54,18 @@ python .claude/skills/init-session/scripts/session_guard.py mark
|
|
|
54
54
|
Glob で `.claude/memory/sessions/*.tmp` を検索し、ファイル名(YYYYMMDD)が最大のものを Read する。
|
|
55
55
|
ファイルが存在しない場合は「前回セッションなし」として Step 3 へ進む。
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
`モード:` 行も Read 対象に含める。セッションファイル内に `モード: ` で始まる行があれば、Step 3 のサマリ表示と Step 5 の復元判定に使用する。
|
|
58
|
+
|
|
59
|
+
> **セキュリティ注記(SR L-2 表示用)**: `現在地:` 行と `モード:` 行の値をユーザーへ表示する前に、
|
|
58
60
|
> 制御文字(C0/C1)・DEL・改行(`\n`/`\r`)・`-->`・U+2028/U+2029 を除去して表示すること。
|
|
59
61
|
> セッションファイルは外部編集が可能な信頼境界であり、悪意ある値が混入した場合の
|
|
60
62
|
> ターミナルインジェクションを防ぐ。
|
|
61
63
|
|
|
64
|
+
> **パス用 choke point(§3-4(b) 封じ込め検査)**: `plan-path` を `Read` 引数に使う場合は、
|
|
65
|
+
> 表示用 SR L-2 とは別に、改行・NUL・制御文字を除去し、絶対パスへ正規化(realpath)して、
|
|
66
|
+
> `~/.claude/plans/` 以下に収まるかを判定する。許可ルート外またはシンボリックリンク解決後に
|
|
67
|
+
> 範囲外になる場合は無効化する。その後、正規化されたパスに対して `Read` で実在確認を行う。
|
|
68
|
+
|
|
62
69
|
---
|
|
63
70
|
|
|
64
71
|
## Step 1.5: 残タスクと git log を照合する(陳腐化チェック)
|
|
@@ -97,6 +104,8 @@ Glob で `.claude/memory/sessions/*.tmp` を検索し、ファイル名(YYYYMM
|
|
|
97
104
|
|
|
98
105
|
**現在地:** {session.tmp の「現在地:」行の値。空なら「(未設定)」と表示。表示前に制御文字・改行・`-->`・U+2028/U+2029 を除去すること(SR L-2)}
|
|
99
106
|
|
|
107
|
+
**運転モード:** {session.tmp の「モード:」行の値。無効/不在は「HITL(既定)」と表示。値は SR L-2 でサニタイズする}
|
|
108
|
+
|
|
100
109
|
**残タスク:**
|
|
101
110
|
{残タスクリスト。完了済み [x] は省略する}
|
|
102
111
|
|
|
@@ -156,13 +165,33 @@ AskUserQuestion ツールで以下を提示する:
|
|
|
156
165
|
## Step 5: 選択に応じて分岐する
|
|
157
166
|
|
|
158
167
|
**「続きから作業する」の場合:**
|
|
159
|
-
Step 1 で読んだ session.tmp
|
|
168
|
+
Step 1 で読んだ session.tmp の「現在地:」行と「モード:」行の値を確認する。
|
|
169
|
+
|
|
170
|
+
> **パス用封じ込め検査の機械実行必須化(SR-AI-001)**: realpath 正規化+許可ルート封じ込め判定は、
|
|
171
|
+
> `.claude/skills/autonomous-mode/scripts/mode_line.py` を Bash 経由で実際に呼び出して判定結果を得ること。
|
|
172
|
+
> 文字列の目視比較で代替しないこと。スクリプト不在(配布先等)の場合は無効=HITL(fail-closed)。
|
|
173
|
+
> 呼び出し例(セッションファイルからモード行を直接パイプで渡す。コマンドラインに現れるリテラルは
|
|
174
|
+
> ファイルパスのみで、モード行の内容はパイプのデータとして流れシェルに評価されない):
|
|
175
|
+
> `grep -m1 '^モード: ' .claude/memory/sessions/{YYYYMMDD}.tmp | python .claude/skills/autonomous-mode/scripts/mode_line.py`
|
|
176
|
+
> → `VALID<TAB>{正規化済み絶対パス}`(exit 0)/`INVALID<TAB>{理由コード}`(exit 1)の 1 行で判定する。
|
|
177
|
+
> モード行の値をコマンド文字列(here-string・引数・ダブルクォート内展開)に直接埋め込んではならない。
|
|
160
178
|
|
|
161
179
|
- **「現在地:」が非空かつ「完了」でない場合**(dev-workflow フェーズ進行中):
|
|
162
|
-
|
|
163
|
-
-
|
|
164
|
-
|
|
165
|
-
|
|
180
|
+
|
|
181
|
+
- **「モード:」行が有効な自律宣言の場合**(autonomous-mode skill の有効性判定 3 条件をすべて満たす):
|
|
182
|
+
1. `モード:` 行から `plan=` で指定されたパスを取得する
|
|
183
|
+
2. そのパスに対して、Step 1 の「パス用 choke point」検査を実施する(改行/NUL 除去 → realpath 正規化 → `~/.claude/plans/` 封じ込め判定)
|
|
184
|
+
2.5. 正規化されたパスに対し `Read` で実在確認を行う。存在しなければ無効化し、「モード行が無効(プラン不在/許可ルート外)→ HITL で継続」を明示表示する
|
|
185
|
+
3. 検査と実在確認を通ったら、`.claude/skills/autonomous-mode/SKILL.md` を Read して、ゲート対応表と運用規約を確認する
|
|
186
|
+
4. その後、対応 skill(`develop` / `review-phase` / `start` など)を起動して運転を継続する
|
|
187
|
+
|
|
188
|
+
- **「モード:」行が無効(不在・`plan=` 欠落・許可ルート外・実在しない plan)の場合**:
|
|
189
|
+
「モード行が無効(プラン不在/許可ルート外)→ HITL で継続」を明示表示し、従来どおり HITL 動作で対応 skill を起動する
|
|
190
|
+
|
|
191
|
+
- 残タスクに直接着手せず、対応 skill 経由で再開し、各エージェント出力後の Approval Flow を守ること:
|
|
192
|
+
- フェーズ D 実装中 → `develop` skill を使う
|
|
193
|
+
- フェーズ E レビュー中 → `review-phase` skill を使う
|
|
194
|
+
- それ以外 → `start` skill を使う
|
|
166
195
|
|
|
167
196
|
- **「現在地:」が空または「完了」の場合**:
|
|
168
197
|
従来どおり残タスク(引き継ぎバックログ)の内容を確認してそのまま作業に入る。
|
|
@@ -21,3 +21,13 @@ python "${CLAUDE_SKILL_DIR}/scripts/get_timestamp.py"
|
|
|
21
21
|
|
|
22
22
|
- PowerShell・bash の date コマンドは使わない(時刻部分が 000000 になる場合がある)
|
|
23
23
|
- python コマンドが使えない場合のみ `python3` にフォールバックする
|
|
24
|
+
|
|
25
|
+
## Bash を持たないエージェントの場合(親がタイムスタンプを渡す)
|
|
26
|
+
|
|
27
|
+
architect / planner / design-critic 等、tools に Bash を含まないエージェントは本スクリプトを実行できない。
|
|
28
|
+
その場合は**サブエージェントを起動する親 Claude が本 skill でタイムスタンプを取得し、
|
|
29
|
+
出力先レポートのファイル名を確定させた形で起動プロンプトに含める**こと(例: 「レポートは
|
|
30
|
+
`.claude/reports/architecture-report-20260714-213000.md` に Write。このファイル名を厳守」)。
|
|
31
|
+
エージェント側でのタイムスタンプの手採番(推測・連番)は禁止する。
|
|
32
|
+
(Bash を持たないエージェントに Bash を追加する対処は採らない: レポート role の最小権限を崩し、
|
|
33
|
+
allow 済み git コマンド経由で「非可逆操作は人間の関所」を迂回する経路が開くため)
|
|
@@ -83,6 +83,7 @@ site/
|
|
|
83
83
|
# v2.1.0: tdd-develop / worktree-tdd-workflow 廃止後の復活防止
|
|
84
84
|
.claude/agents/tdd-develop.md
|
|
85
85
|
.claude/skills/worktree-tdd-workflow/
|
|
86
|
+
.claude/skills/autonomous-mode/
|
|
86
87
|
|
|
87
88
|
# Personal Claude Code overrides (per-user / per-worktree, never committed)
|
|
88
89
|
# wheel からは _excludes.py / hatch_build.py で既に除外済み。git でも tracking しない方針。
|
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.50.0] - 2026-07-17
|
|
4
|
+
|
|
5
|
+
### 修正
|
|
6
|
+
|
|
7
|
+
- **MCP 設定の `command` を `sys.executable` 絶対パス化(Codex / Cursor adapter)**: 生成される `.codex/config.toml` / `.cursor/mcp.json` の `command` が `"python"` ハードコードだったため、無印 `python` が存在しない環境(macOS 12.3 以降の標準 / Homebrew)・pipx / venv 隔離(`No module named c3`)・GUI 起動の Codex(シェル PATH 非継承)で MCP サーバーが起動時エラーになり `c3_ask_user_question` が使えなかった問題を修正。`c3 init` / `c3 update --platform` を実行した Python の絶対パスを埋め込む(TOML は既存 `_toml_escape` でエスケープ)。X 上の実利用報告「Claude Code だと動くが Codex だと起動時にエラーで読み込まれない」が起点
|
|
8
|
+
|
|
9
|
+
### 追加
|
|
10
|
+
|
|
11
|
+
- **`c3 doctor` の MCP 起動自己診断**: 生成済み config(`.codex/config.toml` / `.cursor/mcp.json`)から `command` を読み取り、(a) 実行可能性 (b) `<command> -c "import c3"` の成否を検証する。失敗時は WARN と `c3 update --platform <p>` での再生成を案内。この種の環境起因の起動失敗を利用者が一発で自己診断できるようにした
|
|
12
|
+
|
|
13
|
+
### 変更
|
|
14
|
+
|
|
15
|
+
- `.claude/docs/platform-adapters.md` の「既知の制限」に、Codex の trust 要件(未 trust プロジェクトの `.codex/config.toml` は**エラーなしで無視**される・trust 手順)・`command` 絶対パス化の理由・adapter 生成物のマシン固有性(チームで共有せず各マシンで `c3 init/update --platform` 再生成する運用)を追記
|
|
16
|
+
|
|
17
|
+
### 後方互換
|
|
18
|
+
|
|
19
|
+
- 既存の生成済み config は次回 `c3 update --platform` 実行まで従来のまま。再生成すると `command` が実行環境の絶対パスに変わる(マシン固有・意図された挙動。チーム共有リポジトリでは各メンバーが自マシンで再生成する)。**破壊的変更なし**
|
|
20
|
+
|
|
21
|
+
## [2.49.0] - 2026-07-17
|
|
22
|
+
|
|
23
|
+
### 追加
|
|
24
|
+
|
|
25
|
+
- **運転モード切替の接続点(autonomous-mode 対応・既定 HITL・非破壊)**: dev-workflow に「運転モードによるゲート挙動の切替」集中参照ブロックを 1 つ追加(既存 10 承認ゲート本文は無改変)。session.tmp の `モード:` 行が有効な自律宣言のときのみ `.claude/skills/autonomous-mode/SKILL.md` のゲート対応表へ委譲する。**自律モード skill 本体は配布対象外**(配布元でのドッグフーディング熟成中・利用先には存在しないためモード行検証が常に無効=完全従来動作で挙動変化ゼロ)
|
|
26
|
+
- **init-session のモード行復元導線**: Step 1/3/5 に `モード:` 行の読込・サマリ表示(`運転モード:`)・自律復帰分岐を追加。plan-path は表示用 SR L-2 とは別の**パス用封じ込め型防御**(改行/NUL 除去→realpath 正規化→許可ルート `~/.claude/plans/` のみへの封じ込め→実在確認)を機械実行(`mode_line.py` へのパイプ渡し・目視代替禁止・スクリプト不在時は無効=HITL の fail-closed)で検証する。無効時は「HITL で継続」を明示表示
|
|
27
|
+
- **配布除外の 3 ファイル同期+機械検査**: `_excludes.py` / `hatch_build.py` に `skills/autonomous-mode/*`、`.gitignore` に `.claude/skills/autonomous-mode/` を追加(配布切替は 3 行削除で往復可)。`tests/test_excludes.py` に 3 ファイル同期の同一性検査(importlib+正規表現フォールバック)・除外パターン検査・モード行有効性判定テスト(空白パス・symlink/junction 偽装・CLI 契約)計 12 件を追加
|
|
28
|
+
|
|
29
|
+
### 変更
|
|
30
|
+
|
|
31
|
+
- `agents/architect.md`・`agents/planner.md` の tools に `Edit` を追加、`skills/report-timestamp/SKILL.md` に「Bash 非保持エージェントは親がタイムスタンプを取得しファイル名確定済みで渡す(手採番禁止・Bash 追加は最小権限維持のため採らない)」節を追記(2026-07-14 の設計監査で確定した運用契約の明文化)
|
|
32
|
+
|
|
33
|
+
### 後方互換
|
|
34
|
+
|
|
35
|
+
- `モード:` 行が無い session.tmp では全ゲートが従来どおり AskUserQuestion で動作(機械検証で担保)。wheel の `_template/` 一覧は本リリース前後で増減なし(HEAD 基準ビルドとの全数比較で確認)。**破壊的変更なし**
|
|
36
|
+
|
|
3
37
|
## [2.48.1] - 2026-07-15
|
|
4
38
|
|
|
5
39
|
### 修正
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-conductor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.50.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
|
|
@@ -60,6 +60,8 @@ EXCLUDE_PATTERNS: tuple[str, ...] = (
|
|
|
60
60
|
# v2.1.0: tdd-develop / worktree-tdd-workflow 廃止(planner が TDD を 3-wave に分解する設計に統一)
|
|
61
61
|
"agents/tdd-develop.md",
|
|
62
62
|
"skills/worktree-tdd-workflow/*",
|
|
63
|
+
# autonomous-mode skill を配布除外での熟成・配布切替は本行削除で往復可
|
|
64
|
+
"skills/autonomous-mode/*",
|
|
63
65
|
)
|
|
64
66
|
|
|
65
67
|
KEEP_PATTERNS: tuple[str, ...] = (
|
|
@@ -57,6 +57,8 @@ EXCLUDE_PATTERNS: tuple[str, ...] = (
|
|
|
57
57
|
# v2.1.0: tdd-develop / worktree-tdd-workflow 廃止(planner が TDD を 3-wave に分解する設計に統一)
|
|
58
58
|
"agents/tdd-develop.md",
|
|
59
59
|
"skills/worktree-tdd-workflow/*",
|
|
60
|
+
# autonomous-mode skill を配布除外での熟成・配布切替は本行削除で往復可
|
|
61
|
+
"skills/autonomous-mode/*",
|
|
60
62
|
)
|
|
61
63
|
|
|
62
64
|
KEEP_PATTERNS: tuple[str, ...] = (
|
|
@@ -223,7 +223,7 @@ def _write_cursor_mcp(target_root: Path, *, dry_run: bool) -> list[AdapterAction
|
|
|
223
223
|
if pythonpath is not None:
|
|
224
224
|
env["PYTHONPATH"] = str(pythonpath)
|
|
225
225
|
servers["c3"] = {
|
|
226
|
-
"command":
|
|
226
|
+
"command": sys.executable,
|
|
227
227
|
"args": ["-m", "c3.mcp_server"],
|
|
228
228
|
"env": env,
|
|
229
229
|
}
|
|
@@ -331,7 +331,7 @@ tool, read the matching generated Codex skill from `.agents/skills/<name>/SKILL.
|
|
|
331
331
|
def _codex_config_section() -> str:
|
|
332
332
|
lines = [
|
|
333
333
|
"[mcp_servers.c3]",
|
|
334
|
-
'command = "
|
|
334
|
+
f'command = "{_toml_escape(sys.executable)}"',
|
|
335
335
|
'args = ["-m", "c3.mcp_server"]',
|
|
336
336
|
"startup_timeout_sec = 10",
|
|
337
337
|
"tool_timeout_sec = 600",
|
|
@@ -4,7 +4,9 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
6
|
import json
|
|
7
|
+
import re
|
|
7
8
|
import shutil
|
|
9
|
+
import subprocess
|
|
8
10
|
from pathlib import Path
|
|
9
11
|
|
|
10
12
|
from c3._terminal import supports_color as _supports_color
|
|
@@ -15,6 +17,8 @@ _OK = "OK"
|
|
|
15
17
|
_WARN = "WARN"
|
|
16
18
|
_ERR = "ERR"
|
|
17
19
|
|
|
20
|
+
_MCP_STARTUP_TIMEOUT_SEC = 10
|
|
21
|
+
|
|
18
22
|
|
|
19
23
|
def register(subparsers: argparse._SubParsersAction) -> None:
|
|
20
24
|
parser = subparsers.add_parser(
|
|
@@ -111,6 +115,9 @@ def _check_codex_adapter() -> list[tuple[str, str, str]]:
|
|
|
111
115
|
findings.append(_dir_or_warn(".agents/skills", skills, "run `c3 init --platform codex`"))
|
|
112
116
|
findings.append(_file_or_warn(".codex/config.toml", config, "run `c3 init --platform codex`"))
|
|
113
117
|
findings.append(_dir_or_warn(".codex/agents", custom_agents, "run `c3 init --platform codex`"))
|
|
118
|
+
if config.is_file():
|
|
119
|
+
command = _extract_codex_mcp_command(config.read_text(encoding="utf-8"))
|
|
120
|
+
findings.append(_check_mcp_command_startup("codex MCP startup", command, "codex"))
|
|
114
121
|
codex = shutil.which("codex")
|
|
115
122
|
if codex is None:
|
|
116
123
|
findings.append((_WARN, "codex binary", "not on PATH; install Codex CLI to use the adapter"))
|
|
@@ -147,6 +154,9 @@ def _check_cursor_adapter() -> list[tuple[str, str, str]]:
|
|
|
147
154
|
mcp = root / ".cursor" / "mcp.json"
|
|
148
155
|
findings.append(_file_or_warn(".cursor/rules/c3-core.mdc", rule, "run `c3 init --platform cursor`"))
|
|
149
156
|
findings.append(_file_or_warn(".cursor/mcp.json", mcp, "run `c3 init --platform cursor`"))
|
|
157
|
+
if mcp.is_file():
|
|
158
|
+
command = _extract_cursor_mcp_command(mcp.read_text(encoding="utf-8"))
|
|
159
|
+
findings.append(_check_mcp_command_startup("cursor MCP startup", command, "cursor"))
|
|
150
160
|
cursor = shutil.which("cursor-agent") or shutil.which("cursor")
|
|
151
161
|
if cursor is None:
|
|
152
162
|
findings.append((_WARN, "cursor binary", "not on PATH; install Cursor CLI/editor to use the adapter"))
|
|
@@ -155,6 +165,73 @@ def _check_cursor_adapter() -> list[tuple[str, str, str]]:
|
|
|
155
165
|
return findings
|
|
156
166
|
|
|
157
167
|
|
|
168
|
+
def _extract_codex_mcp_command(config_text: str) -> str | None:
|
|
169
|
+
"""Read the ``command`` value from the ``[mcp_servers.c3]`` table.
|
|
170
|
+
|
|
171
|
+
Uses a targeted regex rather than a full TOML parser: ``tomllib`` is only
|
|
172
|
+
available from Python 3.11 while C3 supports 3.10+, and the generated
|
|
173
|
+
config is always a single-line ``key = "value"`` pair under the section.
|
|
174
|
+
"""
|
|
175
|
+
match = re.search(
|
|
176
|
+
r'(?ms)^\[mcp_servers\.c3\]\s*$.*?^command\s*=\s*"((?:[^"\\]|\\.)*)"',
|
|
177
|
+
config_text,
|
|
178
|
+
)
|
|
179
|
+
if match is None:
|
|
180
|
+
return None
|
|
181
|
+
return match.group(1).replace('\\"', '"').replace("\\\\", "\\")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _extract_cursor_mcp_command(mcp_json_text: str) -> str | None:
|
|
185
|
+
try:
|
|
186
|
+
payload = json.loads(mcp_json_text)
|
|
187
|
+
except json.JSONDecodeError:
|
|
188
|
+
return None
|
|
189
|
+
if not isinstance(payload, dict):
|
|
190
|
+
return None
|
|
191
|
+
servers = payload.get("mcpServers")
|
|
192
|
+
if not isinstance(servers, dict):
|
|
193
|
+
return None
|
|
194
|
+
c3 = servers.get("c3")
|
|
195
|
+
if not isinstance(c3, dict):
|
|
196
|
+
return None
|
|
197
|
+
command = c3.get("command")
|
|
198
|
+
return command if isinstance(command, str) else None
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _check_mcp_command_startup(
|
|
202
|
+
label: str, command: str | None, platform: str
|
|
203
|
+
) -> tuple[str, str, str]:
|
|
204
|
+
"""Verify that the generated MCP ``command`` can actually launch and import c3."""
|
|
205
|
+
hint = f"run `c3 update --platform {platform}` to regenerate"
|
|
206
|
+
if not command:
|
|
207
|
+
return _WARN, label, f"command not found in generated config; {hint}"
|
|
208
|
+
|
|
209
|
+
resolvable = (Path(command).is_absolute() and Path(command).is_file()) or (
|
|
210
|
+
shutil.which(command) is not None
|
|
211
|
+
)
|
|
212
|
+
if not resolvable:
|
|
213
|
+
return _WARN, label, f"command not executable: {command}; {hint}"
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
result = subprocess.run(
|
|
217
|
+
[command, "-c", "import c3"],
|
|
218
|
+
capture_output=True,
|
|
219
|
+
text=True,
|
|
220
|
+
timeout=_MCP_STARTUP_TIMEOUT_SEC,
|
|
221
|
+
)
|
|
222
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
223
|
+
return _WARN, label, f"failed to launch {command}: {exc}; {hint}"
|
|
224
|
+
|
|
225
|
+
if result.returncode != 0:
|
|
226
|
+
detail = next(
|
|
227
|
+
(line for line in reversed(result.stderr.strip().splitlines()) if line.strip()),
|
|
228
|
+
"import c3 failed",
|
|
229
|
+
)
|
|
230
|
+
return _WARN, label, f'`{command} -c "import c3"` failed: {detail}; {hint}'
|
|
231
|
+
|
|
232
|
+
return _OK, label, f"{command} -c \"import c3\" succeeded"
|
|
233
|
+
|
|
234
|
+
|
|
158
235
|
def _file_or_warn(label: str, path: Path, hint: str) -> tuple[str, str, str]:
|
|
159
236
|
if path.is_file():
|
|
160
237
|
return _OK, label, str(path)
|
|
@@ -58,6 +58,48 @@ def test_toml_multiline_escape_keeps_newlines():
|
|
|
58
58
|
assert _toml_multiline_escape(text) == text
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
# ----------------------------------------------------------------------
|
|
62
|
+
# MCP `command` resolution — absolute sys.executable, not bare "python"
|
|
63
|
+
# ----------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_codex_config_section_uses_absolute_sys_executable(monkeypatch):
|
|
67
|
+
monkeypatch.setattr(adapters.sys, "executable", r"C:\Python312\python.exe")
|
|
68
|
+
|
|
69
|
+
section = adapters._codex_config_section()
|
|
70
|
+
|
|
71
|
+
assert 'command = "C:\\\\Python312\\\\python.exe"' in section
|
|
72
|
+
assert 'command = "python"' not in section
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_codex_config_section_escapes_plain_posix_executable(monkeypatch):
|
|
76
|
+
monkeypatch.setattr(adapters.sys, "executable", "/usr/bin/python3")
|
|
77
|
+
|
|
78
|
+
section = adapters._codex_config_section()
|
|
79
|
+
|
|
80
|
+
assert 'command = "/usr/bin/python3"' in section
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_write_codex_config_writes_absolute_windows_command(tmp_path: Path, monkeypatch):
|
|
84
|
+
monkeypatch.setattr(adapters.sys, "executable", r"C:\Users\dev\venv\Scripts\python.exe")
|
|
85
|
+
|
|
86
|
+
adapters._write_codex_config(tmp_path, dry_run=False)
|
|
87
|
+
|
|
88
|
+
config_text = (tmp_path / ".codex" / "config.toml").read_text(encoding="utf-8")
|
|
89
|
+
assert 'command = "C:\\\\Users\\\\dev\\\\venv\\\\Scripts\\\\python.exe"' in config_text
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_write_cursor_mcp_uses_absolute_sys_executable(tmp_path: Path, monkeypatch):
|
|
93
|
+
monkeypatch.setattr(adapters.sys, "executable", r"C:\Python312\python.exe")
|
|
94
|
+
|
|
95
|
+
adapters._write_cursor_mcp(tmp_path, dry_run=False)
|
|
96
|
+
|
|
97
|
+
payload = json.loads((tmp_path / ".cursor" / "mcp.json").read_text(encoding="utf-8"))
|
|
98
|
+
command = payload["mcpServers"]["c3"]["command"]
|
|
99
|
+
assert command == r"C:\Python312\python.exe"
|
|
100
|
+
assert Path(command).is_absolute()
|
|
101
|
+
|
|
102
|
+
|
|
61
103
|
# ----------------------------------------------------------------------
|
|
62
104
|
# _convert_skill
|
|
63
105
|
# ----------------------------------------------------------------------
|
|
@@ -178,7 +220,8 @@ def test_write_cursor_mcp_creates_file_when_missing(tmp_path: Path):
|
|
|
178
220
|
actions = _write_cursor_mcp(target, dry_run=False)
|
|
179
221
|
assert any(a.action == "add" for a in actions)
|
|
180
222
|
payload = json.loads((target / ".cursor" / "mcp.json").read_text(encoding="utf-8"))
|
|
181
|
-
assert payload["mcpServers"]["c3"]["command"] ==
|
|
223
|
+
assert payload["mcpServers"]["c3"]["command"] == sys.executable
|
|
224
|
+
assert Path(payload["mcpServers"]["c3"]["command"]).is_absolute()
|
|
182
225
|
|
|
183
226
|
|
|
184
227
|
def test_write_cursor_mcp_rejects_invalid_json(tmp_path: Path):
|
|
@@ -7,6 +7,8 @@ in handle() for opencode, codex, and cursor platforms.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import argparse
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
10
12
|
from pathlib import Path
|
|
11
13
|
|
|
12
14
|
import c3.cli_doctor as cli_doctor
|
|
@@ -181,3 +183,169 @@ def test_check_opencode_adapter_reports_binary_present(tmp_path: Path, monkeypat
|
|
|
181
183
|
status == _OK and "opencode binary" in label and fake_path in detail
|
|
182
184
|
for status, label, detail in findings
|
|
183
185
|
), f"Expected OK for 'opencode binary' with path, got: {findings}"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
# MCP startup self-diagnostic — _check_mcp_command_startup
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_check_mcp_command_startup_ok_when_import_succeeds(monkeypatch):
|
|
194
|
+
"""When the resolved command can `import c3`, the check must return OK."""
|
|
195
|
+
monkeypatch.setattr(
|
|
196
|
+
cli_doctor.subprocess,
|
|
197
|
+
"run",
|
|
198
|
+
lambda *a, **k: subprocess.CompletedProcess(a[0], 0, stdout="", stderr=""),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
status, label, detail = cli_doctor._check_mcp_command_startup(
|
|
202
|
+
"codex MCP startup", sys.executable, "codex"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
assert status == _OK
|
|
206
|
+
assert "codex MCP startup" == label
|
|
207
|
+
assert "import c3" in detail
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_check_mcp_command_startup_warns_when_command_missing():
|
|
211
|
+
"""When the config has no `command` value at all, the check must WARN."""
|
|
212
|
+
status, label, detail = cli_doctor._check_mcp_command_startup(
|
|
213
|
+
"codex MCP startup", None, "codex"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
assert status == _WARN
|
|
217
|
+
assert "not found" in detail
|
|
218
|
+
assert "c3 update --platform codex" in detail
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_check_mcp_command_startup_warns_when_command_not_resolvable(monkeypatch):
|
|
222
|
+
"""When the command path/binary cannot be located at all, the check must WARN
|
|
223
|
+
without attempting to spawn a subprocess."""
|
|
224
|
+
monkeypatch.setattr(cli_doctor.shutil, "which", lambda name: None)
|
|
225
|
+
called = []
|
|
226
|
+
monkeypatch.setattr(
|
|
227
|
+
cli_doctor.subprocess,
|
|
228
|
+
"run",
|
|
229
|
+
lambda *a, **k: called.append(a) or subprocess.CompletedProcess(a[0], 0),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
status, label, detail = cli_doctor._check_mcp_command_startup(
|
|
233
|
+
"cursor MCP startup", "/nonexistent/path/to/python", "cursor"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
assert status == _WARN
|
|
237
|
+
assert "not executable" in detail
|
|
238
|
+
assert "c3 update --platform cursor" in detail
|
|
239
|
+
assert called == []
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def test_check_mcp_command_startup_warns_when_import_c3_fails(monkeypatch):
|
|
243
|
+
"""When the resolved command runs but `import c3` fails (e.g. an isolated
|
|
244
|
+
venv/pipx environment without c3 installed), the check must WARN."""
|
|
245
|
+
monkeypatch.setattr(
|
|
246
|
+
cli_doctor.subprocess,
|
|
247
|
+
"run",
|
|
248
|
+
lambda *a, **k: subprocess.CompletedProcess(
|
|
249
|
+
a[0], 1, stdout="", stderr="ModuleNotFoundError: No module named 'c3'"
|
|
250
|
+
),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
status, label, detail = cli_doctor._check_mcp_command_startup(
|
|
254
|
+
"codex MCP startup", sys.executable, "codex"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
assert status == _WARN
|
|
258
|
+
assert "No module named" in detail
|
|
259
|
+
assert "c3 update --platform codex" in detail
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_check_mcp_command_startup_warns_when_launch_raises(monkeypatch):
|
|
263
|
+
"""When subprocess.run itself raises (e.g. the binary vanished between the
|
|
264
|
+
resolvability check and the launch), the check must WARN, not raise."""
|
|
265
|
+
def _boom(*a, **k):
|
|
266
|
+
raise FileNotFoundError("no such file")
|
|
267
|
+
|
|
268
|
+
monkeypatch.setattr(cli_doctor.subprocess, "run", _boom)
|
|
269
|
+
|
|
270
|
+
status, label, detail = cli_doctor._check_mcp_command_startup(
|
|
271
|
+
"codex MCP startup", sys.executable, "codex"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
assert status == _WARN
|
|
275
|
+
assert "failed to launch" in detail
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# ---------------------------------------------------------------------------
|
|
279
|
+
# command extraction from generated adapter configs
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def test_extract_codex_mcp_command_reads_absolute_windows_path():
|
|
284
|
+
config_text = (
|
|
285
|
+
"[mcp_servers.c3]\n"
|
|
286
|
+
'command = "C:\\\\Python312\\\\python.exe"\n'
|
|
287
|
+
'args = ["-m", "c3.mcp_server"]\n'
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
command = cli_doctor._extract_codex_mcp_command(config_text)
|
|
291
|
+
|
|
292
|
+
assert command == r"C:\Python312\python.exe"
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def test_extract_codex_mcp_command_none_when_section_missing():
|
|
296
|
+
assert cli_doctor._extract_codex_mcp_command("[some_other_table]\nfoo = 1\n") is None
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def test_extract_cursor_mcp_command_reads_absolute_path():
|
|
300
|
+
mcp_json = '{"mcpServers": {"c3": {"command": "/usr/bin/python3", "args": []}}}'
|
|
301
|
+
|
|
302
|
+
command = cli_doctor._extract_cursor_mcp_command(mcp_json)
|
|
303
|
+
|
|
304
|
+
assert command == "/usr/bin/python3"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_extract_cursor_mcp_command_none_when_invalid_json():
|
|
308
|
+
assert cli_doctor._extract_cursor_mcp_command("{not valid") is None
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# ---------------------------------------------------------------------------
|
|
312
|
+
# integration — _check_codex_adapter / _check_cursor_adapter wire the
|
|
313
|
+
# self-diagnostic in using the real, currently-running interpreter
|
|
314
|
+
# ---------------------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_check_codex_adapter_includes_ok_mcp_startup_with_real_interpreter(
|
|
318
|
+
tmp_path: Path, monkeypatch
|
|
319
|
+
):
|
|
320
|
+
"""End-to-end: generate a real adapter config pointed at sys.executable
|
|
321
|
+
(which does have c3 importable under the sandbox PYTHONPATH) and confirm
|
|
322
|
+
the doctor wires the self-diagnostic in as OK."""
|
|
323
|
+
(tmp_path / ".claude").mkdir()
|
|
324
|
+
monkeypatch.chdir(tmp_path)
|
|
325
|
+
from c3.adapters import _write_codex_config
|
|
326
|
+
|
|
327
|
+
_write_codex_config(tmp_path, dry_run=False)
|
|
328
|
+
|
|
329
|
+
findings = cli_doctor._check_codex_adapter()
|
|
330
|
+
|
|
331
|
+
assert any(
|
|
332
|
+
status == _OK and label == "codex MCP startup"
|
|
333
|
+
for status, label, _ in findings
|
|
334
|
+
), f"Expected OK codex MCP startup finding, got: {findings}"
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def test_check_cursor_adapter_includes_ok_mcp_startup_with_real_interpreter(
|
|
338
|
+
tmp_path: Path, monkeypatch
|
|
339
|
+
):
|
|
340
|
+
(tmp_path / ".claude").mkdir()
|
|
341
|
+
monkeypatch.chdir(tmp_path)
|
|
342
|
+
from c3.adapters import _write_cursor_mcp
|
|
343
|
+
|
|
344
|
+
_write_cursor_mcp(tmp_path, dry_run=False)
|
|
345
|
+
|
|
346
|
+
findings = cli_doctor._check_cursor_adapter()
|
|
347
|
+
|
|
348
|
+
assert any(
|
|
349
|
+
status == _OK and label == "cursor MCP startup"
|
|
350
|
+
for status, label, _ in findings
|
|
351
|
+
), f"Expected OK cursor MCP startup finding, got: {findings}"
|