fix: Shift+Enter が改行にならず [27;2;13~ と挿入される問題を修正 - #14
Merged
Conversation
Ghostty/xterm 系端末は Shift+Enter を modifyOtherKeys / CSI-u エスケープ (`ESC [27;2;13~`)で送るが、Ink はこれを解釈できず生テキストとして渡すため、 入力欄に `[27;2;13~` がそのまま挿入されていた。 - core/key-sequence.ts に純粋な decodeKeySequence() を追加(parseSgrMouse と同様の 境界パーサ)。modifyOtherKeys(`[27;mod;code~`)と CSI-u(`[code;mod u`)の両形式を 復号し、Enter/Tab/Esc/Backspace/印字文字+Shift/Ctrl/Meta 修飾に正規化する。 - session-list の useInput でマウス解釈の直後に復号し、実キー chord へ変換してから resolveEnter / editText に渡す。これで Shift+Enter が改行として扱われる。 - 単体テスト(key-sequence.spec.ts)と統合テスト(app.test.tsx)を追加。
takecchi
added a commit
that referenced
this pull request
Aug 6, 2026
## 背景 セッションが自分で別ブランチを切って `gh pr create` すると、1 セッションに複数の PR が紐づきます。しかし一覧の PR セルは**セッションブランチ(`codiva/<slug>`)の PR しか出せず**、2 本目以降はどこにも表示されませんでした。 ## 変更内容 ### 検知(`core/pr-detect.ts` 新規・純粋) - `gh pr create` を実行した **tool_use の結果**に出る PR URL を拾う(`sdk-parse` が tool_use id を控えて tool_result と突き合わせる)。 - ログ全体から URL を拾わないのは誤検出を避けるため — `gh pr list` の出力や、`gh pr view` / WebFetch で覗いただけの他人の PR まで「このセッションの PR」になってしまう。 - MCP の `create_pull_request` 系ツールもツール名で拾う。 - 参照の追加・除去は**変化が無ければ同じ配列参照を返す**(`state.json` を無駄に書き直さない / 再描画を増やさない)。 ### 状態・永続化 - `SessionState.extraPrs`(識別のみ・**永続**)と `prCreateToolIds`(結果待ちの対応付け・transient)を追加。 - `state.json` に保存・復元(壊れた要素は 1 件ずつ落とす)。reducer はブランチの PR と重複したら畳む。 ### 表示 | 画面 | 表記 | |---|---| | 一覧 | `✓ #12 +2`(代表 + 残り件数)。PR 列は複数 PR の行があるときだけ 10 → 14 桁に広げる | | 詳細 | `PR 3 件: ✓ #12 · #13 · #14`(複数 PR のときだけ 1 行。1 本ならログの縦幅を 1 行も譲らない) | - **代表はセッションブランチの PR**。`prStatus`(グリフ)を持つ唯一の PR で、クリック / `p` で開く先でもあるため、見えている番号と開く先とグリフの意味を一致させた。 - セッションが自分で作った PR は codiva が追跡・操作しない(別ブランチのものを勝手に ready 化・マージしない)ので**番号のみ・グリフなし**。状態を知らないのに緑や赤で嘘をつかない。 - **`gh` の追加呼び出しはゼロ**(API クォータを増やさない)。 - 描画とクリック当たり判定は必ず同じ列幅(`core/list-hit.ts` の `prCellWidth`)を使う。 - `PrCell` / `prStatusBadge` は `ui/pr-cell.tsx` に共通化(一覧と詳細で同じグリフ・同じ色)。 ### ドキュメント `README.md`(利用者向けの表記説明)/ `docs/ARCHITECTURE.md`(状態フィールド・設計判断)/ `docs/TASKS.md`(Phase 27)/ `CLAUDE.md`(コードの地図)。 ## テスト - `src/core/pr-detect.spec.ts`(新規・テーブルドリブン): コマンド判定 / URL 抽出(GHE ホスト・サブパス・重複・issues リンク)/ 参照同一性 / 上限 / 表示ヘルパ。 - `src/core/sdk-parse.spec.ts`: 実フィクスチャと同じメッセージ形で tool_use → tool_result の対応、`gh pr list` を数えないこと、ブランチ PR と判明したら畳むこと。 - `src/core/persistence.spec.ts`: 往復と壊れた JSON の扱い。 - `src/core/list-hit.spec.ts`: 列幅の切替。 - `tests/app.test.tsx`: 一覧に `✓ #42 +2` が出て `#43` は出ないこと、詳細に全件が並ぶこと、1 本のときは詳細に PR 行を出さないこと。 ### 動作確認 - [x] `npm run lint` / `npm run typecheck` / `npm test`(2,232 件)/ `npm run build` - [ ] 実 TTY + Claude 認証での手動確認(非対話セッションでは実行できないため未実施)。確認観点: 複数 PR を出したセッションの行が `#12 +2` になり、PR セルのクリックで代表の PR が開く / 詳細ビューに全件が並ぶ / 再起動しても `+n` が残る。 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
Shift+Enter で改行しようとすると、入力欄に
[27;2;13~という文字列がそのまま挿入されてしまう問題を修正しました。原因
Ghostty / xterm 系の端末は Shift+Enter を modifyOtherKeys / CSI-u エスケープ(
ESC [27;2;13~)として送信します。Ink のキーパーサはこの形式を解釈できず、ESC を剥がした生テキスト[27;2;13~をuseInputに渡すため、composer にそのまま挿入されていました。変更点
src/core/key-sequence.ts(新規): 純粋なdecodeKeySequence()を追加。parseSgrMouseと同様の境界パーサで、CSI 27 ; mod ; code ~CSI code ; mod uの両方を復号し、Enter / Tab / Esc / Backspace / 印字文字 + Shift / Ctrl / Meta 修飾に正規化します。修飾値は
1 + bitmask(bit0=Shift, bit1=Alt, bit2=Ctrl)。src/ui/session-list.tsx:useInputでマウス解釈の直後にエスケープを復号し、実キー chord に変換してからresolveEnter/editTextに渡すように変更。これで Shift+Enter が改行として扱われます(既存の「末尾バックスラッシュ + Enter」フォールバックも維持)。アーキテクチャ規約に従い、解析ロジックは純粋関数として
core/に配置し、I/O(InkuseInput)は view 側に閉じ込めています。テスト計画
src/core/key-sequence.spec.ts: Shift/Ctrl/Alt+Enter、Tab/Esc/Backspace/印字文字、CSI-u 形式、非該当入力(テキスト・マウスレポート・未終端)をテーブルドリブンで検証tests/app.test.tsx:ESC [27;2;13~を実際に stdin へ流し、改行が入って submit されない → プレーン Enter で 2 行プロンプトが送信される、を end-to-end で検証npm test(365 passed / coverage: key-sequence.ts 100% lines)npm run typecheck/npm run lint(新規エラー・警告なし)