claude-code-conductor 2.11.0__tar.gz → 2.12.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.11.0 → claude_code_conductor-2.12.0}/.claude/settings.json +3 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/dev-workflow/SKILL.md +10 -10
- claude_code_conductor-2.12.0/.claude/skills/dev-workflow/scripts/record_review_decision.py +138 -0
- {claude_code_conductor-2.11.0/.claude/hooks → claude_code_conductor-2.12.0/.claude/skills/dev-workflow/scripts}/record_tier_outcome.py +34 -7
- {claude_code_conductor-2.11.0/.claude/hooks → claude_code_conductor-2.12.0/.claude/skills/dev-workflow/scripts}/review_hint_inject.py +67 -9
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/mcp-config/SKILL.md +5 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/CHANGELOG.md +58 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/PKG-INFO +1 -1
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/__init__.py +1 -1
- claude_code_conductor-2.12.0/tests/hooks/test_record_review_decision.py +239 -0
- claude_code_conductor-2.12.0/tests/hooks/test_record_tier_outcome.py +505 -0
- claude_code_conductor-2.12.0/tests/hooks/test_review_hint_inject.py +625 -0
- claude_code_conductor-2.11.0/.claude/hooks/record_review_decision.py +0 -73
- claude_code_conductor-2.11.0/.claude/hooks/subagent_log.py +0 -227
- claude_code_conductor-2.11.0/tests/hooks/test_record_tier_outcome.py +0 -291
- claude_code_conductor-2.11.0/tests/hooks/test_review_hint_inject.py +0 -356
- claude_code_conductor-2.11.0/tests/hooks/test_subagent_log.py +0 -1030
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/schema.sql +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/select_tier.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/rules/code-review-checklist.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/rules/security-review-checklist.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/code-review/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.gitignore +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/LICENSE +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/LICENSES/chroma-hnswlib-LICENSE +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/LICENSES/chroma-hnswlib-NOTICE +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/README.md +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_tier.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/db.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_select_tier.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_cli_tier.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/tests/test_worktree_guard.py +0 -0
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
"Bash(python .claude/hooks/restore_session.py*)",
|
|
8
8
|
"Bash(python .claude/hooks/permission_handler.py*)",
|
|
9
9
|
"Bash(python .claude/hooks/statusline.py*)",
|
|
10
|
+
"Bash(python .claude/skills/dev-workflow/scripts/review_hint_inject.py*)",
|
|
11
|
+
"Bash(python .claude/skills/dev-workflow/scripts/record_review_decision.py*)",
|
|
12
|
+
"Bash(python .claude/skills/dev-workflow/scripts/record_tier_outcome.py*)",
|
|
10
13
|
"Read(**)",
|
|
11
14
|
"Glob(**)",
|
|
12
15
|
"Grep(**)",
|
{claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/dev-workflow/SKILL.md
RENAMED
|
@@ -406,10 +406,10 @@ AskUserQuestion で確認する:
|
|
|
406
406
|
Agent ツールで `code-reviewer` エージェントを起動する。
|
|
407
407
|
|
|
408
408
|
**review-hint 過去判断ヒント注入(レポート生成後):**
|
|
409
|
-
code-reviewer がレポートを Write し終えたら、Bash で `.claude/
|
|
409
|
+
code-reviewer がレポートを Write し終えたら、Bash で `.claude/skills/dev-workflow/scripts/review_hint_inject.py` を呼んで過去判断ヒントをレポート末尾に追記する:
|
|
410
410
|
|
|
411
411
|
```bash
|
|
412
|
-
python .claude/
|
|
412
|
+
python .claude/skills/dev-workflow/scripts/review_hint_inject.py .claude/reports/code-review-report-{timestamp}.md
|
|
413
413
|
```
|
|
414
414
|
|
|
415
415
|
ヒントは独立セクションとして追加されるだけで、code-reviewer の指摘本文は変更されない。
|
|
@@ -465,7 +465,7 @@ AskUserQuestion で確認する:
|
|
|
465
465
|
2. 許容する指摘の直下に `> **[許容]** {理由}` を Edit で追記する(検出記録は削除しない)
|
|
466
466
|
3. **review-hint 判断記録**: 各指摘について Bash で c3.db に記録する(`[CR-XX-NNN]` を含むもののみ。`[CR-NEW]` は記録対象外、チェックリスト追加候補として別途扱う):
|
|
467
467
|
```bash
|
|
468
|
-
python .claude/
|
|
468
|
+
python .claude/skills/dev-workflow/scripts/record_review_decision.py \
|
|
469
469
|
--checklist-id CR-Q-001 \
|
|
470
470
|
--finding "{指摘本文を 1 行で}" \
|
|
471
471
|
--decision {fixed|accepted} \
|
|
@@ -500,10 +500,10 @@ AskUserQuestion で許容理由を確認する:
|
|
|
500
500
|
Agent ツールで `security-reviewer` エージェントを起動する。
|
|
501
501
|
|
|
502
502
|
**review-hint 過去判断ヒント注入(レポート生成後):**
|
|
503
|
-
security-reviewer がレポートを Write し終えたら、Bash で `.claude/
|
|
503
|
+
security-reviewer がレポートを Write し終えたら、Bash で `.claude/skills/dev-workflow/scripts/review_hint_inject.py` に **両レポートのパス** を渡して呼ぶ。両方渡すことで重複指摘フラグ(同じ checklist_id を CR と SR が指摘)が判定される:
|
|
504
504
|
|
|
505
505
|
```bash
|
|
506
|
-
python .claude/
|
|
506
|
+
python .claude/skills/dev-workflow/scripts/review_hint_inject.py \
|
|
507
507
|
.claude/reports/code-review-report-{ts1}.md \
|
|
508
508
|
.claude/reports/security-review-report-{ts2}.md
|
|
509
509
|
```
|
|
@@ -530,7 +530,7 @@ AskUserQuestion で確認する:
|
|
|
530
530
|
承認後 → セッションファイルの `- [ ] security-review` を `- [x]` に Edit する。続けて **「引き継ぎバックログの照合」**(後述の共通ステップ)を実行してからコミットを提案する。
|
|
531
531
|
**tier-routing 結果記録**: フェーズ E の最終承認時のみ Bash で記録する(多重カウント防止のため E-1 では記録しない):
|
|
532
532
|
```bash
|
|
533
|
-
python .claude/
|
|
533
|
+
python .claude/skills/dev-workflow/scripts/record_tier_outcome.py --outcome success
|
|
534
534
|
```
|
|
535
535
|
|
|
536
536
|
**指摘がある場合:**
|
|
@@ -565,7 +565,7 @@ python .claude/hooks/record_tier_outcome.py --outcome success
|
|
|
565
565
|
2. 許容する指摘の直下に `> **[許容]** {理由}` を Edit で追記する(検出記録は削除しない)
|
|
566
566
|
3. **review-hint 判断記録**: 各指摘について Bash で c3.db に記録する(`[SR-XX-NNN]` を含むもののみ。`[SR-NEW]` は記録対象外、チェックリスト追加候補として別途扱う):
|
|
567
567
|
```bash
|
|
568
|
-
python .claude/
|
|
568
|
+
python .claude/skills/dev-workflow/scripts/record_review_decision.py \
|
|
569
569
|
--checklist-id SR-K-002 \
|
|
570
570
|
--finding "{指摘本文を 1 行で}" \
|
|
571
571
|
--decision {fixed|accepted} \
|
|
@@ -590,7 +590,7 @@ AskUserQuestion で許容理由を確認する:
|
|
|
590
590
|
4. セッションファイルの `- [ ] security-review` を `- [x]` に Edit する。続けて **「引き継ぎバックログの照合」**(後述の共通ステップ)を実行してからコミットを提案する。
|
|
591
591
|
5. **tier-routing 結果記録**: 全許容で完了するのも「成功」としてカウント:
|
|
592
592
|
```bash
|
|
593
|
-
python .claude/
|
|
593
|
+
python .claude/skills/dev-workflow/scripts/record_tier_outcome.py --outcome success
|
|
594
594
|
```
|
|
595
595
|
|
|
596
596
|
**「否認・再診断を依頼する」の場合:**
|
|
@@ -598,13 +598,13 @@ AskUserQuestion で許容理由を確認する:
|
|
|
598
598
|
セッションファイルの `## 試みたが失敗したアプローチ` に教訓をルール形式で追記し `patterns` に追加する。
|
|
599
599
|
**tier-routing 結果記録**: 否認は「失敗」としてカウント:
|
|
600
600
|
```bash
|
|
601
|
-
python .claude/
|
|
601
|
+
python .claude/skills/dev-workflow/scripts/record_tier_outcome.py --outcome failure
|
|
602
602
|
```
|
|
603
603
|
|
|
604
604
|
**「全て対応する」「対応する指摘を選ぶ」の場合(フェーズ C へ戻る):**
|
|
605
605
|
これらも tier の選択がコスト最適でなかったとみなし、**tier-routing 結果記録**で失敗をカウントしてからフェーズ C へ:
|
|
606
606
|
```bash
|
|
607
|
-
python .claude/
|
|
607
|
+
python .claude/skills/dev-workflow/scripts/record_tier_outcome.py --outcome failure
|
|
608
608
|
```
|
|
609
609
|
|
|
610
610
|
---
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""CLI: record a review decision in c3.db.review_decisions.
|
|
3
|
+
|
|
4
|
+
review-hint: dev-workflow フェーズ E でユーザーが「対応 / 許容 / 保留」を選んだ
|
|
5
|
+
判断を SQLite に記録するための薄い CLI ラッパー。実装本体は
|
|
6
|
+
``c3.db.insert_review_decision``。
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python .claude/skills/dev-workflow/scripts/record_review_decision.py \
|
|
10
|
+
--checklist-id CR-Q-001 \
|
|
11
|
+
--finding "関数が長い" \
|
|
12
|
+
--decision accepted \
|
|
13
|
+
--reason "既存スタイルを尊重するため" \
|
|
14
|
+
--reviewer code-reviewer
|
|
15
|
+
|
|
16
|
+
Exit code:
|
|
17
|
+
0: 記録成功 / DB が無く何もしなかった(呼び出し元を止めない方針)
|
|
18
|
+
2: 引数不正(argparse のエラー時)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import re
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# checklist_id 形式検証用の正規表現([CR-XX-NNN] / [SR-XX-NNN]、連番 3 桁以上)[SR-V-001]。
|
|
29
|
+
# review_hint_inject.py の CHECKLIST_ID_RE と整合([ ] なし)。
|
|
30
|
+
CHECKLIST_ID_PATTERN = re.compile(r"^(CR|SR)-[A-Z]+-\d{3,}$")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# DB 肥大化防止のためのフィールド長上限 [SR-V-001]。
|
|
34
|
+
# 文字数 / バイト数の両方を上限として切り詰める(呼び出し元を止めない方針)。
|
|
35
|
+
# サロゲートペア・絵文字等で UTF-8 バイト長が文字長を大きく超えるケースを防ぐ。
|
|
36
|
+
MAX_FINDING_LEN = 2000 # 文字数上限
|
|
37
|
+
MAX_REASON_LEN = 2000
|
|
38
|
+
MAX_CONTEXT_LEN = 1000
|
|
39
|
+
MAX_FIELD_BYTES = 8 * 1024 # 全フィールド共通バイト数上限(8 KB)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _truncate(value: str | None, limit: int, name: str) -> str | None:
|
|
43
|
+
"""value が文字数 limit 超または UTF-8 バイト数 MAX_FIELD_BYTES 超なら切り詰めて警告を出す。
|
|
44
|
+
|
|
45
|
+
None / 空文字列はそのまま返す。文字数で切ったあともバイト数を再確認し、
|
|
46
|
+
両条件で安全になるまで切り詰める(BMP 外文字対応)。
|
|
47
|
+
|
|
48
|
+
NOTE: 現在の定数(MAX_FINDING_LEN=2000・MAX_FIELD_BYTES=8192)では、
|
|
49
|
+
BMP 外の 4 バイト UTF-8 文字 2000 個でも 8000 バイトしか消費しないため、
|
|
50
|
+
文字数で切った後の while ループは実質的に発火しない(防御的残置)。
|
|
51
|
+
将来 MAX_*_LEN を引き上げる場合に備えて while を残してある。
|
|
52
|
+
定数を変更する際は両条件の整合(MAX_LEN * 4 > MAX_FIELD_BYTES)を必ず確認すること。
|
|
53
|
+
|
|
54
|
+
パフォーマンス: while ループ最悪計算量は O(N^2)(N=文字数)であり、入力長が
|
|
55
|
+
数十 MB のオーダーで遅くなる。ローカル CLI 前提・上記の通り通常入力では
|
|
56
|
+
そもそも while に入らないため許容。本来高速化するなら bytes 単位で 2 分探索する。
|
|
57
|
+
"""
|
|
58
|
+
if not value:
|
|
59
|
+
return value
|
|
60
|
+
truncated = False
|
|
61
|
+
if len(value) > limit:
|
|
62
|
+
value = value[:limit]
|
|
63
|
+
truncated = True
|
|
64
|
+
byte_len = len(value.encode("utf-8"))
|
|
65
|
+
while byte_len > MAX_FIELD_BYTES:
|
|
66
|
+
value = value[: max(1, len(value) - 1)]
|
|
67
|
+
byte_len = len(value.encode("utf-8"))
|
|
68
|
+
truncated = True
|
|
69
|
+
if truncated:
|
|
70
|
+
print(
|
|
71
|
+
f"[record_review_decision] --{name} truncated to {len(value)} chars / "
|
|
72
|
+
f"{byte_len} bytes",
|
|
73
|
+
file=sys.stderr,
|
|
74
|
+
)
|
|
75
|
+
return value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main(argv: list[str] | None = None) -> int:
|
|
79
|
+
parser = argparse.ArgumentParser(description="Record a review decision in c3.db")
|
|
80
|
+
parser.add_argument("--checklist-id", required=True,
|
|
81
|
+
help="例: CR-Q-001 / SR-K-002")
|
|
82
|
+
parser.add_argument("--finding", required=True,
|
|
83
|
+
help=f"指摘本文(後の参照用、最大 {MAX_FINDING_LEN} 文字で切り詰め)")
|
|
84
|
+
parser.add_argument("--decision", required=True,
|
|
85
|
+
choices=["fixed", "accepted", "deferred"],
|
|
86
|
+
help="判断")
|
|
87
|
+
parser.add_argument("--reason", default=None,
|
|
88
|
+
help=f"許容/保留時の理由(最大 {MAX_REASON_LEN} 文字で切り詰め)")
|
|
89
|
+
parser.add_argument("--context", default=None,
|
|
90
|
+
help=f"ファイル名・コミット等の補助情報(最大 {MAX_CONTEXT_LEN} 文字で切り詰め)")
|
|
91
|
+
parser.add_argument("--reviewer", required=True,
|
|
92
|
+
choices=["code-reviewer", "security-reviewer"],
|
|
93
|
+
help="どちらの reviewer の指摘か")
|
|
94
|
+
|
|
95
|
+
args = parser.parse_args(argv)
|
|
96
|
+
|
|
97
|
+
# checklist-id 形式検証(不正な値は DB に蓄積させず skip。CR-NEW / SR-NEW は対象外として除外)
|
|
98
|
+
# [SR-V-001] 不正な ID が DB に入ると review-hint 照合が空振りするため insert を中止する
|
|
99
|
+
if args.checklist_id not in ("CR-NEW", "SR-NEW") and not CHECKLIST_ID_PATTERN.match(args.checklist_id):
|
|
100
|
+
print(
|
|
101
|
+
f"[record_review_decision] --checklist-id format invalid (skipped): {args.checklist_id!r} "
|
|
102
|
+
f"(expected pattern: CR-XX-NNN or SR-XX-NNN)",
|
|
103
|
+
file=sys.stderr,
|
|
104
|
+
)
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
# 長さ上限を適用(DB 肥大化防止)
|
|
108
|
+
args.finding = _truncate(args.finding, MAX_FINDING_LEN, "finding")
|
|
109
|
+
args.reason = _truncate(args.reason, MAX_REASON_LEN, "reason")
|
|
110
|
+
args.context = _truncate(args.context, MAX_CONTEXT_LEN, "context")
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
from c3.db import insert_review_decision
|
|
114
|
+
except ImportError as exc:
|
|
115
|
+
print(f"[record_review_decision] c3.db import failed: {exc}",
|
|
116
|
+
file=sys.stderr)
|
|
117
|
+
return 0 # 呼び出し元を止めない
|
|
118
|
+
|
|
119
|
+
ok = insert_review_decision(
|
|
120
|
+
checklist_id=args.checklist_id,
|
|
121
|
+
finding_text=args.finding,
|
|
122
|
+
decision=args.decision,
|
|
123
|
+
reason=args.reason,
|
|
124
|
+
context_summary=args.context,
|
|
125
|
+
reviewer=args.reviewer,
|
|
126
|
+
)
|
|
127
|
+
if not ok:
|
|
128
|
+
# 失敗は警告のみ(DB 不在等で C3 利用先で頻繁に起きうる)
|
|
129
|
+
print(
|
|
130
|
+
f"[record_review_decision] not recorded "
|
|
131
|
+
f"(DB unavailable?): {args.checklist_id}",
|
|
132
|
+
file=sys.stderr,
|
|
133
|
+
)
|
|
134
|
+
return 0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
sys.exit(main())
|
|
@@ -7,8 +7,8 @@ tier-routing MVP: dev-workflow フェーズ E の承認/否認シグナルを受
|
|
|
7
7
|
更新する。
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
|
-
python .claude/
|
|
11
|
-
python .claude/
|
|
10
|
+
python .claude/skills/dev-workflow/scripts/record_tier_outcome.py --outcome success
|
|
11
|
+
python .claude/skills/dev-workflow/scripts/record_tier_outcome.py --outcome failure
|
|
12
12
|
|
|
13
13
|
設計のポイント:
|
|
14
14
|
- 1 引数のみ(``--outcome``)にして dev-workflow から呼びやすくする。
|
|
@@ -20,6 +20,7 @@ Usage:
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
22
|
import argparse
|
|
23
|
+
import collections
|
|
23
24
|
import json
|
|
24
25
|
import os
|
|
25
26
|
import sys
|
|
@@ -33,8 +34,23 @@ try:
|
|
|
33
34
|
except AttributeError:
|
|
34
35
|
pass
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
# このファイルは .claude/skills/dev-workflow/scripts/ に置かれている前提。
|
|
38
|
+
# 上位 3 階層を遡って .claude/ ディレクトリを得る:
|
|
39
|
+
# scripts/ → dev-workflow/ → skills/ → .claude/
|
|
40
|
+
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
41
|
+
_CLAUDE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(_SCRIPT_DIR)))
|
|
42
|
+
# 3 階層遡りで `.claude` に到達することを実行時に検証。
|
|
43
|
+
# 将来スクリプトが別階層に移動された場合のサイレント破綻を防ぐ。
|
|
44
|
+
#
|
|
45
|
+
# 注: これはセキュリティ防御ではなく、スクリプトの誤配置(ディレクトリ階層変更・
|
|
46
|
+
# 移動忘れ等)を実行時に検出するための開発時チェック。外部攻撃者がディレクトリ
|
|
47
|
+
# 構造を制御できる脅威モデルは前提としていない([SR-NEW])。
|
|
48
|
+
# `assert` は `python -O` で無効化されるため、RuntimeError で明示的に投げる。
|
|
49
|
+
if not (_CLAUDE_DIR.endswith(os.sep + ".claude") or _CLAUDE_DIR.endswith("/.claude")):
|
|
50
|
+
raise RuntimeError(
|
|
51
|
+
f"_CLAUDE_DIR resolution broke: expected to end with '.claude' but got {_CLAUDE_DIR!r}. "
|
|
52
|
+
"Check that this file is at .claude/skills/dev-workflow/scripts/."
|
|
53
|
+
)
|
|
38
54
|
TIER_SELECTION_PATH = os.path.join(_CLAUDE_DIR, "state", "tier_selection.json")
|
|
39
55
|
# Phase 2-C: prompt 履歴ファイル(select_tier.py が読む類似度推定の母数)。
|
|
40
56
|
PROMPT_HISTORY_PATH = os.path.join(_CLAUDE_DIR, "logs", "prompt-history.jsonl")
|
|
@@ -107,9 +123,8 @@ def _rotate_prompt_history_if_needed() -> None:
|
|
|
107
123
|
return
|
|
108
124
|
try:
|
|
109
125
|
# 末尾 N 行のみ deque で保持して上書きする(ファイル全体は走査するが I/O のみ)
|
|
110
|
-
import collections as _c
|
|
111
126
|
with open(PROMPT_HISTORY_PATH, "r", encoding="utf-8") as f:
|
|
112
|
-
tail = list(
|
|
127
|
+
tail = list(collections.deque(f, maxlen=_PROMPT_HISTORY_TRUNCATE_LINES))
|
|
113
128
|
tmp_path = PROMPT_HISTORY_PATH + ".tmp"
|
|
114
129
|
with open(tmp_path, "w", encoding="utf-8") as f:
|
|
115
130
|
f.writelines(tail)
|
|
@@ -143,8 +158,20 @@ def _append_prompt_history(selection: dict, success: bool) -> None:
|
|
|
143
158
|
try:
|
|
144
159
|
os.makedirs(os.path.dirname(PROMPT_HISTORY_PATH), exist_ok=True)
|
|
145
160
|
_rotate_prompt_history_if_needed()
|
|
161
|
+
line = json.dumps(record, ensure_ascii=False)
|
|
162
|
+
# JSONL 互換性: U+2028 (LINE SEPARATOR) / U+2029 (PARAGRAPH SEPARATOR) を
|
|
163
|
+
# ECMAScript パーサが行区切りと解釈するため事前にエスケープする [SR-V-001]。
|
|
164
|
+
# NOTE: ソースコード上は escape 表記で識谞し、実体文字を埋め込まない
|
|
165
|
+
# (Cycle 3 M-01 / Cycle 4 H-01 の回帰防止。)
|
|
166
|
+
_LS = chr(0x2028) # LINE SEPARATOR
|
|
167
|
+
_PS = chr(0x2029) # PARAGRAPH SEPARATOR
|
|
168
|
+
# The second arg must be the 6-char ASCII string '\u2028' (literal backslash + 'u2028'),
|
|
169
|
+
# NOT the actual U+2028 char. In Python a normal string "\u2028" evaluates to the
|
|
170
|
+
# 1-char separator, but raw string r"\u2028" keeps the backslash literal, giving the
|
|
171
|
+
# 6-char escape sequence that JSON consumers need. Do NOT remove the r-prefix.
|
|
172
|
+
line = line.replace(_LS, r"\u2028").replace(_PS, r"\u2029")
|
|
146
173
|
with open(PROMPT_HISTORY_PATH, "a", encoding="utf-8") as f:
|
|
147
|
-
f.write(
|
|
174
|
+
f.write(line + "\n")
|
|
148
175
|
except OSError as exc:
|
|
149
176
|
print(
|
|
150
177
|
f"[record_tier_outcome] prompt-history append skipped: {exc}",
|
|
@@ -18,6 +18,14 @@ review-hint: レビュー判断ヒント機能。code-reviewer / security-review
|
|
|
18
18
|
出力:
|
|
19
19
|
- 指定された各レポートの末尾に「## 過去判断ヒント」セクションを追記
|
|
20
20
|
- exit code: 0 (失敗してもセッションを止めない方針)
|
|
21
|
+
|
|
22
|
+
セキュリティ注記:
|
|
23
|
+
- 引数はファイルシステム上の実在するパスのみ受け付ける(`p.is_file()` チェック済み)。
|
|
24
|
+
存在しないパス・ディレクトリは警告を出してスキップする。
|
|
25
|
+
- URL エンコードは解除しない。`%2e%2e` 等のリテラル文字列はディレクトリ名として
|
|
26
|
+
そのまま扱われる(パスは `Path.resolve()` → `relative_to(ALLOWED_REPORT_DIR)` で
|
|
27
|
+
正規化後に .claude/reports/ 配下のみ許可するため、エンコード解除は不要かつ不可)。
|
|
28
|
+
- 攻撃者が引数を直接制御できる脅威モデルは想定外(ローカル CLI 前提)。
|
|
21
29
|
"""
|
|
22
30
|
|
|
23
31
|
from __future__ import annotations
|
|
@@ -47,14 +55,24 @@ DEFAULT_REEVAL_DAYS = 30 * 6
|
|
|
47
55
|
# 短すぎる連番([CR-Q-1] 等)は誤抽出を防ぐため対象外とする。
|
|
48
56
|
CHECKLIST_ID_RE = re.compile(r"\[((?:CR|SR)-[A-Z]+-\d{3,})\]")
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
# パスガード: main() の引数は .claude/reports/ 配下に限定する [SR-V-002]。
|
|
59
|
+
# parents[3] で .claude/ に到達(scripts/ → dev-workflow/ → skills/ → .claude/)。
|
|
60
|
+
# テストでは monkeypatch で差し替え可能。
|
|
61
|
+
ALLOWED_REPORT_DIR = (Path(__file__).resolve().parents[3] / "reports").resolve()
|
|
62
|
+
# 誤配置検出: ALLOWED_REPORT_DIR の親が `.claude` であることを実行時に検証 [SR-NEW]。
|
|
63
|
+
# パストラバーサル防御ではなく、スクリプトが別階層に移動された場合の
|
|
64
|
+
# サイレント破綻を防ぐ。`assert` は `python -O` で無効化されるため
|
|
65
|
+
# RuntimeError で明示的に投げる。
|
|
66
|
+
# record_tier_outcome.py の _CLAUDE_DIR 検証と対称な endswith パターンを使用。
|
|
67
|
+
# `os.sep + ".claude"` は Windows (`\\.claude`)・`"/.claude"` は POSIX 用の固定リテラル。
|
|
68
|
+
# 両方チェックすることで OS 依存しないパス末尾検証になる。
|
|
69
|
+
_parent_str = str(ALLOWED_REPORT_DIR.parent)
|
|
70
|
+
if not (_parent_str.endswith(os.sep + ".claude") or _parent_str.endswith("/.claude")):
|
|
71
|
+
raise RuntimeError(
|
|
72
|
+
f"ALLOWED_REPORT_DIR resolution broke: expected parent to end with '.claude' "
|
|
73
|
+
f"but got {_parent_str!r}. "
|
|
74
|
+
"Check that this file is at .claude/skills/dev-workflow/scripts/."
|
|
75
|
+
)
|
|
58
76
|
|
|
59
77
|
|
|
60
78
|
def extract_checklist_ids(report_text: str) -> list[str]:
|
|
@@ -80,6 +98,29 @@ def _is_old(decided_at_iso: str, days: int = DEFAULT_REEVAL_DAYS) -> bool:
|
|
|
80
98
|
return (now - decided) > timedelta(days=days)
|
|
81
99
|
|
|
82
100
|
|
|
101
|
+
def _sanitize_md(s: str) -> str:
|
|
102
|
+
"""マークダウン構造を崩しうる文字(改行・# / ``` )を空白に置換する。
|
|
103
|
+
|
|
104
|
+
対象文字:
|
|
105
|
+
- ``\\r`` / ``\\n``: 行区切り
|
|
106
|
+
- ``#``: 見出し記号
|
|
107
|
+
- `` ` ``: コードブロック・インラインコード境界
|
|
108
|
+
- ``\\u2028`` (LINE SEPARATOR) / ``\\u2029`` (PARAGRAPH SEPARATOR):
|
|
109
|
+
Python の ``str.splitlines()`` および ECMAScript JSON.parse が行区切りとして扱う Unicode 文字。
|
|
110
|
+
Markdown レンダラー上の実害は軽微だが、record_tier_outcome.py の
|
|
111
|
+
U+2028/U+2029 エスケープとの一貫性のため対象に含める。
|
|
112
|
+
- ``\\x85`` (NEXT LINE / NEL):
|
|
113
|
+
Python の ``str.splitlines()`` が行区切りとして扱う Unicode 文字。
|
|
114
|
+
splitlines() 互換性のため対象に含める。
|
|
115
|
+
|
|
116
|
+
DB 由来フィールドをレポート Markdown に埋め込む際の防御。[SR-NEW] / [CR-Q-001]
|
|
117
|
+
"""
|
|
118
|
+
# NOTE: ソースコード上は escape 表記で記述し、実体文字を埋め込まない。
|
|
119
|
+
# raw string を使わず通常文字列にすることで \u2028 / \u2029 / \x85 を Python が Unicode に解決する。
|
|
120
|
+
# コードポイント昇順: \r(0D) < \n(0A) < \x85(85) < # < ` < \u2028 < \u2029
|
|
121
|
+
return re.sub("[\r\n\x85#`\u2028\u2029]", " ", str(s))
|
|
122
|
+
|
|
123
|
+
|
|
83
124
|
def build_hint_section(
|
|
84
125
|
decisions_by_id: dict[str, list[dict]],
|
|
85
126
|
*,
|
|
@@ -129,8 +170,12 @@ def build_hint_section(
|
|
|
129
170
|
flag = " [要再評価]" if _is_old(latest_decided, reeval_days) else ""
|
|
130
171
|
latest_decision = latest.get("decision", "?")
|
|
131
172
|
latest_reason = latest.get("reason") or "(理由未記載)"
|
|
173
|
+
latest_decided_safe = _sanitize_md(latest_decided)
|
|
174
|
+
latest_decision_safe = _sanitize_md(latest_decision)
|
|
175
|
+
latest_reason_safe = _sanitize_md(latest_reason)
|
|
132
176
|
lines.append(
|
|
133
|
-
f"- 直近: {
|
|
177
|
+
f"- 直近: {latest_decided_safe}({latest_decision_safe}、"
|
|
178
|
+
f"理由: {latest_reason_safe}){flag}"
|
|
134
179
|
)
|
|
135
180
|
lines.append("")
|
|
136
181
|
|
|
@@ -208,7 +253,20 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
208
253
|
report_paths: list[Path] = []
|
|
209
254
|
for a in argv:
|
|
210
255
|
p = Path(a)
|
|
256
|
+
# .claude/reports/ 配下のみを許可する [SR-V-002]。
|
|
257
|
+
# NOTE: Path.resolve() は URL エンコード(%2e%2e 等)を解除しないため、
|
|
258
|
+
# ALLOWED_REPORT_DIR 配下にリテラルディレクトリ名として `%2e%2e` を含む
|
|
259
|
+
# パスを渡すと relative_to() を通過する。実体ファイルが存在しない限り
|
|
260
|
+
# 直後の is_file() が二段目防御として弾くため実害なし(脅威モデル: ローカル CLI)。
|
|
261
|
+
try:
|
|
262
|
+
resolved = p.resolve()
|
|
263
|
+
resolved.relative_to(ALLOWED_REPORT_DIR)
|
|
264
|
+
except (OSError, ValueError):
|
|
265
|
+
print(f"[review_hint_inject] path outside reports/ (skipped): {p}", file=sys.stderr)
|
|
266
|
+
continue
|
|
211
267
|
if not p.is_file():
|
|
268
|
+
# 二段目防御: %2e%2e 等のリテラルが ALLOWED 配下に解決された場合も
|
|
269
|
+
# 実ファイルが存在しなければここで弾かれる
|
|
212
270
|
print(f"[review_hint_inject] not a file (skipped): {p}", file=sys.stderr)
|
|
213
271
|
continue
|
|
214
272
|
report_paths.append(p)
|
{claude_code_conductor-2.11.0 → claude_code_conductor-2.12.0}/.claude/skills/mcp-config/SKILL.md
RENAMED
|
@@ -8,6 +8,11 @@ disable-model-invocation: true
|
|
|
8
8
|
MCP サーバーの追加・一覧・削除を対話形式で行う。
|
|
9
9
|
全ての設定は `.claude/settings.json` のプロジェクトスコープに書き込む。
|
|
10
10
|
|
|
11
|
+
> **注意:** Claude Code にはネイティブの `/mcp` コマンドが別途存在するが、それとは別物。
|
|
12
|
+
> ネイティブ `/mcp add` は `settings.json` 以外のファイル(`~/.claude/mcp.json` 等)に書き込む可能性があるが、
|
|
13
|
+
> 正確な書き込み先は Claude Code のバージョン依存であり未確認(要確認)。
|
|
14
|
+
> C3 プロジェクトでは必ずこの `/mcp-config` スキルを使うこと(設定の分散を防ぐため)。
|
|
15
|
+
|
|
11
16
|
---
|
|
12
17
|
|
|
13
18
|
## Step 1: 操作を選択する
|
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.12.0] - 2026-05-21
|
|
4
|
+
|
|
5
|
+
### タクソノミー棚卸(hooks/ 整理)
|
|
6
|
+
|
|
7
|
+
- **skill-callable CLI ヘルパーの再配置**: `review_hint_inject.py` / `record_review_decision.py` / `record_tier_outcome.py` を `.claude/hooks/` から `.claude/skills/dev-workflow/scripts/` に移動。`hooks/` は Claude Code のイベントフックとそのヘルパーモジュール専用とする
|
|
8
|
+
- **`subagent_log.py` を削除**: PO(Parallel Orchestra、v2.0.0 廃止)時代の残骸。SubagentStart/Stop イベントの開発用ロギングフック
|
|
9
|
+
- **`taxonomy.md` / `decisions.md` 更新**: skills の「オーケストレーション skill」「ユーティリティ skill」2 種類分類を追記、D-010 / D-011 を追加(フック拡張・promoted スキルパス変更)
|
|
10
|
+
- **`record_review_decision.py` に文字数 / バイト数上限を導入**: `MAX_FINDING_LEN=2000` / `MAX_REASON_LEN=2000` / `MAX_FIELD_BYTES=8192` で DB 肥大化を防止
|
|
11
|
+
- **`review_hint_inject.py` に `ALLOWED_REPORT_DIR` パスガード**: `.claude/reports/` 配下のみ許可(パストラバーサル防御)
|
|
12
|
+
|
|
13
|
+
#### Migration(既存利用先環境向け cleanup 手順)
|
|
14
|
+
|
|
15
|
+
`c3 update` は配布物の削除を検出しないため、`pip install -U claude-code-conductor` 後に以下を手動実行してください。
|
|
16
|
+
|
|
17
|
+
**1. 旧 hooks/ 配下の skill-callable スクリプトを削除**
|
|
18
|
+
|
|
19
|
+
POSIX(Linux/macOS):
|
|
20
|
+
```bash
|
|
21
|
+
rm -f .claude/hooks/review_hint_inject.py
|
|
22
|
+
rm -f .claude/hooks/record_review_decision.py
|
|
23
|
+
rm -f .claude/hooks/record_tier_outcome.py
|
|
24
|
+
rm -f .claude/hooks/subagent_log.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
PowerShell(Windows):
|
|
28
|
+
```powershell
|
|
29
|
+
Remove-Item -Force .claude\hooks\review_hint_inject.py
|
|
30
|
+
Remove-Item -Force .claude\hooks\record_review_decision.py
|
|
31
|
+
Remove-Item -Force .claude\hooks\record_tier_outcome.py
|
|
32
|
+
Remove-Item -Force .claude\hooks\subagent_log.py
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**2. `.claude/settings.local.json` から `subagent_log.py` 関連を削除**(個人ファイル)
|
|
36
|
+
|
|
37
|
+
- `permissions.allow` の `"Bash(python .claude/hooks/subagent_log.py*)"` エントリ
|
|
38
|
+
- `hooks.SubagentStart` ブロック全体(subagent_log.py を呼ぶもの)
|
|
39
|
+
- `hooks.SubagentStop` ブロック全体(同上)
|
|
40
|
+
|
|
41
|
+
**3. `.claude/settings.json` の `permissions.allow` を新パスに置換**
|
|
42
|
+
|
|
43
|
+
```diff
|
|
44
|
+
- "Bash(python .claude/hooks/review_hint_inject.py*)",
|
|
45
|
+
- "Bash(python .claude/hooks/record_review_decision.py*)",
|
|
46
|
+
- "Bash(python .claude/hooks/record_tier_outcome.py*)",
|
|
47
|
+
+ "Bash(python .claude/skills/dev-workflow/scripts/review_hint_inject.py*)",
|
|
48
|
+
+ "Bash(python .claude/skills/dev-workflow/scripts/record_review_decision.py*)",
|
|
49
|
+
+ "Bash(python .claude/skills/dev-workflow/scripts/record_tier_outcome.py*)",
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**4. 確認**
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 旧パス参照が残っていないか確認
|
|
56
|
+
grep -r "\.claude/hooks/review_hint_inject\|\.claude/hooks/record_review_decision\|\.claude/hooks/record_tier_outcome\|\.claude/hooks/subagent_log" .claude/ || echo "OK: 旧パス参照なし"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
3
61
|
## [2.11.0] - 2026-05-21
|
|
4
62
|
|
|
5
63
|
### 破壊的変更: summarize-memory 機能の廃止
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-conductor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.12.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
|