Skip to content

feat(claude): claude-interactive モードで対話的 Claude Code 経路に対応 - #1

Draft
owayo wants to merge 3 commits into
mainfrom
feature/claude-interactive
Draft

owayo wants to merge 3 commits into
mainfrom
feature/claude-interactive

Conversation

@owayo

@owayo owayo commented May 18, 2026

Copy link
Copy Markdown
Owner

Summary

2026-06-15 以降、Anthropic は claude -p / Agent SDK / GitHub Actions を Agent SDK 専用月次クレジット(Pro $20/月、Max 5x $100/月、Max 20x $200/月)に分離します。token-burn は「プラン使用枠を使い切る」のが目的なので、claude -p のままだと月 $20 相当しか使えなくなり目的が成立しなくなります。プラン枠を消費する 対話モード経路claude "prompt")への移行を実装しました。

方針: このPRはdraftのまま 2026-06-15 を待ってから main にマージします

機能

1. AgentMode enum

auto / generic / claude-print / claude-interactive、デフォルト auto。command 内容から自動判定。

2. claude-interactive モード

  • claude "prompt" を tmux 実 TTY で起動
  • --settings <task-settings.json> で公式 Stop / StopFailure hooks を注入
  • token-burn claude-hook が outcome JSON を書き出し、token-burn classify-claude-outcome が分類
  • tmux pipe-pane でログ取得、outcome 監視 watcher が C-d で claude を閉じる

3. claude-print モード(既存経路の保持)

従来の claude -p 経路を opt-in で保持(段階的移行)。

4. claude_settings: user の Claude settings を統合

公式 --settings の複数指定挙動は非明記なので、token-burn が必ず 1 個だけ渡す方針に統一。[[agents]].claude_settings に user 設定を集約し、token-burn の Stop / StopFailure hooks を prepend で deep merge して 1 ファイル化:

claude_settings = [
    { file = "~/.config/claude/plugin-settings.json" },
    { command = ["bash", "-lc", "~/bin/claude-plugin-settings.sh"] },
    { inline = { enabledPlugins = { "my-plugin@org" = true } } },
]
  • 動的判定(cwd 依存等)は command source で実現
  • file / command / inline ソースは valid な JSON object を返す必要あり
  • hooks.Stop / hooks.StopFailure 配列は token-burn の hook が user hooks の 先頭 に prepend されるため、user hooks の decision: "block" は保持される

5. validate 強化

  • claude-interactive + -p/--print/--output-format 等の print-only フラグ → reject
  • claude 経路で command 内の --settings / --settings=... 直書き → reject(claude_settings に集約)
  • generic モードで claude_settings 非空 → reject

あるべき姿(codex 相談済み)

設計判断は codex に 2 回相談:

  1. Plan D 採用: tmux send-keys / PTY 制御 / claude resume より、公式 Stop / StopFailure hook が堅牢
  2. claude_settings source enum 採用: user の wrapper script --settings $PLUGIN_SETTINGS と token-burn の --settings <path> が衝突する問題に対し、token-burn が user 設定を取り込んで 1 個に統合するのが正しい責務分担

詳細は memory/project_claude_p_migration.md 参照(auto memory)。

注意・制限事項

  • claude-interactive モードでは stream-json の rate_limit_event が使えないため、settings.rate_limit_threshold による 95% 自動停止は機能しない。StopFailure(error=rate_limit) を受けたタスクのみが failed 扱いになる
  • Anthropic 側のポリシー変更で対話モード経路もいずれ制限される可能性あり(README/AGENTS.md に明記)
  • --settings 指定時に user/project settings.json の hooks がマージされるか上書きされるかは公式ドキュメント非明記(claude_settings で user 設定を明示的に統合する想定で設計)

Test plan

  • cargo test 451 件 pass(既存 407 + 新規 44)
  • cargo fmt --check clean
  • cargo clippy --all-targets -- -D warnings warning ゼロ
  • hook simulator: Stop / StopFailure(rate_limit/server_error/billing_error) の各 outcome JSON を CLI 経由で生成・分類し、exit code が期待通りであることを確認
  • validate 動作確認: claude-interactive + -p 設定で実 binary が reject すること、-p なし設定で --verbose などのフラグが自動付与されないこと、--dry-run で plan が表示されること
  • 実機検証 (要マニュアル): 小さな claude プロンプトを mode = "claude-interactive" で 1 件流し、outcome JSON が Stop で書かれることを確認
  • 実機検証: 意図的にレート制限を発生させ、StopFailure(error=rate_limit)failed-N マーカーが作られることを確認
  • 実機検証: claude_settings = [{ command = [...] }] で wrapper の plugin 設定が merged settings 経由で適用され、token-burn の Stop hook も発火することを確認
  • 実機検証: hook が発火しなかった場合に failed-N + hook did not fire エラーが記録されることを確認
  • 既存 mode = "claude-print" 経路で従来通り動くことを回帰確認

