@akiojin/gwt 4.0.0 → 4.1.0

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.
Files changed (161) hide show
  1. package/README.ja.md +0 -1
  2. package/README.md +0 -1
  3. package/dist/claude.d.ts +21 -0
  4. package/dist/claude.d.ts.map +1 -1
  5. package/dist/claude.js +73 -30
  6. package/dist/claude.js.map +1 -1
  7. package/dist/cli/ui/components/common/Select.d.ts +6 -0
  8. package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
  9. package/dist/cli/ui/components/common/Select.js +3 -2
  10. package/dist/cli/ui/components/common/Select.js.map +1 -1
  11. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +6 -0
  12. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
  13. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +3 -2
  14. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
  15. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.d.ts +3 -0
  16. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.d.ts.map +1 -1
  17. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.js +3 -2
  18. package/dist/cli/ui/components/screens/BatchMergeProgressScreen.js.map +1 -1
  19. package/dist/cli/ui/components/screens/BatchMergeResultScreen.d.ts +3 -0
  20. package/dist/cli/ui/components/screens/BatchMergeResultScreen.d.ts.map +1 -1
  21. package/dist/cli/ui/components/screens/BatchMergeResultScreen.js +3 -2
  22. package/dist/cli/ui/components/screens/BatchMergeResultScreen.js.map +1 -1
  23. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +3 -0
  24. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
  25. package/dist/cli/ui/components/screens/BranchCreatorScreen.js +3 -2
  26. package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
  27. package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -0
  28. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  29. package/dist/cli/ui/components/screens/BranchListScreen.js +3 -4
  30. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  31. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts +10 -1
  32. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
  33. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +7 -22
  34. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
  35. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.d.ts +3 -0
  36. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.d.ts.map +1 -1
  37. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.js +3 -2
  38. package/dist/cli/ui/components/screens/EnvironmentProfileScreen.js.map +1 -1
  39. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +15 -0
  40. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
  41. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +3 -2
  42. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
  43. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts +6 -0
  44. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -1
  45. package/dist/cli/ui/components/screens/ModelSelectorScreen.js +3 -2
  46. package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -1
  47. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +6 -0
  48. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts.map +1 -1
  49. package/dist/cli/ui/components/screens/PRCleanupScreen.js +3 -2
  50. package/dist/cli/ui/components/screens/PRCleanupScreen.js.map +1 -1
  51. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +6 -0
  52. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts.map +1 -1
  53. package/dist/cli/ui/components/screens/SessionSelectorScreen.js +3 -2
  54. package/dist/cli/ui/components/screens/SessionSelectorScreen.js.map +1 -1
  55. package/dist/cli/ui/hooks/useAppInput.d.ts +20 -0
  56. package/dist/cli/ui/hooks/useAppInput.d.ts.map +1 -0
  57. package/dist/cli/ui/hooks/useAppInput.js +137 -0
  58. package/dist/cli/ui/hooks/useAppInput.js.map +1 -0
  59. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts +3 -0
  60. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
  61. package/dist/cli/ui/screens/BranchActionSelectorScreen.js +3 -2
  62. package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
  63. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  64. package/dist/cli/ui/utils/branchFormatter.js +0 -2
  65. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  66. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
  67. package/dist/cli/ui/utils/modelOptions.js +11 -18
  68. package/dist/cli/ui/utils/modelOptions.js.map +1 -1
  69. package/dist/client/assets/{index-f5D2XwDh.js → index-v8smkNOL.js} +16 -16
  70. package/dist/client/index.html +1 -1
  71. package/dist/codex.d.ts +33 -1
  72. package/dist/codex.d.ts.map +1 -1
  73. package/dist/codex.js +33 -2
  74. package/dist/codex.js.map +1 -1
  75. package/dist/config/builtin-tools.d.ts +1 -7
  76. package/dist/config/builtin-tools.d.ts.map +1 -1
  77. package/dist/config/builtin-tools.js +1 -20
  78. package/dist/config/builtin-tools.js.map +1 -1
  79. package/dist/config/tools.d.ts.map +1 -1
  80. package/dist/config/tools.js +1 -4
  81. package/dist/config/tools.js.map +1 -1
  82. package/dist/gemini.d.ts +17 -0
  83. package/dist/gemini.d.ts.map +1 -1
  84. package/dist/gemini.js +21 -21
  85. package/dist/gemini.js.map +1 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +0 -5
  88. package/dist/index.js.map +1 -1
  89. package/dist/shared/aiToolConstants.d.ts +1 -1
  90. package/dist/shared/aiToolConstants.d.ts.map +1 -1
  91. package/dist/shared/aiToolConstants.js +1 -1
  92. package/dist/shared/aiToolConstants.js.map +1 -1
  93. package/dist/utils/command.d.ts +10 -0
  94. package/dist/utils/command.d.ts.map +1 -0
  95. package/dist/utils/command.js +25 -0
  96. package/dist/utils/command.js.map +1 -0
  97. package/dist/utils/session/index.d.ts +0 -2
  98. package/dist/utils/session/index.d.ts.map +1 -1
  99. package/dist/utils/session/index.js +0 -3
  100. package/dist/utils/session/index.js.map +1 -1
  101. package/dist/utils/session/parsers/index.d.ts +0 -1
  102. package/dist/utils/session/parsers/index.d.ts.map +1 -1
  103. package/dist/utils/session/parsers/index.js +0 -2
  104. package/dist/utils/session/parsers/index.js.map +1 -1
  105. package/dist/utils/session.d.ts +0 -1
  106. package/dist/utils/session.d.ts.map +1 -1
  107. package/dist/utils/session.js +0 -1
  108. package/dist/utils/session.js.map +1 -1
  109. package/dist/utils/terminal.d.ts +34 -0
  110. package/dist/utils/terminal.d.ts.map +1 -1
  111. package/dist/utils/terminal.js +51 -4
  112. package/dist/utils/terminal.js.map +1 -1
  113. package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -1
  114. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +0 -2
  115. package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -1
  116. package/package.json +1 -1
  117. package/src/claude.ts +92 -34
  118. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +3 -1
  119. package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +10 -8
  120. package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +6 -6
  121. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +1 -1
  122. package/src/cli/ui/components/common/Select.tsx +9 -2
  123. package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +9 -2
  124. package/src/cli/ui/components/screens/BatchMergeProgressScreen.tsx +6 -2
  125. package/src/cli/ui/components/screens/BatchMergeResultScreen.tsx +6 -2
  126. package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +6 -2
  127. package/src/cli/ui/components/screens/BranchListScreen.tsx +6 -4
  128. package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +17 -26
  129. package/src/cli/ui/components/screens/EnvironmentProfileScreen.tsx +6 -2
  130. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +18 -2
  131. package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +9 -2
  132. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +9 -2
  133. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +9 -2
  134. package/src/cli/ui/hooks/useAppInput.ts +171 -0
  135. package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +6 -2
  136. package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +68 -1
  137. package/src/cli/ui/utils/branchFormatter.ts +0 -1
  138. package/src/cli/ui/utils/modelOptions.test.ts +16 -4
  139. package/src/cli/ui/utils/modelOptions.ts +12 -18
  140. package/src/codex.ts +41 -2
  141. package/src/config/builtin-tools.ts +1 -21
  142. package/src/config/tools.ts +1 -5
  143. package/src/gemini.ts +25 -21
  144. package/src/index.ts +0 -6
  145. package/src/shared/aiToolConstants.ts +1 -1
  146. package/src/utils/command.ts +26 -0
  147. package/src/utils/session/index.ts +0 -4
  148. package/src/utils/session/parsers/index.ts +0 -3
  149. package/src/utils/session.ts +0 -1
  150. package/src/utils/terminal.ts +65 -4
  151. package/src/web/client/src/components/branch-detail/BranchInfoCards.tsx +0 -1
  152. package/dist/qwen.d.ts +0 -16
  153. package/dist/qwen.d.ts.map +0 -1
  154. package/dist/qwen.js +0 -202
  155. package/dist/qwen.js.map +0 -1
  156. package/dist/utils/session/parsers/qwen.d.ts +0 -21
  157. package/dist/utils/session/parsers/qwen.d.ts.map +0 -1
  158. package/dist/utils/session/parsers/qwen.js +0 -36
  159. package/dist/utils/session/parsers/qwen.js.map +0 -1
  160. package/src/qwen.ts +0 -273
  161. package/src/utils/session/parsers/qwen.ts +0 -54
