@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/claude.ts
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import { execa } from "execa";
|
|
1
|
+
import { execa, type Options as ExecaOptions } 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 { findLatestClaudeSession } from "./utils/session.js";
|
|
6
11
|
|
|
7
12
|
const CLAUDE_CLI_PACKAGE = "@anthropic-ai/claude-code@latest";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Error wrapper used by `launchClaudeCode` to preserve the original failure
|
|
16
|
+
* while providing a user-friendly message.
|
|
17
|
+
*/
|
|
8
18
|
export class ClaudeError extends Error {
|
|
9
19
|
constructor(
|
|
10
20
|
message: string,
|
|
@@ -15,6 +25,20 @@ export class ClaudeError extends Error {
|
|
|
15
25
|
}
|
|
16
26
|
}
|
|
17
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Launches Claude Code in the given worktree path.
|
|
30
|
+
*
|
|
31
|
+
* This function:
|
|
32
|
+
* - validates the worktree path
|
|
33
|
+
* - normalizes launch arguments (mode/model/session/extra args)
|
|
34
|
+
* - resets terminal modes before and after the child process
|
|
35
|
+
* - auto-detects a local `claude` command and falls back to `npx` (Windows) or
|
|
36
|
+
* `bunx` when needed
|
|
37
|
+
*
|
|
38
|
+
* @param worktreePath - Worktree directory to run Claude Code in
|
|
39
|
+
* @param options - Launch options (mode/session/model/permissions/env)
|
|
40
|
+
* @returns Captured session id when available
|
|
41
|
+
*/
|
|
18
42
|
export async function launchClaudeCode(
|
|
19
43
|
worktreePath: string,
|
|
20
44
|
options: {
|
|
@@ -175,6 +199,7 @@ export async function launchClaudeCode(
|
|
|
175
199
|
console.log(chalk.gray(` 📋 Args: ${args.join(" ")}`));
|
|
176
200
|
|
|
177
201
|
terminal.exitRawMode();
|
|
202
|
+
resetTerminalModes(terminal.stdout);
|
|
178
203
|
|
|
179
204
|
const baseEnv = { ...process.env, ...(options.envOverrides ?? {}) };
|
|
180
205
|
const launchEnvSource =
|
|
@@ -191,26 +216,58 @@ export async function launchClaudeCode(
|
|
|
191
216
|
|
|
192
217
|
// Auto-detect locally installed claude command
|
|
193
218
|
const hasLocalClaude = await isClaudeCommandAvailable();
|
|
219
|
+
const hasNpx =
|
|
220
|
+
process.platform === "win32" ? await isNpxCommandAvailable() : false;
|
|
221
|
+
|
|
222
|
+
const execInteractive = async (
|
|
223
|
+
file: string,
|
|
224
|
+
fileArgs: string[],
|
|
225
|
+
execOptions: Omit<ExecaOptions, "shell">,
|
|
226
|
+
) => {
|
|
227
|
+
if (process.platform !== "win32") {
|
|
228
|
+
await execa(file, fileArgs, { ...execOptions, shell: true });
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
await execa(file, fileArgs, { ...execOptions, shell: false });
|
|
234
|
+
return;
|
|
235
|
+
} catch (error: unknown) {
|
|
236
|
+
const err = error as NodeJS.ErrnoException;
|
|
237
|
+
if (err?.code === "ENOENT" || err?.code === "EINVAL") {
|
|
238
|
+
await execa(file, fileArgs, { ...execOptions, shell: true });
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
194
244
|
|
|
195
245
|
try {
|
|
196
246
|
if (hasLocalClaude) {
|
|
197
247
|
console.log(
|
|
198
248
|
chalk.green(" ✨ Using locally installed claude command"),
|
|
199
249
|
);
|
|
200
|
-
await
|
|
250
|
+
await execInteractive("claude", args, {
|
|
201
251
|
cwd: worktreePath,
|
|
202
|
-
shell: true,
|
|
203
252
|
stdin: childStdio.stdin,
|
|
204
253
|
stdout: childStdio.stdout,
|
|
205
254
|
stderr: childStdio.stderr,
|
|
206
255
|
env: launchEnv,
|
|
207
256
|
});
|
|
208
257
|
} else {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
258
|
+
if (hasNpx) {
|
|
259
|
+
console.log(
|
|
260
|
+
chalk.cyan(
|
|
261
|
+
" 🔄 Falling back to npx @anthropic-ai/claude-code@latest",
|
|
262
|
+
),
|
|
263
|
+
);
|
|
264
|
+
} else {
|
|
265
|
+
console.log(
|
|
266
|
+
chalk.cyan(
|
|
267
|
+
" 🔄 Falling back to bunx @anthropic-ai/claude-code@latest",
|
|
268
|
+
),
|
|
269
|
+
);
|
|
270
|
+
}
|
|
214
271
|
console.log(
|
|
215
272
|
chalk.yellow(
|
|
216
273
|
" 💡 Recommended: Install Claude Code via official method for faster startup",
|
|
@@ -231,14 +288,23 @@ export async function launchClaudeCode(
|
|
|
231
288
|
);
|
|
232
289
|
console.log("");
|
|
233
290
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
291
|
+
if (hasNpx) {
|
|
292
|
+
await execInteractive("npx", ["-y", CLAUDE_CLI_PACKAGE, ...args], {
|
|
293
|
+
cwd: worktreePath,
|
|
294
|
+
stdin: childStdio.stdin,
|
|
295
|
+
stdout: childStdio.stdout,
|
|
296
|
+
stderr: childStdio.stderr,
|
|
297
|
+
env: launchEnv,
|
|
298
|
+
});
|
|
299
|
+
} else {
|
|
300
|
+
await execInteractive("bunx", [CLAUDE_CLI_PACKAGE, ...args], {
|
|
301
|
+
cwd: worktreePath,
|
|
302
|
+
stdin: childStdio.stdin,
|
|
303
|
+
stdout: childStdio.stdout,
|
|
304
|
+
stderr: childStdio.stderr,
|
|
305
|
+
env: launchEnv,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
242
308
|
}
|
|
243
309
|
} finally {
|
|
244
310
|
childStdio.cleanup();
|
|
@@ -327,29 +393,21 @@ export async function launchClaudeCode(
|
|
|
327
393
|
throw new ClaudeError(errorMessage, error);
|
|
328
394
|
} finally {
|
|
329
395
|
terminal.exitRawMode();
|
|
396
|
+
resetTerminalModes(terminal.stdout);
|
|
330
397
|
}
|
|
331
398
|
}
|
|
332
399
|
|
|
333
|
-
/**
|
|
334
|
-
* Check if locally installed `claude` command is available
|
|
335
|
-
* @returns true if `claude` command exists in PATH, false otherwise
|
|
336
|
-
*/
|
|
337
400
|
async function isClaudeCommandAvailable(): Promise<boolean> {
|
|
338
|
-
|
|
339
|
-
const command = process.platform === "win32" ? "where" : "which";
|
|
340
|
-
await execa(command, ["claude"], {
|
|
341
|
-
shell: true,
|
|
342
|
-
stdin: "ignore",
|
|
343
|
-
stdout: "ignore",
|
|
344
|
-
stderr: "ignore",
|
|
345
|
-
});
|
|
346
|
-
return true;
|
|
347
|
-
} catch {
|
|
348
|
-
// claude command not found in PATH
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
401
|
+
return isCommandAvailable("claude");
|
|
351
402
|
}
|
|
352
403
|
|
|
404
|
+
async function isNpxCommandAvailable(): Promise<boolean> {
|
|
405
|
+
return isCommandAvailable("npx");
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Checks whether Claude Code is available via `bunx` in the current environment.
|
|
410
|
+
*/
|
|
353
411
|
export async function isClaudeCodeAvailable(): Promise<boolean> {
|
|
354
412
|
try {
|
|
355
413
|
await execa("bunx", [CLAUDE_CLI_PACKAGE, "--version"], { shell: true });
|
|
@@ -204,6 +204,8 @@ describe("App protected branch handling", () => {
|
|
|
204
204
|
});
|
|
205
205
|
|
|
206
206
|
expect(navigateToMock).toHaveBeenCalledWith("branch-action-selector");
|
|
207
|
-
|
|
207
|
+
// When branchQuickStart is empty, navigates to ai-tool-selector instead of branch-quick-start
|
|
208
|
+
// So AIToolSelectorScreen should be rendered, not BranchQuickStartScreen
|
|
209
|
+
expect(aiToolProps).not.toHaveLength(0);
|
|
208
210
|
});
|
|
209
211
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import React from "react";
|
|
5
5
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
6
|
-
import { render, waitFor
|
|
6
|
+
import { render, waitFor } from "@testing-library/react";
|
|
7
7
|
import { ModelSelectorScreen } from "../../components/screens/ModelSelectorScreen.js";
|
|
8
8
|
import type { ModelSelectionResult } from "../../components/screens/ModelSelectorScreen.js";
|
|
9
9
|
import { Window } from "happy-dom";
|
|
@@ -55,8 +55,9 @@ describe("ModelSelectorScreen initial selection", () => {
|
|
|
55
55
|
await waitFor(() => expect(selectMocks.length).toBeGreaterThan(0));
|
|
56
56
|
const modelSelect = selectMocks.at(-1);
|
|
57
57
|
const index = modelSelect.initialIndex as number;
|
|
58
|
-
// codex-cli models: [gpt-5.1-codex, gpt-5.2, gpt-5.1-codex-max, gpt-5.1-codex-mini, gpt-5.1]
|
|
59
|
-
|
|
58
|
+
// codex-cli models: ["", gpt-5.1-codex, gpt-5.2, gpt-5.1-codex-max, gpt-5.1-codex-mini, gpt-5.1]
|
|
59
|
+
// (index 0 = Default/Auto, index 3 = gpt-5.1-codex-max)
|
|
60
|
+
expect(index).toBe(3);
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
it("includes gpt-5.2 in model options and preserves ordering", async () => {
|
|
@@ -75,9 +76,11 @@ describe("ModelSelectorScreen initial selection", () => {
|
|
|
75
76
|
);
|
|
76
77
|
|
|
77
78
|
await waitFor(() => expect(selectMocks.length).toBeGreaterThan(1));
|
|
78
|
-
const modelSelect = selectMocks.at(-1)
|
|
79
|
-
|
|
79
|
+
const modelSelect = selectMocks.at(-1);
|
|
80
|
+
expect(modelSelect).toBeDefined();
|
|
81
|
+
const ids = modelSelect?.items.map((item) => item.value);
|
|
80
82
|
expect(ids).toEqual([
|
|
83
|
+
"", // Default (Auto)
|
|
81
84
|
"gpt-5.1-codex",
|
|
82
85
|
"gpt-5.2",
|
|
83
86
|
"gpt-5.1-codex-max",
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
|
-
import { Box, Text,
|
|
2
|
+
import { Box, Text, useStdout } from "ink";
|
|
3
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Item descriptor for the `Select` component.
|
|
7
|
+
*/
|
|
4
8
|
export interface SelectItem {
|
|
5
9
|
label: string;
|
|
6
10
|
value: string;
|
|
7
11
|
}
|
|
8
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Props for the `Select` component.
|
|
15
|
+
*/
|
|
9
16
|
export interface SelectProps<T extends SelectItem = SelectItem> {
|
|
10
17
|
items: T[];
|
|
11
18
|
onSelect: (item: T) => void;
|
|
@@ -133,7 +140,7 @@ const SelectComponent = <T extends SelectItem = SelectItem>({
|
|
|
133
140
|
}
|
|
134
141
|
}, [items, limit]);
|
|
135
142
|
|
|
136
|
-
|
|
143
|
+
useAppInput((input, key) => {
|
|
137
144
|
if (disabled) {
|
|
138
145
|
return;
|
|
139
146
|
}
|
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Header } from "../parts/Header.js";
|
|
4
4
|
import { Footer } from "../parts/Footer.js";
|
|
5
5
|
import { Select } from "../common/Select.js";
|
|
6
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
6
7
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
8
|
import { getAllTools } from "../../../../config/tools.js";
|
|
8
9
|
import type { AIToolConfig } from "../../../../types/tools.js";
|
|
9
10
|
import type { AITool } from "../../types.js";
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Renderable item for the AI tool selector list.
|
|
14
|
+
*/
|
|
11
15
|
export interface AIToolItem {
|
|
12
16
|
label: string;
|
|
13
17
|
value: AITool;
|
|
14
18
|
description: string;
|
|
15
19
|
}
|
|
16
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Props for `AIToolSelectorScreen`.
|
|
23
|
+
*/
|
|
17
24
|
export interface AIToolSelectorScreenProps {
|
|
18
25
|
onBack: () => void;
|
|
19
26
|
onSelect: (tool: AITool) => void;
|
|
@@ -96,7 +103,7 @@ export function AIToolSelectorScreen({
|
|
|
96
103
|
|
|
97
104
|
// Handle keyboard input
|
|
98
105
|
// Note: Select component handles Enter and arrow keys
|
|
99
|
-
|
|
106
|
+
useAppInput((input, key) => {
|
|
100
107
|
if (key.escape) {
|
|
101
108
|
onBack();
|
|
102
109
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Header } from "../parts/Header.js";
|
|
4
4
|
import { Footer } from "../parts/Footer.js";
|
|
5
5
|
import { ProgressBar } from "../parts/ProgressBar.js";
|
|
6
6
|
import { MergeStatusList } from "../parts/MergeStatusList.js";
|
|
7
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
7
8
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
8
9
|
import type { BatchMergeProgress, BranchMergeStatus } from "../../types.js";
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Props for `BatchMergeProgressScreen`.
|
|
13
|
+
*/
|
|
10
14
|
export interface BatchMergeProgressScreenProps {
|
|
11
15
|
progress: BatchMergeProgress;
|
|
12
16
|
statuses: BranchMergeStatus[];
|
|
@@ -26,7 +30,7 @@ export function BatchMergeProgressScreen({
|
|
|
26
30
|
const { rows } = useTerminalSize();
|
|
27
31
|
|
|
28
32
|
// Handle keyboard input
|
|
29
|
-
|
|
33
|
+
useAppInput((input, key) => {
|
|
30
34
|
if ((input === "q" || key.escape) && onCancel) {
|
|
31
35
|
onCancel();
|
|
32
36
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Header } from "../parts/Header.js";
|
|
4
4
|
import { Footer } from "../parts/Footer.js";
|
|
5
5
|
import { MergeStatusList } from "../parts/MergeStatusList.js";
|
|
6
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
6
7
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
8
|
import type { BatchMergeResult } from "../../types.js";
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Props for `BatchMergeResultScreen`.
|
|
12
|
+
*/
|
|
9
13
|
export interface BatchMergeResultScreenProps {
|
|
10
14
|
result: BatchMergeResult;
|
|
11
15
|
onBack?: () => void;
|
|
@@ -25,7 +29,7 @@ export function BatchMergeResultScreen({
|
|
|
25
29
|
const { rows } = useTerminalSize();
|
|
26
30
|
|
|
27
31
|
// Handle keyboard input
|
|
28
|
-
|
|
32
|
+
useAppInput((input, key) => {
|
|
29
33
|
if (input === "q" && onQuit) {
|
|
30
34
|
onQuit();
|
|
31
35
|
} else if (key.escape && onBack) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useState, useCallback, useEffect, useRef } from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Header } from "../parts/Header.js";
|
|
4
4
|
import { Footer } from "../parts/Footer.js";
|
|
5
5
|
import { Select } from "../common/Select.js";
|
|
6
6
|
import { Input } from "../common/Input.js";
|
|
7
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
7
8
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
8
9
|
import { BRANCH_PREFIXES } from "../../../../config/constants.js";
|
|
9
10
|
|
|
@@ -12,6 +13,9 @@ type Step = "type-selection" | "name-input";
|
|
|
12
13
|
|
|
13
14
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧"];
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Props for `BranchCreatorScreen`.
|
|
18
|
+
*/
|
|
15
19
|
export interface BranchCreatorScreenProps {
|
|
16
20
|
onBack: () => void;
|
|
17
21
|
onCreate: (branchName: string) => Promise<void>;
|
|
@@ -52,7 +56,7 @@ export function BranchCreatorScreen({
|
|
|
52
56
|
const spinnerFrame = SPINNER_FRAMES[spinnerIndex] ?? SPINNER_FRAMES[0];
|
|
53
57
|
|
|
54
58
|
// Handle keyboard input for back navigation
|
|
55
|
-
|
|
59
|
+
useAppInput((input, key) => {
|
|
56
60
|
if (isCreating) {
|
|
57
61
|
return;
|
|
58
62
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { useCallback, useState, useMemo, useEffect } from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Header } from "../parts/Header.js";
|
|
4
4
|
import { Stats } from "../parts/Stats.js";
|
|
5
5
|
import { Footer } from "../parts/Footer.js";
|
|
6
6
|
import { Select } from "../common/Select.js";
|
|
7
7
|
import { Input } from "../common/Input.js";
|
|
8
8
|
import { LoadingIndicator } from "../common/LoadingIndicator.js";
|
|
9
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
9
10
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
10
11
|
import type { BranchItem, Statistics } from "../../types.js";
|
|
11
12
|
import stringWidth from "string-width";
|
|
@@ -77,6 +78,9 @@ interface CleanupUIState {
|
|
|
77
78
|
inputLocked: boolean;
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Props for `BranchListScreen`.
|
|
83
|
+
*/
|
|
80
84
|
export interface BranchListScreenProps {
|
|
81
85
|
branches: BranchItem[];
|
|
82
86
|
stats: Statistics;
|
|
@@ -166,7 +170,7 @@ export function BranchListScreen({
|
|
|
166
170
|
// Handle keyboard input
|
|
167
171
|
// Note: Input component blocks specific keys (c/r/f) using blockKeys prop
|
|
168
172
|
// This prevents shortcuts from triggering while typing in the filter
|
|
169
|
-
|
|
173
|
+
useAppInput((input, key) => {
|
|
170
174
|
if (cleanupUI?.inputLocked) {
|
|
171
175
|
return;
|
|
172
176
|
}
|
|
@@ -326,8 +330,6 @@ export function BranchListScreen({
|
|
|
326
330
|
return chalk.cyan(label);
|
|
327
331
|
case "gemini-cli":
|
|
328
332
|
return chalk.magenta(label);
|
|
329
|
-
case "qwen-cli":
|
|
330
|
-
return chalk.green(label);
|
|
331
333
|
default: {
|
|
332
334
|
const trimmed = label.trim().toLowerCase();
|
|
333
335
|
if (!toolId || trimmed === "unknown") {
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Header } from "../parts/Header.js";
|
|
4
4
|
import { Footer } from "../parts/Footer.js";
|
|
5
5
|
import { Select, type SelectItem } from "../common/Select.js";
|
|
6
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
6
7
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Action returned by `BranchQuickStartScreen`.
|
|
11
|
+
*/
|
|
8
12
|
export type QuickStartAction = "reuse-continue" | "reuse-new" | "manual";
|
|
9
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Previous tool/session configuration shown in the quick start list.
|
|
16
|
+
*/
|
|
10
17
|
export interface BranchQuickStartOption {
|
|
11
18
|
toolId?: string | null;
|
|
12
19
|
toolLabel: string;
|
|
13
|
-
toolCategory?: "Codex" | "Claude" | "Gemini" | "
|
|
20
|
+
toolCategory?: "Codex" | "Claude" | "Gemini" | "Other";
|
|
14
21
|
model?: string | null;
|
|
15
22
|
sessionId?: string | null;
|
|
16
23
|
inferenceLevel?: string | null;
|
|
@@ -54,6 +61,9 @@ type QuickStartItem = SelectItem & {
|
|
|
54
61
|
categoryColor: "cyan" | "yellow" | "magenta" | "green" | "white";
|
|
55
62
|
};
|
|
56
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Props for `BranchQuickStartScreen`.
|
|
66
|
+
*/
|
|
57
67
|
export interface BranchQuickStartScreenProps {
|
|
58
68
|
previousOptions: BranchQuickStartOption[];
|
|
59
69
|
loading?: boolean;
|
|
@@ -78,7 +88,6 @@ export function BranchQuickStartScreen({
|
|
|
78
88
|
"codex-cli": { label: "Codex", color: "cyan" },
|
|
79
89
|
"claude-code": { label: "Claude", color: "yellow" },
|
|
80
90
|
"gemini-cli": { label: "Gemini", color: "magenta" },
|
|
81
|
-
"qwen-cli": { label: "Qwen", color: "green" },
|
|
82
91
|
other: { label: "Other", color: "white" },
|
|
83
92
|
} as const;
|
|
84
93
|
|
|
@@ -92,8 +101,6 @@ export function BranchQuickStartScreen({
|
|
|
92
101
|
return CATEGORY_META["claude-code"];
|
|
93
102
|
case "gemini-cli":
|
|
94
103
|
return CATEGORY_META["gemini-cli"];
|
|
95
|
-
case "qwen-cli":
|
|
96
|
-
return CATEGORY_META["qwen-cli"];
|
|
97
104
|
default:
|
|
98
105
|
return CATEGORY_META.other;
|
|
99
106
|
}
|
|
@@ -101,7 +108,7 @@ export function BranchQuickStartScreen({
|
|
|
101
108
|
|
|
102
109
|
const items: QuickStartItem[] = previousOptions.length
|
|
103
110
|
? (() => {
|
|
104
|
-
const order = ["Claude", "Codex", "Gemini", "
|
|
111
|
+
const order = ["Claude", "Codex", "Gemini", "Other"];
|
|
105
112
|
const sorted = [...previousOptions].sort((a, b) => {
|
|
106
113
|
const ca = resolveCategory(a.toolId).label;
|
|
107
114
|
const cb = resolveCategory(b.toolId).label;
|
|
@@ -171,7 +178,7 @@ export function BranchQuickStartScreen({
|
|
|
171
178
|
categoryColor: CATEGORY_META.other.color,
|
|
172
179
|
});
|
|
173
180
|
|
|
174
|
-
|
|
181
|
+
useAppInput((_, key) => {
|
|
175
182
|
if (key.escape) {
|
|
176
183
|
onBack();
|
|
177
184
|
}
|
|
@@ -197,27 +204,29 @@ export function BranchQuickStartScreen({
|
|
|
197
204
|
if (item.disabled) return;
|
|
198
205
|
onSelect(item.action, item.toolId ?? null);
|
|
199
206
|
}}
|
|
200
|
-
renderItem={(item: QuickStartItem, isSelected) =>
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<Text
|
|
209
|
-
{
|
|
207
|
+
renderItem={(item: QuickStartItem, isSelected) => {
|
|
208
|
+
return (
|
|
209
|
+
<Box
|
|
210
|
+
flexDirection="column"
|
|
211
|
+
marginTop={
|
|
212
|
+
item.groupStart ? 1 : item.category === "Other" ? 1 : 0
|
|
213
|
+
}
|
|
214
|
+
>
|
|
215
|
+
<Text>
|
|
216
|
+
<Text color={item.categoryColor} inverse={isSelected}>
|
|
217
|
+
{`[${item.category}] `}
|
|
218
|
+
</Text>
|
|
219
|
+
<Text inverse={isSelected}>
|
|
220
|
+
{item.label}
|
|
221
|
+
{item.disabled ? " (disabled)" : ""}
|
|
222
|
+
</Text>
|
|
210
223
|
</Text>
|
|
211
|
-
|
|
212
|
-
{item.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
<Text color="gray"> {item.description}</Text>
|
|
218
|
-
)}
|
|
219
|
-
</Box>
|
|
220
|
-
)}
|
|
224
|
+
{item.description && (
|
|
225
|
+
<Text color="gray"> {item.description}</Text>
|
|
226
|
+
)}
|
|
227
|
+
</Box>
|
|
228
|
+
);
|
|
229
|
+
}}
|
|
221
230
|
/>
|
|
222
231
|
</Box>
|
|
223
232
|
|
|
@@ -6,16 +6,20 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useState, useCallback, useMemo, useEffect } from "react";
|
|
9
|
-
import { Box, Text
|
|
9
|
+
import { Box, Text } from "ink";
|
|
10
10
|
import { Header } from "../parts/Header.js";
|
|
11
11
|
import { Footer } from "../parts/Footer.js";
|
|
12
12
|
import { Select } from "../common/Select.js";
|
|
13
13
|
import { Input } from "../common/Input.js";
|
|
14
14
|
import { Confirm } from "../common/Confirm.js";
|
|
15
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
15
16
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
16
17
|
import { useProfiles } from "../../hooks/useProfiles.js";
|
|
17
18
|
import { isValidProfileName } from "../../../../types/profiles.js";
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Props for `EnvironmentProfileScreen`.
|
|
22
|
+
*/
|
|
19
23
|
export interface EnvironmentProfileScreenProps {
|
|
20
24
|
onBack: () => void;
|
|
21
25
|
version?: string | null;
|
|
@@ -432,7 +436,7 @@ export function EnvironmentProfileScreen({
|
|
|
432
436
|
);
|
|
433
437
|
|
|
434
438
|
// キーボード入力ハンドリング
|
|
435
|
-
|
|
439
|
+
useAppInput(
|
|
436
440
|
(input, key) => {
|
|
437
441
|
// 入力モード時は他のキーハンドリングをスキップ
|
|
438
442
|
if (
|
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { Box, Text
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Header } from "../parts/Header.js";
|
|
4
4
|
import { Footer } from "../parts/Footer.js";
|
|
5
5
|
import { Select } from "../common/Select.js";
|
|
6
|
+
import { useAppInput } from "../../hooks/useAppInput.js";
|
|
6
7
|
import { useTerminalSize } from "../../hooks/useTerminalSize.js";
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Supported execution modes for interactive tools.
|
|
11
|
+
*/
|
|
8
12
|
export type ExecutionMode = "normal" | "continue" | "resume";
|
|
9
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Selectable execution mode item (step 1).
|
|
16
|
+
*/
|
|
10
17
|
export interface ExecutionModeItem {
|
|
11
18
|
label: string;
|
|
12
19
|
value: ExecutionMode;
|
|
13
20
|
description: string;
|
|
14
21
|
}
|
|
15
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Selectable skip-permissions item (step 2).
|
|
25
|
+
*/
|
|
16
26
|
export interface SkipPermissionsItem {
|
|
17
27
|
label: string;
|
|
18
28
|
value: string; // "yes" or "no"
|
|
19
29
|
description: string;
|
|
20
30
|
}
|
|
21
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Result returned by `ExecutionModeSelectorScreen`.
|
|
34
|
+
*/
|
|
22
35
|
export interface ExecutionModeResult {
|
|
23
36
|
mode: ExecutionMode;
|
|
24
37
|
skipPermissions: boolean;
|
|
25
38
|
}
|
|
26
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Props for `ExecutionModeSelectorScreen`.
|
|
42
|
+
*/
|
|
27
43
|
export interface ExecutionModeSelectorScreenProps {
|
|
28
44
|
onBack: () => void;
|
|
29
45
|
onSelect: (result: ExecutionModeResult) => void;
|
|
@@ -48,7 +64,7 @@ export function ExecutionModeSelectorScreen({
|
|
|
48
64
|
const [selectedMode, setSelectedMode] = useState<ExecutionMode | null>(null);
|
|
49
65
|
|
|
50
66
|
// Handle keyboard input
|
|
51
|
-
|
|
67
|
+
useAppInput((input, key) => {
|
|
52
68
|
if (key.escape) {
|
|
53
69
|
if (step === 2) {
|
|
54
70
|
// Go back to step 1
|