@akiojin/gwt 3.1.2 → 4.0.1
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 +3 -4
- package/README.md +3 -4
- package/dist/claude.d.ts +21 -0
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +73 -30
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/components/common/Select.d.ts +6 -0
- package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Select.js +3 -2
- package/dist/cli/ui/components/common/Select.js.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +6 -0
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +3 -2
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BatchMergeProgressScreen.d.ts +3 -0
- package/dist/cli/ui/components/screens/BatchMergeProgressScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BatchMergeProgressScreen.js +3 -2
- package/dist/cli/ui/components/screens/BatchMergeProgressScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BatchMergeResultScreen.d.ts +3 -0
- package/dist/cli/ui/components/screens/BatchMergeResultScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BatchMergeResultScreen.js +3 -2
- package/dist/cli/ui/components/screens/BatchMergeResultScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +3 -0
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js +3 -2
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -0
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +3 -4
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts +10 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +15 -15
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/EnvironmentProfileScreen.d.ts +3 -0
- package/dist/cli/ui/components/screens/EnvironmentProfileScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/EnvironmentProfileScreen.js +3 -2
- package/dist/cli/ui/components/screens/EnvironmentProfileScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +15 -0
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +3 -2
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts +6 -0
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js +3 -7
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +6 -0
- package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/PRCleanupScreen.js +3 -2
- package/dist/cli/ui/components/screens/PRCleanupScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +6 -0
- package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/SessionSelectorScreen.js +3 -2
- package/dist/cli/ui/components/screens/SessionSelectorScreen.js.map +1 -1
- package/dist/cli/ui/hooks/useAppInput.d.ts +20 -0
- package/dist/cli/ui/hooks/useAppInput.d.ts.map +1 -0
- package/dist/cli/ui/hooks/useAppInput.js +137 -0
- package/dist/cli/ui/hooks/useAppInput.js.map +1 -0
- package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts +3 -0
- package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js +3 -2
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.js +0 -2
- package/dist/cli/ui/utils/branchFormatter.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/client/assets/{index-f5D2XwDh.js → index-v8smkNOL.js} +16 -16
- package/dist/client/index.html +1 -1
- package/dist/codex.d.ts +32 -0
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +32 -1
- package/dist/codex.js.map +1 -1
- package/dist/config/builtin-tools.d.ts +1 -5
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +1 -18
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/gemini.d.ts +17 -0
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +43 -61
- package/dist/gemini.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -20
- package/dist/index.js.map +1 -1
- package/dist/utils/command.d.ts +10 -0
- package/dist/utils/command.d.ts.map +1 -0
- package/dist/utils/command.js +25 -0
- package/dist/utils/command.js.map +1 -0
- package/dist/utils/session/index.d.ts +0 -2
- package/dist/utils/session/index.d.ts.map +1 -1
- package/dist/utils/session/index.js +0 -3
- package/dist/utils/session/index.js.map +1 -1
- package/dist/utils/session/parsers/index.d.ts +0 -1
- package/dist/utils/session/parsers/index.d.ts.map +1 -1
- package/dist/utils/session/parsers/index.js +0 -2
- package/dist/utils/session/parsers/index.js.map +1 -1
- package/dist/utils/session.d.ts +0 -1
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +0 -1
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/terminal.d.ts +34 -0
- package/dist/utils/terminal.d.ts.map +1 -1
- package/dist/utils/terminal.js +51 -4
- package/dist/utils/terminal.js.map +1 -1
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -1
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +0 -2
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -1
- package/package.json +1 -1
- package/src/claude.ts +92 -34
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +3 -1
- package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +8 -5
- package/src/cli/ui/components/common/Select.tsx +9 -2
- package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +9 -2
- package/src/cli/ui/components/screens/BatchMergeProgressScreen.tsx +6 -2
- package/src/cli/ui/components/screens/BatchMergeResultScreen.tsx +6 -2
- package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +6 -2
- package/src/cli/ui/components/screens/BranchListScreen.tsx +6 -4
- package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +36 -27
- package/src/cli/ui/components/screens/EnvironmentProfileScreen.tsx +6 -2
- package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +18 -2
- package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +9 -10
- package/src/cli/ui/components/screens/PRCleanupScreen.tsx +9 -2
- package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +9 -2
- package/src/cli/ui/hooks/useAppInput.ts +171 -0
- package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +6 -2
- package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +68 -1
- package/src/cli/ui/utils/branchFormatter.ts +0 -1
- package/src/cli/ui/utils/modelOptions.test.ts +9 -6
- package/src/cli/ui/utils/modelOptions.ts +25 -18
- package/src/codex.ts +40 -1
- package/src/config/builtin-tools.ts +1 -19
- package/src/gemini.ts +47 -68
- package/src/index.ts +0 -26
- package/src/utils/command.ts +26 -0
- package/src/utils/session/index.ts +0 -4
- package/src/utils/session/parsers/index.ts +0 -3
- package/src/utils/session.ts +0 -1
- package/src/utils/terminal.ts +65 -4
- package/src/web/client/src/components/branch-detail/BranchInfoCards.tsx +0 -1
- package/dist/qwen.d.ts +0 -16
- package/dist/qwen.d.ts.map +0 -1
- package/dist/qwen.js +0 -202
- package/dist/qwen.js.map +0 -1
- package/dist/utils/session/parsers/qwen.d.ts +0 -21
- package/dist/utils/session/parsers/qwen.d.ts.map +0 -1
- package/dist/utils/session/parsers/qwen.js +0 -36
- package/dist/utils/session/parsers/qwen.js.map +0 -1
- package/src/qwen.ts +0 -273
- package/src/utils/session/parsers/qwen.ts +0 -54
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 {
|
|
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,64 +152,43 @@ export async function launchGeminiCLI(
|
|
|
132
152
|
const childStdio = createChildStdio();
|
|
133
153
|
|
|
134
154
|
// Auto-detect locally installed gemini command
|
|
135
|
-
const hasLocalGemini = await
|
|
155
|
+
const hasLocalGemini = await isCommandAvailable("gemini");
|
|
136
156
|
|
|
137
|
-
//
|
|
157
|
+
// Preserve TTY for interactive UI (colors/width) by inheriting stdout/stderr.
|
|
158
|
+
// Session ID is determined via file-based detection after exit.
|
|
138
159
|
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
160
|
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const runGemini = async (
|
|
161
|
-
runArgs: string[],
|
|
162
|
-
): Promise<string | undefined> => {
|
|
163
|
-
// Capture stdout while passing through to terminal
|
|
164
|
-
// Store chunks to extract session ID after process exits
|
|
165
|
-
const outputChunks: string[] = [];
|
|
161
|
+
const runGemini = async (runArgs: string[]): Promise<void> => {
|
|
162
|
+
const execChild = async (child: Promise<unknown>) => {
|
|
163
|
+
try {
|
|
164
|
+
await child;
|
|
165
|
+
} catch (execError: unknown) {
|
|
166
|
+
// Treat SIGINT/SIGTERM as normal exit (user pressed Ctrl+C)
|
|
167
|
+
const signal = (execError as { signal?: unknown })?.signal;
|
|
168
|
+
if (signal === "SIGINT" || signal === "SIGTERM") {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
throw execError;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
166
174
|
|
|
167
|
-
const
|
|
175
|
+
const run = async (cmd: string, args: string[]) => {
|
|
168
176
|
const child = execa(cmd, args, {
|
|
169
177
|
cwd: worktreePath,
|
|
170
178
|
shell: true,
|
|
171
179
|
stdin: childStdio.stdin,
|
|
172
|
-
stdout:
|
|
180
|
+
stdout: childStdio.stdout,
|
|
173
181
|
stderr: childStdio.stderr,
|
|
174
182
|
env: baseEnv,
|
|
175
183
|
});
|
|
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("");
|
|
184
|
+
await execChild(child);
|
|
186
185
|
};
|
|
187
186
|
|
|
188
187
|
if (hasLocalGemini) {
|
|
189
188
|
console.log(
|
|
190
189
|
chalk.green(" ✨ Using locally installed gemini command"),
|
|
191
190
|
);
|
|
192
|
-
return await
|
|
191
|
+
return await run("gemini", runArgs);
|
|
193
192
|
}
|
|
194
193
|
console.log(
|
|
195
194
|
chalk.cyan(" 🔄 Falling back to bunx @google/gemini-cli@latest"),
|
|
@@ -202,15 +201,14 @@ export async function launchGeminiCLI(
|
|
|
202
201
|
console.log(chalk.yellow(" npm install -g @google/gemini-cli"));
|
|
203
202
|
console.log("");
|
|
204
203
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
205
|
-
return await
|
|
204
|
+
return await run("bunx", [GEMINI_CLI_PACKAGE, ...runArgs]);
|
|
206
205
|
};
|
|
207
206
|
|
|
208
|
-
let output: string | undefined;
|
|
209
207
|
let fellBackToLatest = false;
|
|
210
208
|
try {
|
|
211
209
|
// Try with explicit session ID first (if any), then fallback to --resume (latest) once
|
|
212
210
|
try {
|
|
213
|
-
|
|
211
|
+
await runGemini(argsPrimary);
|
|
214
212
|
} catch (err) {
|
|
215
213
|
const shouldRetry =
|
|
216
214
|
(options.mode === "resume" || options.mode === "continue") &&
|
|
@@ -222,7 +220,7 @@ export async function launchGeminiCLI(
|
|
|
222
220
|
` ⚠️ Failed to resume session ${resumeSessionId}. Retrying with latest session...`,
|
|
223
221
|
),
|
|
224
222
|
);
|
|
225
|
-
|
|
223
|
+
await runGemini(argsFallback);
|
|
226
224
|
} else {
|
|
227
225
|
throw err;
|
|
228
226
|
}
|
|
@@ -231,9 +229,6 @@ export async function launchGeminiCLI(
|
|
|
231
229
|
childStdio.cleanup();
|
|
232
230
|
}
|
|
233
231
|
|
|
234
|
-
// Extract session ID from Gemini's exit summary output
|
|
235
|
-
extractSessionId(output);
|
|
236
|
-
|
|
237
232
|
const explicitResumeSucceeded = usedExplicitSessionId && !fellBackToLatest;
|
|
238
233
|
|
|
239
234
|
// If we explicitly resumed a specific session (and did not fall back), keep that ID.
|
|
@@ -268,7 +263,7 @@ export async function launchGeminiCLI(
|
|
|
268
263
|
|
|
269
264
|
return capturedSessionId ? { sessionId: capturedSessionId } : {};
|
|
270
265
|
} catch (error: unknown) {
|
|
271
|
-
const hasLocalGemini = await
|
|
266
|
+
const hasLocalGemini = await isCommandAvailable("gemini");
|
|
272
267
|
let errorMessage: string;
|
|
273
268
|
const err = error as NodeJS.ErrnoException;
|
|
274
269
|
|
|
@@ -316,29 +311,13 @@ export async function launchGeminiCLI(
|
|
|
316
311
|
throw new GeminiError(errorMessage, error);
|
|
317
312
|
} finally {
|
|
318
313
|
terminal.exitRawMode();
|
|
314
|
+
resetTerminalModes(terminal.stdout);
|
|
319
315
|
}
|
|
320
316
|
}
|
|
321
317
|
|
|
322
318
|
/**
|
|
323
|
-
*
|
|
324
|
-
* @returns true if `gemini` command exists in PATH, false otherwise
|
|
319
|
+
* Checks whether Gemini CLI is available via `bunx` in the current environment.
|
|
325
320
|
*/
|
|
326
|
-
async function isGeminiCommandAvailable(): Promise<boolean> {
|
|
327
|
-
try {
|
|
328
|
-
const command = process.platform === "win32" ? "where" : "which";
|
|
329
|
-
await execa(command, ["gemini"], {
|
|
330
|
-
shell: true,
|
|
331
|
-
stdin: "ignore",
|
|
332
|
-
stdout: "ignore",
|
|
333
|
-
stderr: "ignore",
|
|
334
|
-
});
|
|
335
|
-
return true;
|
|
336
|
-
} catch {
|
|
337
|
-
// gemini command not found in PATH
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
321
|
export async function isGeminiCLIAvailable(): Promise<boolean> {
|
|
343
322
|
try {
|
|
344
323
|
await execa("bunx", [GEMINI_CLI_PACKAGE, "--version"], { shell: true });
|
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
|
}
|
|
@@ -658,28 +654,6 @@ export async function handleAIToolWorkflow(
|
|
|
658
654
|
launchOptions.model = model;
|
|
659
655
|
}
|
|
660
656
|
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
657
|
} else {
|
|
684
658
|
// Custom tool
|
|
685
659
|
printInfo(`Launching custom tool: ${toolConfig.displayName}`);
|
|
@@ -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";
|
package/src/utils/session.ts
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
* - ./session/parsers/claude.js - Claude-specific functions
|
|
12
12
|
* - ./session/parsers/codex.js - Codex-specific functions
|
|
13
13
|
* - ./session/parsers/gemini.js - Gemini-specific functions
|
|
14
|
-
* - ./session/parsers/qwen.js - Qwen-specific functions
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
export * from "./session/index.js";
|
package/src/utils/terminal.ts
CHANGED
|
@@ -2,6 +2,13 @@ import fs from "node:fs";
|
|
|
2
2
|
import { platform } from "node:os";
|
|
3
3
|
import { ReadStream, WriteStream } from "node:tty";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Terminal streams used by the CLI (stdin/stdout/stderr) and their raw-mode
|
|
7
|
+
* teardown helper.
|
|
8
|
+
*
|
|
9
|
+
* When the current process is not a TTY, this may fall back to `/dev/tty` so
|
|
10
|
+
* interactive child processes can still read/write correctly.
|
|
11
|
+
*/
|
|
5
12
|
export interface TerminalStreams {
|
|
6
13
|
stdin: NodeJS.ReadStream;
|
|
7
14
|
stdout: NodeJS.WriteStream;
|
|
@@ -14,9 +21,18 @@ export interface TerminalStreams {
|
|
|
14
21
|
}
|
|
15
22
|
|
|
16
23
|
const DEV_TTY_PATH = "/dev/tty";
|
|
24
|
+
// 端末モードのリセット:
|
|
25
|
+
// - ESC[?1l: application cursor keys (DECCKM) を無効化
|
|
26
|
+
// - ESC>: keypad mode を normal/numeric に戻す
|
|
27
|
+
// WSL2/Windowsの矢印キー入力が壊れるケースを抑止する。
|
|
28
|
+
const TERMINAL_RESET_SEQUENCE = "\u001b[?1l\u001b>";
|
|
17
29
|
|
|
18
30
|
let cachedStreams: TerminalStreams | null = null;
|
|
19
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Stdio configuration for launching an interactive child process (via `execa`),
|
|
34
|
+
* plus a cleanup hook to be called after the child exits.
|
|
35
|
+
*/
|
|
20
36
|
export interface ChildStdio {
|
|
21
37
|
stdin: "inherit" | { file: string; append?: boolean };
|
|
22
38
|
stdout: "inherit" | { file: string; append?: boolean };
|
|
@@ -165,6 +181,9 @@ function createTerminalStreams(): TerminalStreams {
|
|
|
165
181
|
}
|
|
166
182
|
}
|
|
167
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Returns cached terminal streams and falls back to `/dev/tty` when needed.
|
|
186
|
+
*/
|
|
168
187
|
export function getTerminalStreams(): TerminalStreams {
|
|
169
188
|
if (!cachedStreams) {
|
|
170
189
|
cachedStreams = createTerminalStreams();
|
|
@@ -172,6 +191,35 @@ export function getTerminalStreams(): TerminalStreams {
|
|
|
172
191
|
return cachedStreams;
|
|
173
192
|
}
|
|
174
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Best-effort terminal mode reset for interactive sessions.
|
|
196
|
+
*
|
|
197
|
+
* This writes a small ANSI sequence to restore cursor-key/keypad modes, which
|
|
198
|
+
* helps prevent broken arrow-key behavior on some Windows/WSL2 terminals after
|
|
199
|
+
* interactive CLIs exit.
|
|
200
|
+
*/
|
|
201
|
+
export function resetTerminalModes(
|
|
202
|
+
stdout: NodeJS.WriteStream | undefined,
|
|
203
|
+
): void {
|
|
204
|
+
if (!stdout || typeof stdout.write !== "function") {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (!("isTTY" in stdout) || !stdout.isTTY) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
stdout.write(TERMINAL_RESET_SEQUENCE);
|
|
212
|
+
} catch {
|
|
213
|
+
// Ignore terminal reset errors.
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Creates stdio settings for launching a child process.
|
|
219
|
+
*
|
|
220
|
+
* When terminal streams are backed by `/dev/tty`, forwards those file
|
|
221
|
+
* descriptors to the child so it remains interactive.
|
|
222
|
+
*/
|
|
175
223
|
export function createChildStdio(): ChildStdio {
|
|
176
224
|
const terminal = getTerminalStreams();
|
|
177
225
|
|
|
@@ -196,6 +244,12 @@ function isInteractive(stream: NodeJS.ReadStream): boolean {
|
|
|
196
244
|
return Boolean(stream.isTTY);
|
|
197
245
|
}
|
|
198
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Prints a message and waits for the user to press Enter when running in an
|
|
249
|
+
* interactive terminal.
|
|
250
|
+
*
|
|
251
|
+
* Useful for pausing on errors while ensuring raw mode is disabled.
|
|
252
|
+
*/
|
|
199
253
|
export async function waitForUserAcknowledgement(
|
|
200
254
|
message: string = DEFAULT_ACK_MESSAGE,
|
|
201
255
|
): Promise<void> {
|
|
@@ -214,20 +268,26 @@ export async function waitForUserAcknowledgement(
|
|
|
214
268
|
terminal.exitRawMode();
|
|
215
269
|
|
|
216
270
|
await new Promise<void>((resolve) => {
|
|
217
|
-
|
|
271
|
+
let finished = false;
|
|
272
|
+
|
|
273
|
+
function cleanup(): void {
|
|
274
|
+
if (finished) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
finished = true;
|
|
218
278
|
stdin.removeListener("data", onData);
|
|
219
279
|
if (typeof stdin.pause === "function") {
|
|
220
280
|
stdin.pause();
|
|
221
281
|
}
|
|
222
|
-
}
|
|
282
|
+
}
|
|
223
283
|
|
|
224
|
-
|
|
284
|
+
function onData(chunk: Buffer | string): void {
|
|
225
285
|
const data = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
226
286
|
if (data.includes("\n") || data.includes("\r")) {
|
|
227
287
|
cleanup();
|
|
228
288
|
resolve();
|
|
229
289
|
}
|
|
230
|
-
}
|
|
290
|
+
}
|
|
231
291
|
|
|
232
292
|
if (typeof stdout?.write === "function") {
|
|
233
293
|
stdout.write(`\n${message}\n`);
|
|
@@ -238,5 +298,6 @@ export async function waitForUserAcknowledgement(
|
|
|
238
298
|
}
|
|
239
299
|
|
|
240
300
|
stdin.on("data", onData);
|
|
301
|
+
process.once("exit", cleanup);
|
|
241
302
|
});
|
|
242
303
|
}
|
|
@@ -13,7 +13,6 @@ function mapToolLabel(toolId: string, toolLabel?: string | null): string {
|
|
|
13
13
|
if (toolId === "claude-code") return "Claude";
|
|
14
14
|
if (toolId === "codex-cli") return "Codex";
|
|
15
15
|
if (toolId === "gemini-cli") return "Gemini";
|
|
16
|
-
if (toolId === "qwen-cli") return "Qwen";
|
|
17
16
|
if (toolLabel) return toolLabel;
|
|
18
17
|
return "Custom";
|
|
19
18
|
}
|
package/dist/qwen.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export declare class QwenError extends Error {
|
|
2
|
-
cause?: unknown | undefined;
|
|
3
|
-
constructor(message: string, cause?: unknown | undefined);
|
|
4
|
-
}
|
|
5
|
-
export declare function launchQwenCLI(worktreePath: string, options?: {
|
|
6
|
-
skipPermissions?: boolean;
|
|
7
|
-
mode?: "normal" | "continue" | "resume";
|
|
8
|
-
extraArgs?: string[];
|
|
9
|
-
envOverrides?: Record<string, string>;
|
|
10
|
-
model?: string;
|
|
11
|
-
sessionId?: string | null;
|
|
12
|
-
}): Promise<{
|
|
13
|
-
sessionId?: string | null;
|
|
14
|
-
}>;
|
|
15
|
-
export declare function isQwenCLIAvailable(): Promise<boolean>;
|
|
16
|
-
//# sourceMappingURL=qwen.d.ts.map
|
package/dist/qwen.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"qwen.d.ts","sourceRoot":"","sources":["../src/qwen.ts"],"names":[],"mappings":"AAQA,qBAAa,SAAU,SAAQ,KAAK;IAGzB,KAAK,CAAC,EAAE,OAAO;gBADtB,OAAO,EAAE,MAAM,EACR,KAAK,CAAC,EAAE,OAAO,YAAA;CAKzB;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,GACL,OAAO,CAAC;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAmNxC;AAiBD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAgB3D"}
|