@akiojin/gwt 2.2.0 → 2.4.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 +6 -4
- package/README.md +6 -4
- package/dist/claude.d.ts +1 -0
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +6 -3
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/components/App.d.ts +6 -4
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +184 -107
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/common/Confirm.d.ts +1 -1
- package/dist/cli/ui/components/common/Confirm.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Confirm.js +7 -7
- package/dist/cli/ui/components/common/Confirm.js.map +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.d.ts +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +1 -1
- package/dist/cli/ui/components/common/ErrorBoundary.js +4 -4
- package/dist/cli/ui/components/common/ErrorBoundary.js.map +1 -1
- package/dist/cli/ui/components/common/Input.d.ts +2 -2
- package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Input.js +4 -4
- package/dist/cli/ui/components/common/Input.js.map +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.d.ts +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +1 -1
- package/dist/cli/ui/components/common/LoadingIndicator.js +4 -4
- package/dist/cli/ui/components/common/LoadingIndicator.js.map +1 -1
- package/dist/cli/ui/components/common/Select.d.ts +1 -1
- package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
- package/dist/cli/ui/components/common/Select.js +11 -12
- package/dist/cli/ui/components/common/Select.js.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +3 -3
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +11 -11
- package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js +39 -36
- package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -3
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +55 -50
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -2
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +25 -25
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts +18 -0
- package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -0
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js +201 -0
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -0
- package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +2 -2
- package/dist/cli/ui/components/screens/PRCleanupScreen.js +21 -21
- package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/SessionSelectorScreen.js +8 -8
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +1 -1
- package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +8 -8
- package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js +7 -4
- package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
- package/dist/cli/ui/types.d.ts +11 -1
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/cli/ui/utils/modelOptions.d.ts +6 -0
- package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -0
- package/dist/cli/ui/utils/modelOptions.js +111 -0
- package/dist/cli/ui/utils/modelOptions.js.map +1 -0
- package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
- package/dist/client/index.html +1 -1
- package/dist/codex.d.ts +6 -0
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +11 -4
- package/dist/codex.js.map +1 -1
- package/dist/config/builtin-tools.d.ts +10 -2
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +40 -4
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/tools.d.ts.map +1 -1
- package/dist/config/tools.js +4 -3
- package/dist/config/tools.js.map +1 -1
- package/dist/gemini.d.ts +13 -0
- package/dist/gemini.d.ts.map +1 -0
- package/dist/gemini.js +157 -0
- package/dist/gemini.js.map +1 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -7
- package/dist/index.js.map +1 -1
- package/dist/qwen.d.ts +13 -0
- package/dist/qwen.d.ts.map +1 -0
- package/dist/qwen.js +157 -0
- package/dist/qwen.js.map +1 -0
- package/dist/services/git.service.d.ts.map +1 -1
- package/dist/services/git.service.js.map +1 -1
- package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
- package/dist/web/client/src/components/BranchGraph.js +1 -1
- package/dist/web/client/src/components/BranchGraph.js.map +1 -1
- package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
- package/dist/web/client/src/components/EnvEditor.js +7 -4
- package/dist/web/client/src/components/EnvEditor.js.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.js +55 -18
- package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.js +10 -4
- package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.js +4 -2
- package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
- package/package.json +2 -1
- package/src/claude.ts +8 -3
- package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +69 -50
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +67 -45
- package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +117 -75
- package/src/cli/ui/__tests__/components/App.test.tsx +45 -37
- package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +81 -0
- package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +35 -22
- package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +22 -22
- package/src/cli/ui/__tests__/components/common/Input.test.tsx +29 -22
- package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +63 -43
- package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +57 -66
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +121 -91
- package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +18 -16
- package/src/cli/ui/__tests__/components/parts/Header.test.tsx +13 -13
- package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +20 -20
- package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +38 -26
- package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +31 -31
- package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +73 -37
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +261 -153
- package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +38 -32
- package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +39 -39
- package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +49 -21
- package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +52 -28
- package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +84 -48
- package/src/cli/ui/__tests__/integration/navigation.test.tsx +111 -83
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +111 -108
- package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +50 -37
- package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +75 -76
- package/src/cli/ui/components/App.tsx +317 -150
- package/src/cli/ui/components/common/Confirm.tsx +13 -9
- package/src/cli/ui/components/common/ErrorBoundary.tsx +8 -5
- package/src/cli/ui/components/common/Input.tsx +12 -4
- package/src/cli/ui/components/common/LoadingIndicator.tsx +8 -5
- package/src/cli/ui/components/common/Select.tsx +28 -17
- package/src/cli/ui/components/parts/Header.test.tsx +5 -15
- package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +20 -15
- package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +74 -54
- package/src/cli/ui/components/screens/BranchListScreen.tsx +92 -75
- package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +35 -28
- package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +320 -0
- package/src/cli/ui/components/screens/PRCleanupScreen.tsx +22 -22
- package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +8 -8
- package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +8 -8
- package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +9 -4
- package/src/cli/ui/types.ts +21 -1
- package/src/cli/ui/utils/modelOptions.test.ts +36 -0
- package/src/cli/ui/utils/modelOptions.ts +122 -0
- package/src/codex.ts +23 -4
- package/src/config/builtin-tools.ts +42 -4
- package/src/config/index.ts +2 -12
- package/src/config/tools.ts +16 -6
- package/src/gemini.ts +207 -0
- package/src/git.ts +2 -1
- package/src/index.ts +86 -6
- package/src/qwen.ts +213 -0
- package/src/services/git.service.ts +2 -1
- package/src/web/client/src/components/BranchGraph.tsx +3 -2
- package/src/web/client/src/components/EnvEditor.tsx +44 -11
- package/src/web/client/src/pages/BranchDetailPage.tsx +165 -54
- package/src/web/client/src/pages/BranchListPage.tsx +37 -13
- package/src/web/client/src/pages/ConfigManagementPage.tsx +28 -9
package/src/codex.ts
CHANGED
|
@@ -5,14 +5,23 @@ import { existsSync } from "fs";
|
|
|
5
5
|
import { createChildStdio, getTerminalStreams } from "./utils/terminal.js";
|
|
6
6
|
|
|
7
7
|
const CODEX_CLI_PACKAGE = "@openai/codex@latest";
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
export type CodexReasoningEffort = "low" | "medium" | "high" | "xhigh";
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_CODEX_MODEL = "gpt-5.1-codex";
|
|
12
|
+
export const DEFAULT_CODEX_REASONING_EFFORT: CodexReasoningEffort = "high";
|
|
13
|
+
|
|
14
|
+
export const buildDefaultCodexArgs = (
|
|
15
|
+
model: string = DEFAULT_CODEX_MODEL,
|
|
16
|
+
reasoningEffort: CodexReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT,
|
|
17
|
+
): string[] => [
|
|
9
18
|
"--enable",
|
|
10
19
|
"web_search_request",
|
|
11
|
-
|
|
20
|
+
`--model=${model}`,
|
|
12
21
|
"--sandbox",
|
|
13
22
|
"workspace-write",
|
|
14
23
|
"-c",
|
|
15
|
-
|
|
24
|
+
`model_reasoning_effort=${reasoningEffort}`,
|
|
16
25
|
"-c",
|
|
17
26
|
"model_reasoning_summaries=detailed",
|
|
18
27
|
"-c",
|
|
@@ -42,6 +51,8 @@ export async function launchCodexCLI(
|
|
|
42
51
|
extraArgs?: string[];
|
|
43
52
|
bypassApprovals?: boolean;
|
|
44
53
|
envOverrides?: Record<string, string>;
|
|
54
|
+
model?: string;
|
|
55
|
+
reasoningEffort?: CodexReasoningEffort;
|
|
45
56
|
} = {},
|
|
46
57
|
): Promise<void> {
|
|
47
58
|
const terminal = getTerminalStreams();
|
|
@@ -55,6 +66,12 @@ export async function launchCodexCLI(
|
|
|
55
66
|
console.log(chalk.gray(` Working directory: ${worktreePath}`));
|
|
56
67
|
|
|
57
68
|
const args: string[] = [];
|
|
69
|
+
const model = options.model ?? DEFAULT_CODEX_MODEL;
|
|
70
|
+
const reasoningEffort =
|
|
71
|
+
options.reasoningEffort ?? DEFAULT_CODEX_REASONING_EFFORT;
|
|
72
|
+
|
|
73
|
+
console.log(chalk.green(` 🎯 Model: ${model}`));
|
|
74
|
+
console.log(chalk.green(` 🧠 Reasoning: ${reasoningEffort}`));
|
|
58
75
|
|
|
59
76
|
switch (options.mode) {
|
|
60
77
|
case "continue":
|
|
@@ -80,7 +97,9 @@ export async function launchCodexCLI(
|
|
|
80
97
|
args.push(...options.extraArgs);
|
|
81
98
|
}
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
const codexArgs = buildDefaultCodexArgs(model, reasoningEffort);
|
|
101
|
+
|
|
102
|
+
args.push(...codexArgs);
|
|
84
103
|
|
|
85
104
|
terminal.exitRawMode();
|
|
86
105
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ビルトインAIツール定義
|
|
3
3
|
*
|
|
4
|
-
* Claude Code
|
|
4
|
+
* Claude Code、Codex、Gemini、Qwen の CustomAITool 形式定義
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { CustomAITool } from "../types/tools.js";
|
|
@@ -23,11 +23,11 @@ export const CLAUDE_CODE_TOOL: CustomAITool = {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Codex
|
|
26
|
+
* Codex のビルトイン定義
|
|
27
27
|
*/
|
|
28
28
|
export const CODEX_CLI_TOOL: CustomAITool = {
|
|
29
29
|
id: "codex-cli",
|
|
30
|
-
displayName: "Codex
|
|
30
|
+
displayName: "Codex",
|
|
31
31
|
type: "bunx",
|
|
32
32
|
command: "@openai/codex@latest",
|
|
33
33
|
defaultArgs: ["--auto-approve", "--verbose"],
|
|
@@ -38,7 +38,45 @@ export const CODEX_CLI_TOOL: CustomAITool = {
|
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Gemini のビルトイン定義
|
|
43
|
+
*/
|
|
44
|
+
export const GEMINI_CLI_TOOL: CustomAITool = {
|
|
45
|
+
id: "gemini-cli",
|
|
46
|
+
displayName: "Gemini",
|
|
47
|
+
type: "bunx",
|
|
48
|
+
command: "@google/gemini-cli@latest",
|
|
49
|
+
modeArgs: {
|
|
50
|
+
normal: [],
|
|
51
|
+
continue: ["-r", "latest"],
|
|
52
|
+
resume: ["-r", "latest"],
|
|
53
|
+
},
|
|
54
|
+
permissionSkipArgs: ["-y"],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Qwen のビルトイン定義
|
|
59
|
+
*/
|
|
60
|
+
export const QWEN_CLI_TOOL: CustomAITool = {
|
|
61
|
+
id: "qwen-cli",
|
|
62
|
+
displayName: "Qwen",
|
|
63
|
+
type: "bunx",
|
|
64
|
+
command: "@qwen-code/qwen-code@latest",
|
|
65
|
+
defaultArgs: ["--checkpointing"],
|
|
66
|
+
modeArgs: {
|
|
67
|
+
normal: [],
|
|
68
|
+
continue: [],
|
|
69
|
+
resume: [],
|
|
70
|
+
},
|
|
71
|
+
permissionSkipArgs: ["--yolo"],
|
|
72
|
+
};
|
|
73
|
+
|
|
41
74
|
/**
|
|
42
75
|
* すべてのビルトインツール
|
|
43
76
|
*/
|
|
44
|
-
export const BUILTIN_TOOLS: CustomAITool[] = [
|
|
77
|
+
export const BUILTIN_TOOLS: CustomAITool[] = [
|
|
78
|
+
CLAUDE_CODE_TOOL,
|
|
79
|
+
CODEX_CLI_TOOL,
|
|
80
|
+
GEMINI_CLI_TOOL,
|
|
81
|
+
QWEN_CLI_TOOL,
|
|
82
|
+
];
|
package/src/config/index.ts
CHANGED
|
@@ -88,12 +88,7 @@ export function resetConfigCache(): void {
|
|
|
88
88
|
* セッションデータの保存・読み込み
|
|
89
89
|
*/
|
|
90
90
|
function getSessionFilePath(repositoryRoot: string): string {
|
|
91
|
-
const sessionDir = path.join(
|
|
92
|
-
homedir(),
|
|
93
|
-
".config",
|
|
94
|
-
"gwt",
|
|
95
|
-
"sessions",
|
|
96
|
-
);
|
|
91
|
+
const sessionDir = path.join(homedir(), ".config", "gwt", "sessions");
|
|
97
92
|
const repoName = path.basename(repositoryRoot);
|
|
98
93
|
const repoHash = Buffer.from(repositoryRoot)
|
|
99
94
|
.toString("base64")
|
|
@@ -152,12 +147,7 @@ export async function loadSession(
|
|
|
152
147
|
|
|
153
148
|
export async function getAllSessions(): Promise<SessionData[]> {
|
|
154
149
|
try {
|
|
155
|
-
const sessionDir = path.join(
|
|
156
|
-
homedir(),
|
|
157
|
-
".config",
|
|
158
|
-
"gwt",
|
|
159
|
-
"sessions",
|
|
160
|
-
);
|
|
150
|
+
const sessionDir = path.join(homedir(), ".config", "gwt", "sessions");
|
|
161
151
|
const { readdir } = await import("node:fs/promises");
|
|
162
152
|
|
|
163
153
|
const files = await readdir(sessionDir);
|
package/src/config/tools.ts
CHANGED
|
@@ -7,7 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import path from "node:path";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
readFile,
|
|
12
|
+
writeFile,
|
|
13
|
+
mkdir,
|
|
14
|
+
rename,
|
|
15
|
+
access,
|
|
16
|
+
cp,
|
|
17
|
+
} from "node:fs/promises";
|
|
11
18
|
import type {
|
|
12
19
|
ToolsConfig,
|
|
13
20
|
CustomAITool,
|
|
@@ -20,11 +27,12 @@ import { BUILTIN_TOOLS } from "./builtin-tools.js";
|
|
|
20
27
|
* 環境変数の優先順位: GWT_HOME > CLAUDE_WORKTREE_HOME (後方互換性) > ホームディレクトリ
|
|
21
28
|
*/
|
|
22
29
|
export const WORKTREE_HOME =
|
|
23
|
-
|
|
30
|
+
process.env.GWT_HOME && process.env.GWT_HOME.trim().length > 0
|
|
24
31
|
? process.env.GWT_HOME
|
|
25
|
-
:
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
: process.env.CLAUDE_WORKTREE_HOME &&
|
|
33
|
+
process.env.CLAUDE_WORKTREE_HOME.trim().length > 0
|
|
34
|
+
? process.env.CLAUDE_WORKTREE_HOME
|
|
35
|
+
: homedir();
|
|
28
36
|
|
|
29
37
|
const LEGACY_CONFIG_DIR = path.join(homedir(), ".claude-worktree");
|
|
30
38
|
export const CONFIG_DIR = path.join(WORKTREE_HOME, ".gwt");
|
|
@@ -55,7 +63,9 @@ async function migrateLegacyConfig(): Promise<void> {
|
|
|
55
63
|
// レガシーディレクトリを新しいディレクトリにコピー
|
|
56
64
|
await mkdir(path.dirname(CONFIG_DIR), { recursive: true });
|
|
57
65
|
await cp(LEGACY_CONFIG_DIR, CONFIG_DIR, { recursive: true });
|
|
58
|
-
console.log(
|
|
66
|
+
console.log(
|
|
67
|
+
`✅ Migrated configuration from ${LEGACY_CONFIG_DIR} to ${CONFIG_DIR}`,
|
|
68
|
+
);
|
|
59
69
|
} catch (error) {
|
|
60
70
|
// 移行に失敗しても継続(エラーログのみ)
|
|
61
71
|
if (process.env.DEBUG) {
|
package/src/gemini.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { createChildStdio, getTerminalStreams } from "./utils/terminal.js";
|
|
5
|
+
|
|
6
|
+
const GEMINI_CLI_PACKAGE = "@google/gemini-cli@latest";
|
|
7
|
+
|
|
8
|
+
export class GeminiError extends Error {
|
|
9
|
+
constructor(
|
|
10
|
+
message: string,
|
|
11
|
+
public cause?: unknown,
|
|
12
|
+
) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "GeminiError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function launchGeminiCLI(
|
|
19
|
+
worktreePath: string,
|
|
20
|
+
options: {
|
|
21
|
+
skipPermissions?: boolean;
|
|
22
|
+
mode?: "normal" | "continue" | "resume";
|
|
23
|
+
extraArgs?: string[];
|
|
24
|
+
envOverrides?: Record<string, string>;
|
|
25
|
+
model?: string;
|
|
26
|
+
} = {},
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
const terminal = getTerminalStreams();
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Check if the worktree path exists
|
|
32
|
+
if (!existsSync(worktreePath)) {
|
|
33
|
+
throw new Error(`Worktree path does not exist: ${worktreePath}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(chalk.blue("🚀 Launching Gemini CLI..."));
|
|
37
|
+
console.log(chalk.gray(` Working directory: ${worktreePath}`));
|
|
38
|
+
|
|
39
|
+
const args: string[] = [];
|
|
40
|
+
|
|
41
|
+
if (options.model) {
|
|
42
|
+
args.push("--model", options.model);
|
|
43
|
+
console.log(chalk.green(` 🎯 Model: ${options.model}`));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Handle execution mode
|
|
47
|
+
switch (options.mode) {
|
|
48
|
+
case "continue":
|
|
49
|
+
args.push("-r", "latest");
|
|
50
|
+
console.log(chalk.cyan(" ⏭️ Continuing most recent session"));
|
|
51
|
+
break;
|
|
52
|
+
case "resume":
|
|
53
|
+
args.push("-r", "latest");
|
|
54
|
+
console.log(chalk.cyan(" 🔄 Resuming session"));
|
|
55
|
+
break;
|
|
56
|
+
case "normal":
|
|
57
|
+
default:
|
|
58
|
+
console.log(chalk.green(" ✨ Starting new session"));
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle skip permissions (YOLO mode)
|
|
63
|
+
if (options.skipPermissions) {
|
|
64
|
+
args.push("-y");
|
|
65
|
+
console.log(
|
|
66
|
+
chalk.yellow(" ⚠️ Auto-approving all actions (YOLO mode)"),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Append any pass-through arguments after our flags
|
|
71
|
+
if (options.extraArgs && options.extraArgs.length > 0) {
|
|
72
|
+
args.push(...options.extraArgs);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
terminal.exitRawMode();
|
|
76
|
+
|
|
77
|
+
const baseEnv = {
|
|
78
|
+
...process.env,
|
|
79
|
+
...(options.envOverrides ?? {}),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const childStdio = createChildStdio();
|
|
83
|
+
|
|
84
|
+
// Auto-detect locally installed gemini command
|
|
85
|
+
const hasLocalGemini = await isGeminiCommandAvailable();
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
if (hasLocalGemini) {
|
|
89
|
+
// Use locally installed gemini command
|
|
90
|
+
console.log(
|
|
91
|
+
chalk.green(" ✨ Using locally installed gemini command"),
|
|
92
|
+
);
|
|
93
|
+
await execa("gemini", args, {
|
|
94
|
+
cwd: worktreePath,
|
|
95
|
+
shell: true,
|
|
96
|
+
stdin: childStdio.stdin,
|
|
97
|
+
stdout: childStdio.stdout,
|
|
98
|
+
stderr: childStdio.stderr,
|
|
99
|
+
env: baseEnv,
|
|
100
|
+
} as any);
|
|
101
|
+
} else {
|
|
102
|
+
// Fallback to bunx
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.cyan(" 🔄 Falling back to bunx @google/gemini-cli@latest"),
|
|
105
|
+
);
|
|
106
|
+
console.log(
|
|
107
|
+
chalk.yellow(
|
|
108
|
+
" 💡 Recommended: Install Gemini CLI globally for faster startup",
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
console.log(chalk.yellow(" npm install -g @google/gemini-cli"));
|
|
112
|
+
console.log("");
|
|
113
|
+
// Wait 2 seconds to let user read the message
|
|
114
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
115
|
+
await execa("bunx", [GEMINI_CLI_PACKAGE, ...args], {
|
|
116
|
+
cwd: worktreePath,
|
|
117
|
+
shell: true,
|
|
118
|
+
stdin: childStdio.stdin,
|
|
119
|
+
stdout: childStdio.stdout,
|
|
120
|
+
stderr: childStdio.stderr,
|
|
121
|
+
env: baseEnv,
|
|
122
|
+
} as any);
|
|
123
|
+
}
|
|
124
|
+
} finally {
|
|
125
|
+
childStdio.cleanup();
|
|
126
|
+
}
|
|
127
|
+
} catch (error: any) {
|
|
128
|
+
const hasLocalGemini = await isGeminiCommandAvailable();
|
|
129
|
+
let errorMessage: string;
|
|
130
|
+
|
|
131
|
+
if (error.code === "ENOENT") {
|
|
132
|
+
if (hasLocalGemini) {
|
|
133
|
+
errorMessage =
|
|
134
|
+
"gemini command not found. Please ensure Gemini CLI is properly installed.";
|
|
135
|
+
} else {
|
|
136
|
+
errorMessage =
|
|
137
|
+
"bunx command not found. Please ensure Bun is installed so Gemini CLI can run via bunx.";
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
errorMessage = `Failed to launch Gemini CLI: ${error.message || "Unknown error"}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (process.platform === "win32") {
|
|
144
|
+
console.error(chalk.red("\n💡 Windows troubleshooting tips:"));
|
|
145
|
+
if (hasLocalGemini) {
|
|
146
|
+
console.error(
|
|
147
|
+
chalk.yellow(
|
|
148
|
+
" 1. Confirm that Gemini CLI is installed and the 'gemini' command is on PATH",
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
console.error(
|
|
152
|
+
chalk.yellow(' 2. Run "gemini --version" to verify the setup'),
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
console.error(
|
|
156
|
+
chalk.yellow(
|
|
157
|
+
" 1. Confirm that Bun is installed and bunx is available",
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
console.error(
|
|
161
|
+
chalk.yellow(
|
|
162
|
+
' 2. Run "bunx @google/gemini-cli@latest -- --version" to verify the setup',
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
console.error(
|
|
167
|
+
chalk.yellow(" 3. Restart your terminal or IDE to refresh PATH"),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
throw new GeminiError(errorMessage, error);
|
|
172
|
+
} finally {
|
|
173
|
+
terminal.exitRawMode();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check if locally installed `gemini` command is available
|
|
179
|
+
* @returns true if `gemini` command exists in PATH, false otherwise
|
|
180
|
+
*/
|
|
181
|
+
async function isGeminiCommandAvailable(): Promise<boolean> {
|
|
182
|
+
try {
|
|
183
|
+
const command = process.platform === "win32" ? "where" : "which";
|
|
184
|
+
await execa(command, ["gemini"], { shell: true });
|
|
185
|
+
return true;
|
|
186
|
+
} catch {
|
|
187
|
+
// gemini command not found in PATH
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export async function isGeminiCLIAvailable(): Promise<boolean> {
|
|
193
|
+
try {
|
|
194
|
+
await execa("bunx", [GEMINI_CLI_PACKAGE, "--version"], { shell: true });
|
|
195
|
+
return true;
|
|
196
|
+
} catch (error: any) {
|
|
197
|
+
if (error.code === "ENOENT") {
|
|
198
|
+
console.error(chalk.yellow("\n⚠️ bunx command not found"));
|
|
199
|
+
console.error(
|
|
200
|
+
chalk.gray(
|
|
201
|
+
" Install Bun and confirm that bunx is available before continuing",
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
package/src/git.ts
CHANGED
|
@@ -351,7 +351,8 @@ function getBranchType(branchName: string): BranchInfo["branchType"] {
|
|
|
351
351
|
if (branchName === "main" || branchName === "master") return "main";
|
|
352
352
|
if (branchName === "develop" || branchName === "dev") return "develop";
|
|
353
353
|
if (branchName.startsWith("feature/")) return "feature";
|
|
354
|
-
if (branchName.startsWith("bugfix/") || branchName.startsWith("bug/"))
|
|
354
|
+
if (branchName.startsWith("bugfix/") || branchName.startsWith("bug/"))
|
|
355
|
+
return "bugfix";
|
|
355
356
|
if (branchName.startsWith("hotfix/")) return "hotfix";
|
|
356
357
|
if (branchName.startsWith("release/")) return "release";
|
|
357
358
|
return "other";
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
GitError,
|
|
11
11
|
} from "./git.js";
|
|
12
12
|
import { launchClaudeCode } from "./claude.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
launchCodexCLI,
|
|
15
|
+
CodexError,
|
|
16
|
+
type CodexReasoningEffort,
|
|
17
|
+
} from "./codex.js";
|
|
18
|
+
import { launchGeminiCLI, GeminiError } from "./gemini.js";
|
|
19
|
+
import { launchQwenCLI, QwenError } from "./qwen.js";
|
|
14
20
|
import {
|
|
15
21
|
WorktreeOrchestrator,
|
|
16
22
|
type EnsureWorktreeOptions,
|
|
@@ -96,6 +102,8 @@ function isRecoverableError(error: unknown): boolean {
|
|
|
96
102
|
error instanceof GitError ||
|
|
97
103
|
error instanceof WorktreeError ||
|
|
98
104
|
error instanceof CodexError ||
|
|
105
|
+
error instanceof GeminiError ||
|
|
106
|
+
error instanceof QwenError ||
|
|
99
107
|
error instanceof DependencyInstallError
|
|
100
108
|
) {
|
|
101
109
|
return true;
|
|
@@ -106,6 +114,8 @@ function isRecoverableError(error: unknown): boolean {
|
|
|
106
114
|
error.name === "GitError" ||
|
|
107
115
|
error.name === "WorktreeError" ||
|
|
108
116
|
error.name === "CodexError" ||
|
|
117
|
+
error.name === "GeminiError" ||
|
|
118
|
+
error.name === "QwenError" ||
|
|
109
119
|
error.name === "DependencyInstallError"
|
|
110
120
|
);
|
|
111
121
|
}
|
|
@@ -119,6 +129,8 @@ function isRecoverableError(error: unknown): boolean {
|
|
|
119
129
|
name === "GitError" ||
|
|
120
130
|
name === "WorktreeError" ||
|
|
121
131
|
name === "CodexError" ||
|
|
132
|
+
name === "GeminiError" ||
|
|
133
|
+
name === "QwenError" ||
|
|
122
134
|
name === "DependencyInstallError"
|
|
123
135
|
);
|
|
124
136
|
}
|
|
@@ -284,11 +296,17 @@ export async function handleAIToolWorkflow(
|
|
|
284
296
|
tool,
|
|
285
297
|
mode,
|
|
286
298
|
skipPermissions,
|
|
299
|
+
model,
|
|
300
|
+
inferenceLevel,
|
|
287
301
|
} = selectionResult;
|
|
288
302
|
|
|
289
303
|
const branchLabel = displayName ?? branch;
|
|
304
|
+
const modelInfo =
|
|
305
|
+
model || inferenceLevel
|
|
306
|
+
? `, model=${model ?? "default"}${inferenceLevel ? `/${inferenceLevel}` : ""}`
|
|
307
|
+
: "";
|
|
290
308
|
printInfo(
|
|
291
|
-
`Selected: ${branchLabel} with ${tool} (${mode} mode, skipPermissions: ${skipPermissions})`,
|
|
309
|
+
`Selected: ${branchLabel} with ${tool} (${mode} mode${modelInfo}, skipPermissions: ${skipPermissions})`,
|
|
292
310
|
);
|
|
293
311
|
|
|
294
312
|
try {
|
|
@@ -520,7 +538,12 @@ export async function handleAIToolWorkflow(
|
|
|
520
538
|
// Builtin tools use their dedicated launch functions
|
|
521
539
|
// Custom tools use the generic launchCustomAITool function
|
|
522
540
|
if (tool === "claude-code") {
|
|
523
|
-
|
|
541
|
+
const launchOptions: {
|
|
542
|
+
mode?: "normal" | "continue" | "resume";
|
|
543
|
+
skipPermissions?: boolean;
|
|
544
|
+
envOverrides?: Record<string, string>;
|
|
545
|
+
model?: string;
|
|
546
|
+
} = {
|
|
524
547
|
mode:
|
|
525
548
|
mode === "resume"
|
|
526
549
|
? "resume"
|
|
@@ -529,9 +552,19 @@ export async function handleAIToolWorkflow(
|
|
|
529
552
|
: "normal",
|
|
530
553
|
skipPermissions,
|
|
531
554
|
envOverrides: sharedEnv,
|
|
532
|
-
}
|
|
555
|
+
};
|
|
556
|
+
if (model) {
|
|
557
|
+
launchOptions.model = model;
|
|
558
|
+
}
|
|
559
|
+
await launchClaudeCode(worktreePath, launchOptions);
|
|
533
560
|
} else if (tool === "codex-cli") {
|
|
534
|
-
|
|
561
|
+
const launchOptions: {
|
|
562
|
+
mode?: "normal" | "continue" | "resume";
|
|
563
|
+
bypassApprovals?: boolean;
|
|
564
|
+
envOverrides?: Record<string, string>;
|
|
565
|
+
model?: string;
|
|
566
|
+
reasoningEffort?: CodexReasoningEffort;
|
|
567
|
+
} = {
|
|
535
568
|
mode:
|
|
536
569
|
mode === "resume"
|
|
537
570
|
? "resume"
|
|
@@ -540,7 +573,54 @@ export async function handleAIToolWorkflow(
|
|
|
540
573
|
: "normal",
|
|
541
574
|
bypassApprovals: skipPermissions,
|
|
542
575
|
envOverrides: sharedEnv,
|
|
543
|
-
}
|
|
576
|
+
};
|
|
577
|
+
if (model) {
|
|
578
|
+
launchOptions.model = model;
|
|
579
|
+
}
|
|
580
|
+
if (inferenceLevel) {
|
|
581
|
+
launchOptions.reasoningEffort = inferenceLevel as CodexReasoningEffort;
|
|
582
|
+
}
|
|
583
|
+
await launchCodexCLI(worktreePath, launchOptions);
|
|
584
|
+
} else if (tool === "gemini-cli") {
|
|
585
|
+
const launchOptions: {
|
|
586
|
+
mode?: "normal" | "continue" | "resume";
|
|
587
|
+
skipPermissions?: boolean;
|
|
588
|
+
envOverrides?: Record<string, string>;
|
|
589
|
+
model?: string;
|
|
590
|
+
} = {
|
|
591
|
+
mode:
|
|
592
|
+
mode === "resume"
|
|
593
|
+
? "resume"
|
|
594
|
+
: mode === "continue"
|
|
595
|
+
? "continue"
|
|
596
|
+
: "normal",
|
|
597
|
+
skipPermissions,
|
|
598
|
+
envOverrides: sharedEnv,
|
|
599
|
+
};
|
|
600
|
+
if (model) {
|
|
601
|
+
launchOptions.model = model;
|
|
602
|
+
}
|
|
603
|
+
await launchGeminiCLI(worktreePath, launchOptions);
|
|
604
|
+
} else if (tool === "qwen-cli") {
|
|
605
|
+
const launchOptions: {
|
|
606
|
+
mode?: "normal" | "continue" | "resume";
|
|
607
|
+
skipPermissions?: boolean;
|
|
608
|
+
envOverrides?: Record<string, string>;
|
|
609
|
+
model?: string;
|
|
610
|
+
} = {
|
|
611
|
+
mode:
|
|
612
|
+
mode === "resume"
|
|
613
|
+
? "resume"
|
|
614
|
+
: mode === "continue"
|
|
615
|
+
? "continue"
|
|
616
|
+
: "normal",
|
|
617
|
+
skipPermissions,
|
|
618
|
+
envOverrides: sharedEnv,
|
|
619
|
+
};
|
|
620
|
+
if (model) {
|
|
621
|
+
launchOptions.model = model;
|
|
622
|
+
}
|
|
623
|
+
await launchQwenCLI(worktreePath, launchOptions);
|
|
544
624
|
} else {
|
|
545
625
|
// Custom tool
|
|
546
626
|
printInfo(`Launching custom tool: ${toolConfig.displayName}`);
|