Migration

  • 既存 config で claude -p ... を使っている場合、デフォルトの mode = "auto" のままで claude-print として自動判定される。動作は変わらない
  • 6/15 以降のプラン枠消費を維持したい場合: command から -p / --output-format 等の print-only フラグを除外し、mode = "claude-interactive" を明示
  • wrapper script で claude --settings "$JSON" を渡している場合: wrapper 内の --settings を削除し、生成ロジックを claude_settings = [{ command = ["bash", "-lc", "..."] }] に移植
  • token-burn init の新規生成では mode = "claude-interactive" がデフォルト

Commits

  • cf39aa0: feat(claude): claude-interactive モードで対話的 Claude Code 経路に対応
  • 6794a21: feat(claude): claude_settings で user の Claude settings を統合

owayo and others added 3 commits May 18, 2026 22:12
2026-06-15 以降、claude -p / Agent SDK / GitHub Actions は Agent SDK 専用月次クレジット
(Pro \$20 / Max 5x \$100 / Max 20x \$200)に分離され、token-burn の「プラン枠を使い切る」目的が
成立しなくなる。プラン枠を消費する対話モード経路(claude "prompt" 起動)を新たに実装し、Stop /
StopFailure hooks 経由で outcome を分類するパイプラインを追加。既存の claude -p 経路は
claude-print モードとして保持し、段階的移行を可能にする。

- AgentMode enum (Auto / Generic / ClaudePrint / ClaudeInteractive) を config.rs に追加。
  resolved_mode() で Auto を command 内容から具体モードに解決
- validate_agent_mode で claude-interactive + print-only フラグ (-p, --print, --output-format,
  --input-format, --include-partial-messages 等) の組み合わせを拒否
- ensure_required_flags を mode で分岐: claude-print のみ stream-json flags を強制付与し、
  claude-interactive では一切付与しない
- build_task_script を 3 経路に分割: append_claude_print_body / append_claude_interactive_body /
  append_generic_body
- claude-interactive 経路: tmux pipe-pane でログ取得、--settings <task-settings.json> で hooks
  注入、outcome JSON 監視 watcher が tmux send-keys C-d で claude セッションを閉じる
- 新規 src/claude_hook.rs: stdin の hook JSON を outcome ファイルにアトミック書き出し
- classify-claude-outcome サブコマンドで outcome JSON を ResultClass に分類。StopFailure.error
  の rate_limit / server_error / billing_error / unknown 等を公式仕様に従って分類
- init.rs の DEFAULT_CONFIG を mode = "claude-interactive" に変更(既存の -p 経路は example
  コメントとして残置)
- README.md / README.ja.md / AGENTS.md に 2026-06-15 制限の背景・モード仕様・ポリシー変更リスクを
  追記

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude --settings は複数指定時の挙動が公式非明記のため、token-burn 側で必ず 1 個だけ渡す方針に統一。
user 設定(plugin 有効/無効、permission rules、独自 hooks など)は [[agents]].claude_settings に
集約し、token-burn の Stop / StopFailure hooks を prepend で deep merge して 1 ファイル化する。

これにより wrapper script で --settings "\$PLUGIN_SETTINGS" を渡していたユーザーは、claude_settings
の command source に移植することで動的判定を保ったまま token-burn の hooks も注入できる。

主な変更:
- ClaudeSettingsSource enum (untagged, deny_unknown_fields): File / Command / Inline の 3 経路
- Agent.claude_settings: Vec<ClaudeSettingsSource> を追加
- validate: claude 経路で command 内の --settings / --settings= 直書きを拒否、generic で
  claude_settings を空でないと拒否、claude-interactive で print-only フラグを拒否
- build_claude_interactive_settings: sources を順に resolve → deep_merge → prepend_hook
  (Stop / StopFailure) → serde_json::to_string_pretty で書き出し
- resolve_settings_source: File は read + parse、Command は std::process::Command 経由で
  stdout を JSON object として parse、Inline は toml::Value → serde_json::Value 変換
- deep_merge: object 同士は再帰 merge、それ以外は RHS 完全置換
- prepend_hook: hooks.{Stop,StopFailure} 配列の先頭に token-burn hook entry を挿入。
  hooks ノードが無ければ作成
- 25 個のテストを追加: source 解決、deep_merge、prepend_hook、build_claude_interactive_settings
  の merge 動作、validate の各拒否パスを網羅
- init.rs の DEFAULT_CONFIG / AGENTS.md に claude_settings の 3 経路と wrapper 移行ガイドを追記

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
実際の Claude Code stream-json を確認し、task_updated.patch.is_backgrounded が通知されてもモニターに表示されない問題を修正した。

task_updated.patch.status="killed" は異常終了として failed/cancelled と同じ警告表示にする。

実データ由来の backgrounded/killed ケースをテストに追加し、README と AGENTS の対応イベント説明を更新した。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant