Skip to content

feat: 詳細ビューの Ctrl+C で実行中のターンを中断できるようにする - #89

Merged
takecchi merged 1 commit into
mainfrom
codiva/control-c-claude-code
Aug 4, 2026
Merged

feat: 詳細ビューの Ctrl+C で実行中のターンを中断できるようにする#89
takecchi merged 1 commit into
mainfrom
codiva/control-c-claude-code

Conversation

@takecchi

@takecchi takecchi commented Aug 4, 2026

Copy link
Copy Markdown
Owner

概要

セッション詳細ビューで Ctrl+C を押すと、そのセッションが実行中のターンを中断できるようにしました(現行の Claude Code の Ctrl+C と同じ操作感)。中断されたセッションは失敗ではなく「中断」として残るので、そのまま Ctrl+R / 追加指示で同じ会話の続きから再開できます。

これまで走っているセッションを止める手段は破棄(d)だけで、「やめたいのはターンだけ、worktree と会話は残したい」に応える出口がありませんでした。Session.interrupt() は元からありましたが UI から呼ばれておらず、しかも素直に呼ぶと failed に落ちて再開できないという問題がありました(CLI は打ち切ったターンを is_error: true の result で閉じるため)。

変更点

レイヤ 変更
core/status-meta.ts STATUS_METAinterruptible を追加(running / awaiting_permission / awaiting_input)+ isInterruptible()awaiting_* は「動いていないが中断できる」ので active とは別フラグ
core/session.ts interrupt() を「ターンだけをやめる」操作として整備。SDK の応答を待たず先に interrupted を確定 → Query.interrupt() は best-effort(reject を握り潰す)
core/sdk-parse.ts terminal_reason: 'aborted_streaming'(= 中断された result)を interrupted(resumable) に分類。文言ではなく構造で判定し、ログには USER_INTERRUPT_DETAIL を書く(CLI の [ede_diagnostic] … は出さない)
core/session-manager.ts interrupt(id): Promise<boolean> がストアの現在値で対象を判定(連打の吸収は resume() と同じ理由で core 側)
ui/session-detail.tsx Ctrl+Cpending ガードより前で処理(許可/質問ダイアログ表示中も中断できる)。案内は detail.cancelHint(実行中)⇄ resume.oneKeyHint(中断後)で同じ 1 行を入れ替え
core/i18n.ts detail.cancelHint(ja / en 対)

設計上のポイント

  • 状態を先に確定させる理由は2つ: (a) interrupt は control request なので CLI の応答を待つと押しても数百 ms 反応がない、(b) CLI の打ち切り result は is_error: true なので診断が無いと failed になる。先に interrupted を立てておけば、result 側は既存のロールアップガード(isResumable)でコストだけを拾う。
  • sdk-parse 側の aborted_streaming 判定は保険。中断のあとに assistant メッセージが挟まって status が running に戻っても failed にならない。2 経路とも同じ文言を使うので toInterrupted の重複畳み込みが効き、ログは 1 行だけ。
  • 許可待ちでも中断できる。ダイアログの n(拒否)は「そのツール 1 回を断る」だけなので、作業自体をやめる出口が他にない。toInterruptedpendingPermission を落とすことで、既存の commit() 経路が canUseTool の promise を deny で閉じる(未応答の tool_use で終わる transcript は後の resume を壊すため)。
  • 一覧ビューには入れていません。フォーカス横断の Ctrl+C にすると、選択行を取り違えたときに別の走っているセッションを止めてしまうため、中断は詳細ビュー限定の操作にしました。

テスト計画

  • core/status-meta.spec.ts: isInterruptible の全ステータス表
  • core/session.spec.ts: 中断で interrupted になる / aborted_streaming の result で failed に落ちない・ログが 1 行 / 終端状態では no-op / 保留中の許可を deny で閉じる
  • core/session-manager.spec.ts: 対象(running・awaiting_*)と非対象(creating・completed・interrupted・failed・archived)の表 + 未知 id
  • core/sdk-parse.spec.ts: 実採取フィクスチャ(session-interrupt.jsonl)が interrupted になる(従来は failed
  • tests/app.test.tsx: Ctrl+Cinterrupted + 案内が再開へ入れ替わる / 許可ダイアログ表示中の中断(deny も飛ぶ)
  • lint / typecheck / test(カバレッジ閾値込み)/ build すべて緑
  • 実機での体感確認(TTY + Claude 認証が必要なのでユーザーにお願いします): 走っているセッションの詳細で Ctrl+C → 「中断」バッジ + Ctrl+R で続きから再開できること、質問ダイアログ中の Ctrl+C

ドキュメント

README.md(「実行中の作業を中断する(Ctrl+C)」節)/ docs/ARCHITECTURE.md(状態機械 + 「ユーザーによる中断」節)/ docs/TECH_NOTES.md(interrupt の実測フィールド)/ docs/TASKS.md(Phase 24)/ .claude/rules/session-domain.md / .claude/rules/ink-components.md / .claude/skills/add-session-status/SKILL.md(7 フィールド化)

🤖 Generated with Claude Code

走り出したセッションを止める手段が破棄(d)しかなく、「やめたいのはターンだけで
worktree と会話は残したい」(Claude Code の Ctrl+C と同じ期待)に応えられていなかった。
Session.interrupt() は以前からあったが UI から呼ばれておらず、素直に呼ぶと CLI の
打ち切り result(is_error: true)が failed に分類されて再開できなくなっていた。

- STATUS_META に interruptible を追加(running / awaiting_* = ターンが生きている区間。
  awaiting_* は「動いていないが中断できる」ので active とは別フラグ)
- Session.interrupt(): SDK の応答を待たず先に interrupted を確定(体感 + 分類)。
  Query.interrupt() は best-effort。許可待ちで押された場合は既存の commit() 経路が
  canUseTool を deny で閉じる(未応答の tool_use は後の resume を壊す)
- sdk-parse: terminal_reason='aborted_streaming' を interrupted に分類(実測フィクスチャ
  準拠。文言ではなく構造で判定し、CLI の内部診断はログに出さない)
- SessionManager.interrupt(id) はストアの現在値で判定して連打を吸収(resume と同じ)
- 詳細ビューは pending ガードより前で Ctrl+C を処理(許可/質問ダイアログ中も中断できる)。
  案内は detail.cancelHint(実行中)⇄ resume.oneKeyHint(中断後)で入れ替え
@takecchi
takecchi merged commit e17361e into main Aug 4, 2026
1 check passed
@takecchi
takecchi deleted the codiva/control-c-claude-code branch August 4, 2026 11:18
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