@akiojin/gwt 4.0.0 → 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 +0 -1
- package/README.md +0 -1
- 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 +7 -22
- 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 -2
- 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/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 -7
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +1 -20
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/config/tools.d.ts.map +1 -1
- package/dist/config/tools.js +1 -4
- package/dist/config/tools.js.map +1 -1
- package/dist/gemini.d.ts +17 -0
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +21 -21
- package/dist/gemini.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -5
- 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 +17 -26
- 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 -2
- 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 +1 -1
- package/src/codex.ts +40 -1
- package/src/config/builtin-tools.ts +1 -21
- package/src/config/tools.ts +1 -5
- package/src/gemini.ts +25 -21
- package/src/index.ts +0 -6
- 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
|
@@ -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
|
}
|
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 {
|
|
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
|
|
|
19
|
+
/**
|
|
20
|
+
* Default Codex model used when no override is provided.
|
|
21
|
+
*/
|
|
12
22
|
export const DEFAULT_CODEX_MODEL = "gpt-5.1-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
|
|
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
|
];
|
package/src/config/tools.ts
CHANGED
|
@@ -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
|
-
}))
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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", () =>
|
|
@@ -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"}
|