@akiojin/gwt 3.1.1 → 4.0.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.
- package/README.ja.md +4 -4
- package/README.md +4 -4
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +8 -8
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +26 -11
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js +0 -5
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -1
- package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
- package/dist/cli/ui/utils/modelOptions.js +25 -16
- package/dist/cli/ui/utils/modelOptions.js.map +1 -1
- package/dist/config/builtin-tools.d.ts +2 -0
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +2 -0
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/config/tools.d.ts.map +1 -1
- package/dist/config/tools.js +4 -1
- package/dist/config/tools.js.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +22 -40
- package/dist/gemini.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -21
- package/dist/index.js.map +1 -1
- package/dist/utils/session/common.d.ts +100 -0
- package/dist/utils/session/common.d.ts.map +1 -0
- package/dist/utils/session/common.js +417 -0
- package/dist/utils/session/common.js.map +1 -0
- package/dist/utils/session/index.d.ts +16 -0
- package/dist/utils/session/index.d.ts.map +1 -0
- package/dist/utils/session/index.js +20 -0
- package/dist/utils/session/index.js.map +1 -0
- package/dist/utils/session/parsers/claude.d.ts +56 -0
- package/dist/utils/session/parsers/claude.d.ts.map +1 -0
- package/dist/utils/session/parsers/claude.js +178 -0
- package/dist/utils/session/parsers/claude.js.map +1 -0
- package/dist/utils/session/parsers/codex.d.ts +37 -0
- package/dist/utils/session/parsers/codex.d.ts.map +1 -0
- package/dist/utils/session/parsers/codex.js +113 -0
- package/dist/utils/session/parsers/codex.js.map +1 -0
- package/dist/utils/session/parsers/gemini.d.ts +22 -0
- package/dist/utils/session/parsers/gemini.d.ts.map +1 -0
- package/dist/utils/session/parsers/gemini.js +81 -0
- package/dist/utils/session/parsers/gemini.js.map +1 -0
- package/dist/utils/session/parsers/index.d.ts +8 -0
- package/dist/utils/session/parsers/index.d.ts.map +1 -0
- package/dist/utils/session/parsers/index.js +12 -0
- package/dist/utils/session/parsers/index.js.map +1 -0
- package/dist/utils/session/parsers/qwen.d.ts +21 -0
- package/dist/utils/session/parsers/qwen.d.ts.map +1 -0
- package/dist/utils/session/parsers/qwen.js +36 -0
- package/dist/utils/session/parsers/qwen.js.map +1 -0
- package/dist/utils/session/types.d.ts +38 -0
- package/dist/utils/session/types.d.ts.map +1 -0
- package/dist/utils/session/types.js +5 -0
- package/dist/utils/session/types.js.map +1 -0
- package/dist/utils/session.d.ts +14 -79
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +14 -585
- package/dist/utils/session.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/ui/components/App.tsx +10 -8
- package/src/cli/ui/components/screens/BranchListScreen.tsx +1 -1
- package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +40 -22
- package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +0 -8
- package/src/cli/ui/utils/modelOptions.test.ts +9 -6
- package/src/cli/ui/utils/modelOptions.ts +25 -18
- package/src/config/builtin-tools.ts +2 -0
- package/src/config/tools.ts +5 -1
- package/src/gemini.ts +22 -47
- package/src/index.ts +7 -27
- package/src/utils/session/common.ts +446 -0
- package/src/utils/session/index.ts +46 -0
- package/src/utils/session/parsers/claude.ts +233 -0
- package/src/utils/session/parsers/codex.ts +135 -0
- package/src/utils/session/parsers/gemini.ts +94 -0
- package/src/utils/session/parsers/index.ts +28 -0
- package/src/utils/session/parsers/qwen.ts +54 -0
- package/src/utils/session/types.ts +42 -0
- package/src/utils/session.ts +14 -755
|
@@ -327,7 +327,7 @@ export function BranchListScreen({
|
|
|
327
327
|
case "gemini-cli":
|
|
328
328
|
return chalk.magenta(label);
|
|
329
329
|
case "qwen-cli":
|
|
330
|
-
return chalk.
|
|
330
|
+
return chalk.gray(label);
|
|
331
331
|
default: {
|
|
332
332
|
const trimmed = label.trim().toLowerCase();
|
|
333
333
|
if (!toolId || trimmed === "unknown") {
|
|
@@ -32,6 +32,10 @@ const formatSkip = (skip?: boolean | null) =>
|
|
|
32
32
|
|
|
33
33
|
const supportsReasoning = (toolId?: string | null) => toolId === "codex-cli";
|
|
34
34
|
|
|
35
|
+
const UNSUPPORTED_TOOL_ID = "qwen-cli";
|
|
36
|
+
const UNSUPPORTED_TOOL_MESSAGE = "Unsupported tool (Qwen CLI). ";
|
|
37
|
+
const UNSUPPORTED_TOOL_CATEGORY_LABEL = "Qwen (unsupported)";
|
|
38
|
+
|
|
35
39
|
const describe = (opt: BranchQuickStartOption, includeSessionId = true) => {
|
|
36
40
|
const parts = [`Model: ${opt.model ?? "default"}`];
|
|
37
41
|
if (supportsReasoning(opt.toolId)) {
|
|
@@ -111,6 +115,7 @@ export function BranchQuickStartScreen({
|
|
|
111
115
|
const flat: QuickStartItem[] = [];
|
|
112
116
|
sorted.forEach((opt, idx) => {
|
|
113
117
|
const cat = resolveCategory(opt.toolId);
|
|
118
|
+
const isUnsupportedTool = opt.toolId === UNSUPPORTED_TOOL_ID;
|
|
114
119
|
const prevCat =
|
|
115
120
|
idx > 0 ? resolveCategory(sorted[idx - 1]?.toolId).label : null;
|
|
116
121
|
const isNewCategory = prevCat !== cat.label;
|
|
@@ -121,7 +126,10 @@ export function BranchQuickStartScreen({
|
|
|
121
126
|
value: `reuse-continue:${opt.toolId ?? "unknown"}:${idx}`,
|
|
122
127
|
action: "reuse-continue",
|
|
123
128
|
toolId: opt.toolId ?? null,
|
|
124
|
-
description:
|
|
129
|
+
description: isUnsupportedTool
|
|
130
|
+
? `${UNSUPPORTED_TOOL_MESSAGE}${describe(opt, true)}`
|
|
131
|
+
: describe(opt, true),
|
|
132
|
+
...(isUnsupportedTool ? { disabled: true } : {}),
|
|
125
133
|
groupStart: isNewCategory && flat.length > 0,
|
|
126
134
|
category: cat.label,
|
|
127
135
|
categoryColor: cat.color,
|
|
@@ -131,7 +139,10 @@ export function BranchQuickStartScreen({
|
|
|
131
139
|
value: `reuse-new:${opt.toolId ?? "unknown"}:${idx}`,
|
|
132
140
|
action: "reuse-new",
|
|
133
141
|
toolId: opt.toolId ?? null,
|
|
134
|
-
description:
|
|
142
|
+
description: isUnsupportedTool
|
|
143
|
+
? `${UNSUPPORTED_TOOL_MESSAGE}${describe(opt, false)}`
|
|
144
|
+
: describe(opt, false),
|
|
145
|
+
...(isUnsupportedTool ? { disabled: true } : {}),
|
|
135
146
|
groupStart: false,
|
|
136
147
|
category: cat.label,
|
|
137
148
|
categoryColor: cat.color,
|
|
@@ -197,27 +208,34 @@ export function BranchQuickStartScreen({
|
|
|
197
208
|
if (item.disabled) return;
|
|
198
209
|
onSelect(item.action, item.toolId ?? null);
|
|
199
210
|
}}
|
|
200
|
-
renderItem={(item: QuickStartItem, isSelected) =>
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
renderItem={(item: QuickStartItem, isSelected) => {
|
|
212
|
+
const categoryLabel =
|
|
213
|
+
item.toolId === UNSUPPORTED_TOOL_ID
|
|
214
|
+
? UNSUPPORTED_TOOL_CATEGORY_LABEL
|
|
215
|
+
: item.category;
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<Box
|
|
219
|
+
flexDirection="column"
|
|
220
|
+
marginTop={
|
|
221
|
+
item.groupStart ? 1 : item.category === "Other" ? 1 : 0
|
|
222
|
+
}
|
|
223
|
+
>
|
|
224
|
+
<Text>
|
|
225
|
+
<Text color={item.categoryColor} inverse={isSelected}>
|
|
226
|
+
{`[${categoryLabel}] `}
|
|
227
|
+
</Text>
|
|
228
|
+
<Text inverse={isSelected}>
|
|
229
|
+
{item.label}
|
|
230
|
+
{item.disabled ? " (disabled)" : ""}
|
|
231
|
+
</Text>
|
|
214
232
|
</Text>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
233
|
+
{item.description && (
|
|
234
|
+
<Text color="gray"> {item.description}</Text>
|
|
235
|
+
)}
|
|
236
|
+
</Box>
|
|
237
|
+
);
|
|
238
|
+
}}
|
|
221
239
|
/>
|
|
222
240
|
</Box>
|
|
223
241
|
|
|
@@ -37,7 +37,6 @@ const TOOL_LABELS: Record<string, string> = {
|
|
|
37
37
|
"claude-code": "Claude Code",
|
|
38
38
|
"codex-cli": "Codex",
|
|
39
39
|
"gemini-cli": "Gemini",
|
|
40
|
-
"qwen-cli": "Qwen",
|
|
41
40
|
};
|
|
42
41
|
|
|
43
42
|
const INFERENCE_LABELS: Record<InferenceLevel, string> = {
|
|
@@ -255,13 +254,6 @@ export function ModelSelectorScreen({
|
|
|
255
254
|
{modelOptions.length === 0 ? " (no options)" : ""}
|
|
256
255
|
</Text>
|
|
257
256
|
</Box>
|
|
258
|
-
{tool === "qwen-cli" ? (
|
|
259
|
-
<Box marginBottom={1} flexDirection="column">
|
|
260
|
-
<Text>Latest Qwen models from Alibaba Cloud ModelStudio:</Text>
|
|
261
|
-
<Text>• coder-model (qwen3-coder-plus-2025-09-23)</Text>
|
|
262
|
-
<Text>• vision-model (qwen3-vl-plus-2025-09-23)</Text>
|
|
263
|
-
</Box>
|
|
264
|
-
) : null}
|
|
265
257
|
|
|
266
258
|
{modelItems.length === 0 ? (
|
|
267
259
|
<Select
|
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
const byId = (tool: string) => getModelOptions(tool).map((m) => m.id);
|
|
9
9
|
|
|
10
10
|
describe("modelOptions", () => {
|
|
11
|
-
it("lists Claude official aliases and sets
|
|
11
|
+
it("lists Claude official aliases and sets Default as default", () => {
|
|
12
12
|
const options = getModelOptions("claude-code");
|
|
13
13
|
const ids = options.map((m) => m.id);
|
|
14
|
-
expect(ids).toEqual(["opus", "sonnet", "haiku"]);
|
|
14
|
+
expect(ids).toEqual(["", "opus", "sonnet", "haiku"]);
|
|
15
15
|
const defaultModel = getDefaultModelOption("claude-code");
|
|
16
|
-
expect(defaultModel?.id).toBe("
|
|
17
|
-
expect(defaultModel?.label).toBe("
|
|
16
|
+
expect(defaultModel?.id).toBe("");
|
|
17
|
+
expect(defaultModel?.label).toBe("Default (Auto)");
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
it("has unique Codex models", () => {
|
|
@@ -22,6 +22,7 @@ describe("modelOptions", () => {
|
|
|
22
22
|
const unique = new Set(ids);
|
|
23
23
|
expect(unique.size).toBe(ids.length);
|
|
24
24
|
expect(ids).toEqual([
|
|
25
|
+
"",
|
|
25
26
|
"gpt-5.1-codex",
|
|
26
27
|
"gpt-5.2",
|
|
27
28
|
"gpt-5.1-codex-max",
|
|
@@ -52,14 +53,16 @@ describe("modelOptions", () => {
|
|
|
52
53
|
|
|
53
54
|
it("lists expected Gemini models", () => {
|
|
54
55
|
expect(byId("gemini-cli")).toEqual([
|
|
56
|
+
"",
|
|
55
57
|
"gemini-3-pro-preview",
|
|
58
|
+
"gemini-3-flash-preview",
|
|
56
59
|
"gemini-2.5-pro",
|
|
57
60
|
"gemini-2.5-flash",
|
|
58
61
|
"gemini-2.5-flash-lite",
|
|
59
62
|
]);
|
|
60
63
|
});
|
|
61
64
|
|
|
62
|
-
it("
|
|
63
|
-
expect(byId("qwen-cli")).toEqual([
|
|
65
|
+
it("returns no models for unsupported tools", () => {
|
|
66
|
+
expect(byId("qwen-cli")).toEqual([]);
|
|
64
67
|
});
|
|
65
68
|
});
|
|
@@ -5,12 +5,17 @@ const CODEX_MAX_LEVELS: InferenceLevel[] = ["xhigh", "high", "medium", "low"];
|
|
|
5
5
|
|
|
6
6
|
const MODEL_OPTIONS: Record<string, ModelOption[]> = {
|
|
7
7
|
"claude-code": [
|
|
8
|
+
{
|
|
9
|
+
id: "",
|
|
10
|
+
label: "Default (Auto)",
|
|
11
|
+
description: "Use Claude Code default behavior",
|
|
12
|
+
isDefault: true,
|
|
13
|
+
},
|
|
8
14
|
{
|
|
9
15
|
id: "opus",
|
|
10
16
|
label: "Opus 4.5",
|
|
11
17
|
description:
|
|
12
18
|
"Official Opus alias for Claude Code (non-custom, matches /model option).",
|
|
13
|
-
isDefault: true,
|
|
14
19
|
},
|
|
15
20
|
{
|
|
16
21
|
id: "sonnet",
|
|
@@ -25,13 +30,20 @@ const MODEL_OPTIONS: Record<string, ModelOption[]> = {
|
|
|
25
30
|
},
|
|
26
31
|
],
|
|
27
32
|
"codex-cli": [
|
|
33
|
+
{
|
|
34
|
+
id: "",
|
|
35
|
+
label: "Default (Auto)",
|
|
36
|
+
description: "Use Codex default model",
|
|
37
|
+
isDefault: true,
|
|
38
|
+
inferenceLevels: CODEX_BASE_LEVELS,
|
|
39
|
+
defaultInference: "high",
|
|
40
|
+
},
|
|
28
41
|
{
|
|
29
42
|
id: "gpt-5.1-codex",
|
|
30
43
|
label: "gpt-5.1-codex",
|
|
31
44
|
description: "Standard Codex model",
|
|
32
45
|
inferenceLevels: CODEX_BASE_LEVELS,
|
|
33
46
|
defaultInference: "high",
|
|
34
|
-
isDefault: true,
|
|
35
47
|
},
|
|
36
48
|
{
|
|
37
49
|
id: "gpt-5.2",
|
|
@@ -63,12 +75,22 @@ const MODEL_OPTIONS: Record<string, ModelOption[]> = {
|
|
|
63
75
|
},
|
|
64
76
|
],
|
|
65
77
|
"gemini-cli": [
|
|
78
|
+
{
|
|
79
|
+
id: "",
|
|
80
|
+
label: "Default (Auto)",
|
|
81
|
+
description: "Use Gemini CLI default model",
|
|
82
|
+
isDefault: true,
|
|
83
|
+
},
|
|
66
84
|
{
|
|
67
85
|
id: "gemini-3-pro-preview",
|
|
68
86
|
label: "Pro (gemini-3-pro-preview)",
|
|
69
87
|
description:
|
|
70
88
|
"Default Pro. Falls back to gemini-2.5-pro when preview is unavailable.",
|
|
71
|
-
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "gemini-3-flash-preview",
|
|
92
|
+
label: "Flash (gemini-3-flash-preview)",
|
|
93
|
+
description: "Next-generation high-speed model",
|
|
72
94
|
},
|
|
73
95
|
{
|
|
74
96
|
id: "gemini-2.5-pro",
|
|
@@ -86,21 +108,6 @@ const MODEL_OPTIONS: Record<string, ModelOption[]> = {
|
|
|
86
108
|
description: "Fastest for simple tasks",
|
|
87
109
|
},
|
|
88
110
|
],
|
|
89
|
-
"qwen-cli": [
|
|
90
|
-
{
|
|
91
|
-
id: "coder-model",
|
|
92
|
-
label: "Coder Model",
|
|
93
|
-
description:
|
|
94
|
-
"Latest Qwen Coder model (qwen3-coder-plus-2025-09-23) from Alibaba Cloud ModelStudio",
|
|
95
|
-
isDefault: true,
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
id: "vision-model",
|
|
99
|
-
label: "Vision Model",
|
|
100
|
-
description:
|
|
101
|
-
"Latest Qwen Vision model (qwen3-vl-plus-2025-09-23) from Alibaba Cloud ModelStudio",
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
111
|
};
|
|
105
112
|
|
|
106
113
|
export function getModelOptions(tool: AITool): ModelOption[] {
|
package/src/config/tools.ts
CHANGED
|
@@ -299,13 +299,17 @@ 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
|
+
|
|
302
306
|
// ビルトインツールをAIToolConfig形式に変換
|
|
303
307
|
const builtinConfigs: AIToolConfig[] = BUILTIN_TOOLS.map((tool) => ({
|
|
304
308
|
id: tool.id,
|
|
305
309
|
displayName: tool.displayName,
|
|
306
310
|
...(tool.icon ? { icon: tool.icon } : {}),
|
|
307
311
|
isBuiltin: true,
|
|
308
|
-
}));
|
|
312
|
+
})).filter((tool) => !UNSUPPORTED_BUILTIN_TOOL_IDS.has(tool.id));
|
|
309
313
|
|
|
310
314
|
// カスタムツールをAIToolConfig形式に変換
|
|
311
315
|
const customConfigs: AIToolConfig[] = config.customTools.map((tool) => ({
|
package/src/gemini.ts
CHANGED
|
@@ -134,62 +134,41 @@ export async function launchGeminiCLI(
|
|
|
134
134
|
// Auto-detect locally installed gemini command
|
|
135
135
|
const hasLocalGemini = await isGeminiCommandAvailable();
|
|
136
136
|
|
|
137
|
-
//
|
|
137
|
+
// Preserve TTY for interactive UI (colors/width) by inheriting stdout/stderr.
|
|
138
|
+
// Session ID is determined via file-based detection after exit.
|
|
138
139
|
let capturedSessionId: string | null = null;
|
|
139
|
-
const extractSessionId = (output: string | undefined) => {
|
|
140
|
-
if (!output) return;
|
|
141
|
-
// Gemini outputs "Session ID: <uuid>" in exit summary
|
|
142
|
-
// UUID may be split across lines due to terminal width
|
|
143
|
-
// First, find "Session ID:" and extract following hex characters
|
|
144
|
-
const sessionIdIndex = output.indexOf("Session ID:");
|
|
145
|
-
if (sessionIdIndex === -1) return;
|
|
146
|
-
|
|
147
|
-
// Extract text after "Session ID:" until we have enough hex chars for UUID
|
|
148
|
-
const afterLabel = output.slice(sessionIdIndex + "Session ID:".length);
|
|
149
|
-
// Remove all non-hex characters except dash, then extract UUID pattern
|
|
150
|
-
const hexOnly = afterLabel.replace(/[^0-9a-fA-F-]/g, "");
|
|
151
|
-
// UUID format: 8-4-4-4-12 = 32 hex chars + 4 dashes
|
|
152
|
-
const uuidMatch = hexOnly.match(
|
|
153
|
-
/^([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})/i,
|
|
154
|
-
);
|
|
155
|
-
if (uuidMatch) {
|
|
156
|
-
capturedSessionId = `${uuidMatch[1]}-${uuidMatch[2]}-${uuidMatch[3]}-${uuidMatch[4]}-${uuidMatch[5]}`;
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
140
|
|
|
160
|
-
const runGemini = async (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
141
|
+
const runGemini = async (runArgs: string[]): Promise<void> => {
|
|
142
|
+
const execChild = async (child: Promise<unknown>) => {
|
|
143
|
+
try {
|
|
144
|
+
await child;
|
|
145
|
+
} catch (execError: unknown) {
|
|
146
|
+
// Treat SIGINT/SIGTERM as normal exit (user pressed Ctrl+C)
|
|
147
|
+
const signal = (execError as { signal?: unknown })?.signal;
|
|
148
|
+
if (signal === "SIGINT" || signal === "SIGTERM") {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
throw execError;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
166
154
|
|
|
167
|
-
const
|
|
155
|
+
const run = async (cmd: string, args: string[]) => {
|
|
168
156
|
const child = execa(cmd, args, {
|
|
169
157
|
cwd: worktreePath,
|
|
170
158
|
shell: true,
|
|
171
159
|
stdin: childStdio.stdin,
|
|
172
|
-
stdout:
|
|
160
|
+
stdout: childStdio.stdout,
|
|
173
161
|
stderr: childStdio.stderr,
|
|
174
162
|
env: baseEnv,
|
|
175
163
|
});
|
|
176
|
-
|
|
177
|
-
// Pass stdout through to terminal while capturing
|
|
178
|
-
child.stdout?.on("data", (chunk: Buffer) => {
|
|
179
|
-
const text = chunk.toString("utf8");
|
|
180
|
-
outputChunks.push(text);
|
|
181
|
-
terminal.stdout.write(chunk);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
await child;
|
|
185
|
-
return outputChunks.join("");
|
|
164
|
+
await execChild(child);
|
|
186
165
|
};
|
|
187
166
|
|
|
188
167
|
if (hasLocalGemini) {
|
|
189
168
|
console.log(
|
|
190
169
|
chalk.green(" ✨ Using locally installed gemini command"),
|
|
191
170
|
);
|
|
192
|
-
return await
|
|
171
|
+
return await run("gemini", runArgs);
|
|
193
172
|
}
|
|
194
173
|
console.log(
|
|
195
174
|
chalk.cyan(" 🔄 Falling back to bunx @google/gemini-cli@latest"),
|
|
@@ -202,15 +181,14 @@ export async function launchGeminiCLI(
|
|
|
202
181
|
console.log(chalk.yellow(" npm install -g @google/gemini-cli"));
|
|
203
182
|
console.log("");
|
|
204
183
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
205
|
-
return await
|
|
184
|
+
return await run("bunx", [GEMINI_CLI_PACKAGE, ...runArgs]);
|
|
206
185
|
};
|
|
207
186
|
|
|
208
|
-
let output: string | undefined;
|
|
209
187
|
let fellBackToLatest = false;
|
|
210
188
|
try {
|
|
211
189
|
// Try with explicit session ID first (if any), then fallback to --resume (latest) once
|
|
212
190
|
try {
|
|
213
|
-
|
|
191
|
+
await runGemini(argsPrimary);
|
|
214
192
|
} catch (err) {
|
|
215
193
|
const shouldRetry =
|
|
216
194
|
(options.mode === "resume" || options.mode === "continue") &&
|
|
@@ -222,7 +200,7 @@ export async function launchGeminiCLI(
|
|
|
222
200
|
` ⚠️ Failed to resume session ${resumeSessionId}. Retrying with latest session...`,
|
|
223
201
|
),
|
|
224
202
|
);
|
|
225
|
-
|
|
203
|
+
await runGemini(argsFallback);
|
|
226
204
|
} else {
|
|
227
205
|
throw err;
|
|
228
206
|
}
|
|
@@ -231,9 +209,6 @@ export async function launchGeminiCLI(
|
|
|
231
209
|
childStdio.cleanup();
|
|
232
210
|
}
|
|
233
211
|
|
|
234
|
-
// Extract session ID from Gemini's exit summary output
|
|
235
|
-
extractSessionId(output);
|
|
236
|
-
|
|
237
212
|
const explicitResumeSucceeded = usedExplicitSessionId && !fellBackToLatest;
|
|
238
213
|
|
|
239
214
|
// If we explicitly resumed a specific session (and did not fall back), keep that ID.
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
type CodexReasoningEffort,
|
|
17
17
|
} from "./codex.js";
|
|
18
18
|
import { launchGeminiCLI, GeminiError } from "./gemini.js";
|
|
19
|
-
import { launchQwenCLI, QwenError } from "./qwen.js";
|
|
20
19
|
import {
|
|
21
20
|
WorktreeOrchestrator,
|
|
22
21
|
type EnsureWorktreeOptions,
|
|
@@ -117,7 +116,6 @@ function isRecoverableError(error: unknown): boolean {
|
|
|
117
116
|
error instanceof WorktreeError ||
|
|
118
117
|
error instanceof CodexError ||
|
|
119
118
|
error instanceof GeminiError ||
|
|
120
|
-
error instanceof QwenError ||
|
|
121
119
|
error instanceof DependencyInstallError
|
|
122
120
|
) {
|
|
123
121
|
return true;
|
|
@@ -129,7 +127,6 @@ function isRecoverableError(error: unknown): boolean {
|
|
|
129
127
|
error.name === "WorktreeError" ||
|
|
130
128
|
error.name === "CodexError" ||
|
|
131
129
|
error.name === "GeminiError" ||
|
|
132
|
-
error.name === "QwenError" ||
|
|
133
130
|
error.name === "DependencyInstallError"
|
|
134
131
|
);
|
|
135
132
|
}
|
|
@@ -144,7 +141,6 @@ function isRecoverableError(error: unknown): boolean {
|
|
|
144
141
|
name === "WorktreeError" ||
|
|
145
142
|
name === "CodexError" ||
|
|
146
143
|
name === "GeminiError" ||
|
|
147
|
-
name === "QwenError" ||
|
|
148
144
|
name === "DependencyInstallError"
|
|
149
145
|
);
|
|
150
146
|
}
|
|
@@ -311,6 +307,12 @@ export async function handleAIToolWorkflow(
|
|
|
311
307
|
`Selected: ${branchLabel} with ${tool} (${mode} mode${modelInfo}, skipPermissions: ${skipPermissions})`,
|
|
312
308
|
);
|
|
313
309
|
|
|
310
|
+
if (tool === "qwen-cli") {
|
|
311
|
+
printError("Qwen CLI is currently unsupported.");
|
|
312
|
+
await waitForErrorAcknowledgement();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
314
316
|
try {
|
|
315
317
|
// Get repository root
|
|
316
318
|
const repoRootResult = await runGitStep("retrieve repository root", () =>
|
|
@@ -658,28 +660,6 @@ export async function handleAIToolWorkflow(
|
|
|
658
660
|
launchOptions.model = model;
|
|
659
661
|
}
|
|
660
662
|
launchResult = await launchGeminiCLI(worktreePath, launchOptions);
|
|
661
|
-
} else if (tool === "qwen-cli") {
|
|
662
|
-
const launchOptions: {
|
|
663
|
-
mode?: "normal" | "continue" | "resume";
|
|
664
|
-
skipPermissions?: boolean;
|
|
665
|
-
envOverrides?: Record<string, string>;
|
|
666
|
-
model?: string;
|
|
667
|
-
sessionId?: string | null;
|
|
668
|
-
} = {
|
|
669
|
-
mode:
|
|
670
|
-
mode === "resume"
|
|
671
|
-
? "resume"
|
|
672
|
-
: mode === "continue"
|
|
673
|
-
? "continue"
|
|
674
|
-
: "normal",
|
|
675
|
-
skipPermissions,
|
|
676
|
-
envOverrides: sharedEnv,
|
|
677
|
-
sessionId: resumeSessionId,
|
|
678
|
-
};
|
|
679
|
-
if (model) {
|
|
680
|
-
launchOptions.model = model;
|
|
681
|
-
}
|
|
682
|
-
launchResult = await launchQwenCLI(worktreePath, launchOptions);
|
|
683
663
|
} else {
|
|
684
664
|
// Custom tool
|
|
685
665
|
printInfo(`Launching custom tool: ${toolConfig.displayName}`);
|
|
@@ -743,7 +723,7 @@ export async function handleAIToolWorkflow(
|
|
|
743
723
|
}
|
|
744
724
|
} else if (!finalSessionId && tool === "gemini-cli") {
|
|
745
725
|
try {
|
|
746
|
-
const latestGemini = await findLatestGeminiSession(
|
|
726
|
+
const latestGemini = await findLatestGeminiSession({
|
|
747
727
|
since: launchStartedAt - 60_000,
|
|
748
728
|
until: finishedAt + 60_000,
|
|
749
729
|
preferClosestTo: finishedAt,
|