@@ -0,0 +1,171 @@
1
+ import { useCallback, useEffect, useRef } from "react";
2
+ import { useInput, type Key } from "ink";
3
+
4
+ const ESCAPE_SEQUENCE_TIMEOUT_MS = 25;
5
+
6
+ type InputHandler = (input: string, key: Key) => void;
7
+ type Options = { isActive?: boolean };
8
+
9
+ type BufferedEvent = { input: string; key: Key };
10
+
11
+ type PendingEscape = {
12
+ escapeEvent: BufferedEvent;
13
+ bufferedEvents: BufferedEvent[];
14
+ timeoutId: ReturnType<typeof setTimeout> | null;
15
+ };
16
+
17
+ const createEmptyKey = (): Key => ({
18
+ upArrow: false,
19
+ downArrow: false,
20
+ leftArrow: false,
21
+ rightArrow: false,
22
+ pageDown: false,
23
+ pageUp: false,
24
+ return: false,
25
+ escape: false,
26
+ ctrl: false,
27
+ shift: false,
28
+ tab: false,
29
+ backspace: false,
30
+ delete: false,
31
+ meta: false,
32
+ });
33
+
34
+ function parseArrowDirection(
35
+ sequence: string,
36
+ ): "up" | "down" | "left" | "right" | null {
37
+ if (!sequence) {
38
+ return null;
39
+ }
40
+
41
+ const first = sequence[0];
42
+ if (first !== "[" && first !== "O") {
43
+ return null;
44
+ }
45
+
46
+ const last = sequence.at(-1);
47
+ if (!last) {
48
+ return null;
49
+ }
50
+
51
+ switch (last.toUpperCase()) {
52
+ case "A":
53
+ return "up";
54
+ case "B":
55
+ return "down";
56
+ case "C":
57
+ return "right";
58
+ case "D":
59
+ return "left";
60
+ default:
61
+ return null;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Ink `useInput` wrapper that buffers an initial Escape press to disambiguate
67
+ * between a real Escape key and a split ANSI escape sequence (e.g., arrow keys
68
+ * in WSL2/Windows terminals).
69
+ *
70
+ * When an Escape is received, waits briefly for subsequent characters. If they
71
+ * form an arrow sequence (e.g., `[A`, `[B`, `OA`), emits a synthetic arrow
72
+ * `Key`. Otherwise, forwards the original Escape keypress.
73
+ *
74
+ * @param inputHandler - Callback invoked for each normalized input event
75
+ * @param options - Optional Ink input options (e.g., `{ isActive: false }`)
76
+ */
77
+ export function useAppInput(
78
+ inputHandler: InputHandler,
79
+ options?: Options,
80
+ ): void {
81
+ const handlerRef = useRef(inputHandler);
82
+ const pendingRef = useRef<PendingEscape | null>(null);
83
+
84
+ useEffect(() => {
85
+ handlerRef.current = inputHandler;
86
+ }, [inputHandler]);
87
+
88
+ const clearPending = useCallback(() => {
89
+ const pending = pendingRef.current;
90
+ if (pending?.timeoutId) {
91
+ clearTimeout(pending.timeoutId);
92
+ }
93
+ pendingRef.current = null;
94
+ }, []);
95
+
96
+ const flushPending = useCallback(() => {
97
+ const pending = pendingRef.current;
98
+ if (!pending) {
99
+ return;
100
+ }
101
+
102
+ clearPending();
103
+ handlerRef.current(pending.escapeEvent.input, pending.escapeEvent.key);
104
+ for (const event of pending.bufferedEvents) {
105
+ handlerRef.current(event.input, event.key);
106
+ }
107
+ }, [clearPending]);
108
+
109
+ useEffect(() => {
110
+ if (options?.isActive === false) {
111
+ clearPending();
112
+ }
113
+ }, [options?.isActive, clearPending]);
114
+
115
+ useEffect(() => clearPending, [clearPending]);
116
+
117
+ useInput((input, key) => {
118
+ const pending = pendingRef.current;
119
+
120
+ if (!pending) {
121
+ if (key.escape) {
122
+ const timeoutId = setTimeout(flushPending, ESCAPE_SEQUENCE_TIMEOUT_MS);
123
+ pendingRef.current = {
124
+ escapeEvent: { input, key },
125
+ bufferedEvents: [],
126
+ timeoutId,
127
+ };
128
+ return;
129
+ }
130
+
131
+ handlerRef.current(input, key);
132
+ return;
133
+ }
134
+
135
+ if (pending.timeoutId) {
136
+ clearTimeout(pending.timeoutId);
137
+ }
138
+
139
+ pending.bufferedEvents.push({ input, key });
140
+
141
+ const [firstEvent] = pending.bufferedEvents;
142
+ const firstInput = firstEvent?.input;
143
+
144
+ if (firstInput && firstInput !== "[" && firstInput !== "O") {
145
+ flushPending();
146
+ return;
147
+ }
148
+
149
+ const sequence = pending.bufferedEvents
150
+ .map((event) => event.input)
151
+ .join("");
152
+ const arrow = parseArrowDirection(sequence);
153
+ if (arrow) {
154
+ clearPending();
155
+ const arrowKey = createEmptyKey();
156
+ if (arrow === "up") {
157
+ arrowKey.upArrow = true;
158
+ } else if (arrow === "down") {
159
+ arrowKey.downArrow = true;
160
+ } else if (arrow === "left") {
161
+ arrowKey.leftArrow = true;
162
+ } else if (arrow === "right") {
163
+ arrowKey.rightArrow = true;
164
+ }
165
+ handlerRef.current("", arrowKey);
166
+ return;
167
+ }
168
+
169
+ pending.timeoutId = setTimeout(flushPending, ESCAPE_SEQUENCE_TIMEOUT_MS);
170
+ }, options);
171
+ }
@@ -1,9 +1,13 @@
1
1
  import React from "react";
2
- import { Box, Text, useInput } from "ink";
2
+ import { Box, Text } from "ink";
3
3
  import { Select, type SelectItem } from "../components/common/Select.js";
4
4
  import { Footer } from "../components/parts/Footer.js";
5
+ import { useAppInput } from "../hooks/useAppInput.js";
5
6
  import type { BranchAction } from "../types.js";
6
7
 
8
+ /**
9
+ * Props for `BranchActionSelectorScreen`.
10
+ */
7
11
  export interface BranchActionSelectorScreenProps {
8
12
  selectedBranch: string;
9
13
  onUseExisting: () => void;
@@ -35,7 +39,7 @@ export function BranchActionSelectorScreen({
35
39
  secondaryLabel,
36
40
  }: BranchActionSelectorScreenProps) {
37
41
  // Handle keyboard input for back navigation
38
- useInput((input, key) => {
42
+ useAppInput((input, key) => {
39
43
  if (key.escape) {
40
44
  onBack();
41
45
  }
@@ -2,7 +2,8 @@
2
2
  * @vitest-environment happy-dom
3
3
  */
4
4
  import { describe, it, expect, beforeEach, vi } from "vitest";
5
- import { render } from "@testing-library/react";
5
+ import { act, render } from "@testing-library/react";
6
+ import { render as inkRender } from "ink-testing-library";
6
7
  import React from "react";
7
8
  import { BranchActionSelectorScreen } from "../BranchActionSelectorScreen.js";
8
9
  import { Window } from "happy-dom";
@@ -149,4 +150,70 @@ describe("BranchActionSelectorScreen", () => {
149
150
  // For now, we verify the component structure and callbacks are set up
150
151
  expect(onCreateNew).not.toHaveBeenCalled();
151
152
  });
153
+
154
+ it("should treat split down-arrow sequence as navigation (WSL2) and not as Escape", () => {
155
+ const onUseExisting = vi.fn();
156
+ const onCreateNew = vi.fn();
157
+ const onBack = vi.fn();
158
+
159
+ const inkApp = inkRender(
160
+ <BranchActionSelectorScreen
161
+ selectedBranch="feature-test"
162
+ onUseExisting={onUseExisting}
163
+ onCreateNew={onCreateNew}
164
+ onBack={onBack}
165
+ />,
166
+ );
167
+
168
+ act(() => {
169
+ inkApp.stdin.write("\u001b");
170
+ inkApp.stdin.write("[");
171
+ inkApp.stdin.write("B");
172
+ });
173
+
174
+ act(() => {
175
+ inkApp.stdin.write("\r");
176
+ });
177
+
178
+ expect(onBack).not.toHaveBeenCalled();
179
+ expect(onCreateNew).toHaveBeenCalledTimes(1);
180
+ expect(onUseExisting).not.toHaveBeenCalled();
181
+
182
+ inkApp.unmount();
183
+ });
184
+
185
+ it("should still handle Escape key as back navigation", () => {
186
+ vi.useFakeTimers();
187
+ let inkApp: ReturnType<typeof inkRender> | undefined;
188
+
189
+ try {
190
+ const onUseExisting = vi.fn();
191
+ const onCreateNew = vi.fn();
192
+ const onBack = vi.fn();
193
+
194
+ inkApp = inkRender(
195
+ <BranchActionSelectorScreen
196
+ selectedBranch="feature-test"
197
+ onUseExisting={onUseExisting}
198
+ onCreateNew={onCreateNew}
199
+ onBack={onBack}
200
+ />,
201
+ );
202
+
203
+ act(() => {
204
+ inkApp.stdin.write("\u001b");
205
+ });
206
+
207
+ act(() => {
208
+ vi.advanceTimersByTime(25);
209
+ });
210
+
211
+ expect(onBack).toHaveBeenCalledTimes(1);
212
+ expect(onCreateNew).not.toHaveBeenCalled();
213
+ expect(onUseExisting).not.toHaveBeenCalled();
214
+ } finally {
215
+ inkApp?.unmount();
216
+ vi.useRealTimers();
217
+ }
218
+ });
152
219
  });
@@ -98,7 +98,6 @@ function mapToolLabel(toolId: string, toolLabel?: string): string {
98
98
  if (toolId === "claude-code") return "Claude";
99
99
  if (toolId === "codex-cli") return "Codex";
100
100
  if (toolId === "gemini-cli") return "Gemini";
101
- if (toolId === "qwen-cli") return "Qwen";
102
101
  if (toolLabel) return toolLabel;
103
102
  return "Custom";
104
103
  }
@@ -23,12 +23,24 @@ describe("modelOptions", () => {
23
23
  expect(unique.size).toBe(ids.length);
24
24
  expect(ids).toEqual([
25
25
  "",
26
- "gpt-5.1-codex",
27
- "gpt-5.2",
26
+ "gpt-5.2-codex",
28
27
  "gpt-5.1-codex-max",
29
28
  "gpt-5.1-codex-mini",
30
- "gpt-5.1",
29
+ "gpt-5.2",
30
+ ]);
31
+ });
32
+
33
+ it("exposes gpt-5.2-codex with xhigh reasoning and high default", () => {
34
+ const codex52 = getModelOptions("codex-cli").find(
35
+ (m) => m.id === "gpt-5.2-codex",
36
+ );
37
+ expect(codex52?.inferenceLevels).toEqual([
38
+ "xhigh",
39
+ "high",
40
+ "medium",
41
+ "low",
31
42
  ]);
43
+ expect(getDefaultInferenceForModel(codex52)).toBe("high");
32
44
  });
33
45
 
34
46
  it("uses medium as default reasoning for codex-max", () => {
@@ -63,6 +75,6 @@ describe("modelOptions", () => {
63
75
  });
64
76
 
65
77
  it("returns no models for unsupported tools", () => {
66
- expect(byId("qwen-cli")).toEqual([]);
78
+ expect(byId("unknown-tool")).toEqual([]);
67
79
  });
68
80
  });
@@ -39,39 +39,33 @@ const MODEL_OPTIONS: Record<string, ModelOption[]> = {
39
39
  defaultInference: "high",
40
40
  },
41
41
  {
42
- id: "gpt-5.1-codex",
43
- label: "gpt-5.1-codex",
44
- description: "Standard Codex model",
45
- inferenceLevels: CODEX_BASE_LEVELS,
46
- defaultInference: "high",
47
- },
48
- {
49
- id: "gpt-5.2",
50
- label: "gpt-5.2",
51
- description: "Latest frontier model with extra high reasoning",
42
+ id: "gpt-5.2-codex",
43
+ label: "gpt-5.2-codex",
44
+ description: "Latest frontier agentic coding model",
52
45
  inferenceLevels: CODEX_MAX_LEVELS,
53
- defaultInference: "medium",
46
+ defaultInference: "high",
54
47
  },
55
48
  {
56
49
  id: "gpt-5.1-codex-max",
57
50
  label: "gpt-5.1-codex-max",
58
- description: "Max performance (xhigh available)",
51
+ description: "Codex-optimized flagship for deep and fast reasoning.",
59
52
  inferenceLevels: CODEX_MAX_LEVELS,
60
53
  defaultInference: "medium",
61
54
  },
62
55
  {
63
56
  id: "gpt-5.1-codex-mini",
64
57
  label: "gpt-5.1-codex-mini",
65
- description: "Lightweight / cost-saving",
58
+ description: "Optimized for codex. Cheaper, faster, but less capable.",
66
59
  inferenceLevels: CODEX_BASE_LEVELS,
67
60
  defaultInference: "medium",
68
61
  },
69
62
  {
70
- id: "gpt-5.1",
71
- label: "gpt-5.1",
72
- description: "General-purpose GPT-5.1",
73
- inferenceLevels: CODEX_BASE_LEVELS,
74
- defaultInference: "high",
63
+ id: "gpt-5.2",
64
+ label: "gpt-5.2",
65
+ description:
66
+ "Latest frontier model with improvements across knowledge, reasoning and coding",
67
+ inferenceLevels: CODEX_MAX_LEVELS,
68
+ defaultInference: "medium",
75
69
  },
76
70
  ],
77
71
  "gemini-cli": [
package/src/codex.ts CHANGED
@@ -2,16 +2,36 @@ import { execa } from "execa";
2
2
  import chalk from "chalk";
3
3
  import { platform } from "os";
4
4
  import { existsSync } from "fs";
5
- import { createChildStdio, getTerminalStreams } from "./utils/terminal.js";
5
+ import {
6
+ createChildStdio,
7
+ getTerminalStreams,
8
+ resetTerminalModes,
9
+ } from "./utils/terminal.js";
6
10
  import { findLatestCodexSession } from "./utils/session.js";
7
11
 
8
12
  const CODEX_CLI_PACKAGE = "@openai/codex@latest";
9
13
 
14
+ /**
15
+ * Reasoning effort levels supported by Codex CLI.
16
+ */
10
17
  export type CodexReasoningEffort = "low" | "medium" | "high" | "xhigh";
11
18
 
12
- export const DEFAULT_CODEX_MODEL = "gpt-5.1-codex";
19
+ /**
20
+ * Default Codex model used when no override is provided.
21
+ */
22
+ export const DEFAULT_CODEX_MODEL = "gpt-5.2-codex";
23
+
24
+ /**
25
+ * Default reasoning effort used when no override is provided.
26
+ */
13
27
  export const DEFAULT_CODEX_REASONING_EFFORT: CodexReasoningEffort = "high";
14
28
 
29
+ /**
30
+ * Builds the default argument list for Codex CLI launch.
31
+ *
32
+ * @param model - Model name to pass via `--model`
33
+ * @param reasoningEffort - Reasoning effort to pass via config
34
+ */
15
35
  export const buildDefaultCodexArgs = (
16
36
  model: string = DEFAULT_CODEX_MODEL,
17
37
  reasoningEffort: CodexReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT,
@@ -37,6 +57,10 @@ export const buildDefaultCodexArgs = (
37
57
  "shell_environment_policy.experimental_use_profile=true",
38
58
  ];
39
59
 
60
+ /**
61
+ * Error wrapper used by `launchCodexCLI` to preserve the original failure
62
+ * while providing a user-friendly message.
63
+ */
40
64
  export class CodexError extends Error {
41
65
  constructor(
42
66
  message: string,
@@ -47,6 +71,16 @@ export class CodexError extends Error {
47
71
  }
48
72
  }
49
73
 
74
+ /**
75
+ * Launches Codex CLI in the given worktree path.
76
+ *
77
+ * This function resets terminal modes before and after the child process and
78
+ * tries to detect a session id after launch (when supported).
79
+ *
80
+ * @param worktreePath - Worktree directory to run Codex CLI in
81
+ * @param options - Launch options (mode/session/model/reasoning/env)
82
+ * @returns Captured session id when available
83
+ */
50
84
  export async function launchCodexCLI(
51
85
  worktreePath: string,
52
86
  options: {
@@ -133,6 +167,7 @@ export async function launchCodexCLI(
133
167
  console.log(chalk.gray(` 📋 Args: ${args.join(" ")}`));
134
168
 
135
169
  terminal.exitRawMode();
170
+ resetTerminalModes(terminal.stdout);
136
171
 
137
172
  const childStdio = createChildStdio();
138
173
 
@@ -235,9 +270,13 @@ export async function launchCodexCLI(
235
270
  throw new CodexError(errorMessage, error);
236
271
  } finally {
237
272
  terminal.exitRawMode();
273
+ resetTerminalModes(terminal.stdout);
238
274
  }
239
275
  }
240
276
 
277
+ /**
278
+ * Checks whether Codex CLI is available via `bunx` in the current environment.
279
+ */
241
280
  export async function isCodexAvailable(): Promise<boolean> {
242
281
  try {
243
282
  await execa("bunx", [CODEX_CLI_PACKAGE, "--help"]);
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ビルトインAIツール定義
3
3
  *
4
- * Claude Code、Codex、Gemini、Qwen の CustomAITool 形式定義
4
+ * Claude Code、Codex、Gemini の CustomAITool 形式定義
5
5
  */
6
6
 
7
7
  import type { CustomAITool } from "../types/tools.js";
@@ -58,25 +58,6 @@ export const GEMINI_CLI_TOOL: CustomAITool = {
58
58
  permissionSkipArgs: ["-y"],
59
59
  };
60
60
 
61
- /**
62
- * Qwen のビルトイン定義
63
- *
64
- * NOTE: 現在は未サポート(選択画面には表示しない)。ID予約のため定義のみ残す。
65
- */
66
- export const QWEN_CLI_TOOL: CustomAITool = {
67
- id: "qwen-cli",
68
- displayName: "Qwen",
69
- type: "bunx",
70
- command: "@qwen-code/qwen-code@latest",
71
- defaultArgs: ["--checkpointing"],
72
- modeArgs: {
73
- normal: [],
74
- continue: [],
75
- resume: [],
76
- },
77
- permissionSkipArgs: ["--yolo"],
78
- };
79
-
80
61
  /**
81
62
  * すべてのビルトインツール
82
63
  */
@@ -84,5 +65,4 @@ export const BUILTIN_TOOLS: CustomAITool[] = [
84
65
  CLAUDE_CODE_TOOL,
85
66
  CODEX_CLI_TOOL,
86
67
  GEMINI_CLI_TOOL,
87
- QWEN_CLI_TOOL,
88
68
  ];
@@ -299,17 +299,13 @@ export async function getToolById(
299
299
  export async function getAllTools(): Promise<AIToolConfig[]> {
300
300
  const config = await loadToolsConfig();
301
301
 
302
- // Builtin tools that are reserved but not exposed in selectors.
303
- // These IDs remain blocked from customTools to avoid ambiguity.
304
- const UNSUPPORTED_BUILTIN_TOOL_IDS = new Set<string>(["qwen-cli"]);
305
-
306
302
  // ビルトインツールをAIToolConfig形式に変換
307
303
  const builtinConfigs: AIToolConfig[] = BUILTIN_TOOLS.map((tool) => ({
308
304
  id: tool.id,
309
305
  displayName: tool.displayName,
310
306
  ...(tool.icon ? { icon: tool.icon } : {}),
311
307
  isBuiltin: true,
312
- })).filter((tool) => !UNSUPPORTED_BUILTIN_TOOL_IDS.has(tool.id));
308
+ }));
313
309
 
314
310
  // カスタムツールをAIToolConfig形式に変換
315
311
  const customConfigs: AIToolConfig[] = config.customTools.map((tool) => ({
package/src/gemini.ts CHANGED
@@ -1,11 +1,20 @@
1
1
  import { execa } from "execa";
2
2
  import chalk from "chalk";
3
3
  import { existsSync } from "fs";
4
- import { createChildStdio, getTerminalStreams } from "./utils/terminal.js";
4
+ import {
5
+ createChildStdio,
6
+ getTerminalStreams,
7
+ resetTerminalModes,
8
+ } from "./utils/terminal.js";
9
+ import { isCommandAvailable } from "./utils/command.js";
5
10
  import { findLatestGeminiSessionId } from "./utils/session.js";
6
11
 
7
12
  const GEMINI_CLI_PACKAGE = "@google/gemini-cli@latest";
8
13
 
14
+ /**
15
+ * Error wrapper used by `launchGeminiCLI` to preserve the original failure
16
+ * while providing a user-friendly message.
17
+ */
9
18
  export class GeminiError extends Error {
10
19
  constructor(
11
20
  message: string,
@@ -16,6 +25,16 @@ export class GeminiError extends Error {
16
25
  }
17
26
  }
18
27
 
28
+ /**
29
+ * Launches Gemini CLI in the given worktree path.
30
+ *
31
+ * This function resets terminal modes before and after the child process and
32
+ * supports continue/resume modes when a session id is available.
33
+ *
34
+ * @param worktreePath - Worktree directory to run Gemini CLI in
35
+ * @param options - Launch options (mode/session/model/permissions/env)
36
+ * @returns Captured session id when available
37
+ */
19
38
  export async function launchGeminiCLI(
20
39
  worktreePath: string,
21
40
  options: {
@@ -119,6 +138,7 @@ export async function launchGeminiCLI(
119
138
  );
120
139
  }
121
140
  terminal.exitRawMode();
141
+ resetTerminalModes(terminal.stdout);
122
142
 
123
143
  const baseEnv = Object.fromEntries(
124
144
  Object.entries({
@@ -132,7 +152,7 @@ export async function launchGeminiCLI(
132
152
  const childStdio = createChildStdio();
133
153
 
134
154
  // Auto-detect locally installed gemini command
135
- const hasLocalGemini = await isGeminiCommandAvailable();
155
+ const hasLocalGemini = await isCommandAvailable("gemini");
136
156
 
137
157
  // Preserve TTY for interactive UI (colors/width) by inheriting stdout/stderr.
138
158
  // Session ID is determined via file-based detection after exit.
@@ -243,7 +263,7 @@ export async function launchGeminiCLI(
243
263
 
244
264
  return capturedSessionId ? { sessionId: capturedSessionId } : {};
245
265
  } catch (error: unknown) {
246
- const hasLocalGemini = await isGeminiCommandAvailable();
266
+ const hasLocalGemini = await isCommandAvailable("gemini");
247
267
  let errorMessage: string;
248
268
  const err = error as NodeJS.ErrnoException;
249
269
 
@@ -291,29 +311,13 @@ export async function launchGeminiCLI(
291
311
  throw new GeminiError(errorMessage, error);
292
312
  } finally {
293
313
  terminal.exitRawMode();
314
+ resetTerminalModes(terminal.stdout);
294
315
  }
295
316
  }
296
317
 
297
318
  /**
298
- * Check if locally installed `gemini` command is available
299
- * @returns true if `gemini` command exists in PATH, false otherwise
319
+ * Checks whether Gemini CLI is available via `bunx` in the current environment.
300
320
  */
301
- async function isGeminiCommandAvailable(): Promise<boolean> {
302
- try {
303
- const command = process.platform === "win32" ? "where" : "which";
304
- await execa(command, ["gemini"], {
305
- shell: true,
306
- stdin: "ignore",
307
- stdout: "ignore",
308
- stderr: "ignore",
309
- });
310
- return true;
311
- } catch {
312
- // gemini command not found in PATH
313
- return false;
314
- }
315
- }
316
-
317
321
  export async function isGeminiCLIAvailable(): Promise<boolean> {
318
322
  try {
319
323
  await execa("bunx", [GEMINI_CLI_PACKAGE, "--version"], { shell: true });
package/src/index.ts CHANGED
@@ -307,12 +307,6 @@ export async function handleAIToolWorkflow(
307
307
  `Selected: ${branchLabel} with ${tool} (${mode} mode${modelInfo}, skipPermissions: ${skipPermissions})`,
308
308
  );
309
309
 
310
- if (tool === "qwen-cli") {
311
- printError("Qwen CLI is currently unsupported.");
312
- await waitForErrorAcknowledgement();
313
- return;
314
- }
315
-
316
310
  try {
317
311
  // Get repository root
318
312
  const repoRootResult = await runGitStep("retrieve repository root", () =>
@@ -12,7 +12,7 @@ export const CLAUDE_PERMISSION_SKIP_ARGS = [
12
12
  export const CODEX_DEFAULT_ARGS = [
13
13
  "--enable",
14
14
  "web_search_request",
15
- "--model=gpt-5-codex",
15
+ "--model=gpt-5.2-codex",
16
16
  "--sandbox",
17
17
  "workspace-write",
18
18
  "-c",
@@ -0,0 +1,26 @@
1
+ import { execa } from "execa";
2
+
3
+ /**
4
+ * Checks whether a command is available in the current PATH.
5
+ *
6
+ * Uses `where` on Windows and `which` on other platforms.
7
+ *
8
+ * @param commandName - Command name to look up (e.g. `claude`, `npx`, `gemini`)
9
+ * @returns true if the command exists in PATH
10
+ */
11
+ export async function isCommandAvailable(
12
+ commandName: string,
13
+ ): Promise<boolean> {
14
+ try {
15
+ const command = process.platform === "win32" ? "where" : "which";
16
+ await execa(command, [commandName], {
17
+ shell: true,
18
+ stdin: "ignore",
19
+ stdout: "ignore",
20
+ stderr: "ignore",
21
+ });
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
@@ -5,7 +5,6 @@
5
5
  * - Claude Code
6
6
  * - Codex CLI
7
7
  * - Gemini CLI
8
- * - Qwen CLI
9
8
  */
10
9
 
11
10
  // Type exports
@@ -41,6 +40,3 @@ export {
41
40
  findLatestGeminiSession,
42
41
  findLatestGeminiSessionId,
43
42
  } from "./parsers/gemini.js";
44
-
45
- // Qwen CLI parser
46
- export { findLatestQwenSessionId } from "./parsers/qwen.js";
@@ -23,6 +23,3 @@ export {
23
23
  findLatestGeminiSession,
24
24
  findLatestGeminiSessionId,
25
25
  } from "./gemini.js";
26
-
27
- // Qwen CLI
28
- export { findLatestQwenSessionId } from "./qwen.js";