@akiojin/gwt 2.11.0 → 2.12.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/dist/claude.d.ts +4 -1
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +51 -7
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/components/App.d.ts +7 -0
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +307 -18
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts +21 -0
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -0
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +145 -0
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -0
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +4 -2
- package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/ModelSelectorScreen.js +1 -1
- package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +10 -2
- package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/SessionSelectorScreen.js +18 -7
- package/dist/cli/ui/components/screens/SessionSelectorScreen.js.map +1 -1
- package/dist/cli/ui/types.d.ts +1 -1
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
- package/dist/cli/ui/utils/branchFormatter.js +0 -13
- package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
- package/dist/cli/ui/utils/continueSession.d.ts +18 -0
- package/dist/cli/ui/utils/continueSession.d.ts.map +1 -0
- package/dist/cli/ui/utils/continueSession.js +67 -0
- package/dist/cli/ui/utils/continueSession.js.map +1 -0
- package/dist/codex.d.ts +4 -1
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +70 -5
- package/dist/codex.js.map +1 -1
- package/dist/config/index.d.ts +9 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +11 -2
- package/dist/config/index.js.map +1 -1
- package/dist/gemini.d.ts +4 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +146 -32
- package/dist/gemini.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +118 -8
- package/dist/index.js.map +1 -1
- package/dist/qwen.d.ts +4 -1
- package/dist/qwen.d.ts.map +1 -1
- package/dist/qwen.js +45 -4
- package/dist/qwen.js.map +1 -1
- package/dist/utils/session.d.ts +82 -0
- package/dist/utils/session.d.ts.map +1 -0
- package/dist/utils/session.js +579 -0
- package/dist/utils/session.js.map +1 -0
- package/package.json +1 -1
- package/src/claude.ts +69 -8
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +12 -2
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +2 -2
- package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +142 -0
- package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +14 -0
- package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +29 -10
- package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +4 -1
- package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +0 -1
- package/src/cli/ui/components/App.tsx +403 -23
- package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +237 -0
- package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +5 -1
- package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +1 -1
- package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +34 -6
- package/src/cli/ui/types.ts +1 -0
- package/src/cli/ui/utils/branchFormatter.ts +0 -13
- package/src/cli/ui/utils/continueSession.ts +106 -0
- package/src/codex.ts +91 -6
- package/src/config/index.ts +22 -2
- package/src/gemini.ts +179 -41
- package/src/index.ts +144 -16
- package/src/qwen.ts +56 -5
- package/src/utils/session.ts +704 -0
package/dist/claude.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export declare function launchClaudeCode(worktreePath: string, options?: {
|
|
|
8
8
|
extraArgs?: string[];
|
|
9
9
|
envOverrides?: Record<string, string>;
|
|
10
10
|
model?: string;
|
|
11
|
-
|
|
11
|
+
sessionId?: string | null;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
sessionId?: string | null;
|
|
14
|
+
}>;
|
|
12
15
|
export declare function isClaudeCodeAvailable(): Promise<boolean>;
|
|
13
16
|
//# sourceMappingURL=claude.d.ts.map
|
package/dist/claude.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AAOA,qBAAa,WAAY,SAAQ,KAAK;IAG3B,KAAK,CAAC,EAAE,OAAO;gBADtB,OAAO,EAAE,MAAM,EACR,KAAK,CAAC,EAAE,OAAO,YAAA;CAKzB;AAED,wBAAsB,gBAAgB,CACpC,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,CAqSxC;AAsBD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAe9D"}
|
package/dist/claude.js
CHANGED
|
@@ -2,6 +2,7 @@ import { execa } from "execa";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { existsSync } from "fs";
|
|
4
4
|
import { createChildStdio, getTerminalStreams } from "./utils/terminal.js";
|
|
5
|
+
import { findLatestClaudeSession } from "./utils/session.js";
|
|
5
6
|
const CLAUDE_CLI_PACKAGE = "@anthropic-ai/claude-code@latest";
|
|
6
7
|
export class ClaudeError extends Error {
|
|
7
8
|
cause;
|
|
@@ -13,6 +14,7 @@ export class ClaudeError extends Error {
|
|
|
13
14
|
}
|
|
14
15
|
export async function launchClaudeCode(worktreePath, options = {}) {
|
|
15
16
|
const terminal = getTerminalStreams();
|
|
17
|
+
const startedAt = Date.now();
|
|
16
18
|
try {
|
|
17
19
|
// Check if the worktree path exists
|
|
18
20
|
if (!existsSync(worktreePath)) {
|
|
@@ -28,11 +30,19 @@ export async function launchClaudeCode(worktreePath, options = {}) {
|
|
|
28
30
|
else if (options.model === "opus") {
|
|
29
31
|
console.log(chalk.green(` 🎯 Model: ${options.model} (Default)`));
|
|
30
32
|
}
|
|
33
|
+
const resumeSessionId = options.sessionId && options.sessionId.trim().length > 0
|
|
34
|
+
? options.sessionId.trim()
|
|
35
|
+
: null;
|
|
31
36
|
// Handle execution mode
|
|
32
37
|
switch (options.mode) {
|
|
33
38
|
case "continue":
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
if (resumeSessionId) {
|
|
40
|
+
args.push("--resume", resumeSessionId);
|
|
41
|
+
console.log(chalk.cyan(` 📱 Continuing specific session: ${resumeSessionId}`));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.log(chalk.yellow(" ℹ️ No saved session ID for this branch/tool. Starting new session."));
|
|
45
|
+
}
|
|
36
46
|
break;
|
|
37
47
|
case "resume":
|
|
38
48
|
// TODO: Implement conversation selection with Ink UI
|
|
@@ -86,7 +96,13 @@ export async function launchClaudeCode(worktreePath, options = {}) {
|
|
|
86
96
|
}
|
|
87
97
|
*/
|
|
88
98
|
// Use standard Claude Code resume for now
|
|
89
|
-
|
|
99
|
+
if (resumeSessionId) {
|
|
100
|
+
args.push("--resume", resumeSessionId);
|
|
101
|
+
console.log(chalk.cyan(` 🔄 Resuming Claude session: ${resumeSessionId}`));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
args.push("-r");
|
|
105
|
+
}
|
|
90
106
|
break;
|
|
91
107
|
case "normal":
|
|
92
108
|
default:
|
|
@@ -114,6 +130,7 @@ export async function launchClaudeCode(worktreePath, options = {}) {
|
|
|
114
130
|
if (options.extraArgs && options.extraArgs.length > 0) {
|
|
115
131
|
args.push(...options.extraArgs);
|
|
116
132
|
}
|
|
133
|
+
console.log(chalk.gray(` 📋 Args: ${args.join(" ")}`));
|
|
117
134
|
terminal.exitRawMode();
|
|
118
135
|
const baseEnv = {
|
|
119
136
|
...process.env,
|
|
@@ -127,7 +144,6 @@ export async function launchClaudeCode(worktreePath, options = {}) {
|
|
|
127
144
|
const hasLocalClaude = await isClaudeCommandAvailable();
|
|
128
145
|
try {
|
|
129
146
|
if (hasLocalClaude) {
|
|
130
|
-
// Use locally installed claude command
|
|
131
147
|
console.log(chalk.green(" ✨ Using locally installed claude command"));
|
|
132
148
|
await execa("claude", args, {
|
|
133
149
|
cwd: worktreePath,
|
|
@@ -139,14 +155,12 @@ export async function launchClaudeCode(worktreePath, options = {}) {
|
|
|
139
155
|
});
|
|
140
156
|
}
|
|
141
157
|
else {
|
|
142
|
-
// Fallback to bunx
|
|
143
158
|
console.log(chalk.cyan(" 🔄 Falling back to bunx @anthropic-ai/claude-code@latest"));
|
|
144
159
|
console.log(chalk.yellow(" 💡 Recommended: Install Claude Code via official method for faster startup"));
|
|
145
160
|
console.log(chalk.yellow(" macOS/Linux: brew install --cask claude-code"));
|
|
146
161
|
console.log(chalk.yellow(" or: curl -fsSL https://claude.ai/install.sh | bash"));
|
|
147
162
|
console.log(chalk.yellow(" Windows: irm https://claude.ai/install.ps1 | iex"));
|
|
148
163
|
console.log("");
|
|
149
|
-
// Wait 2 seconds to let user read the message
|
|
150
164
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
151
165
|
await execa("bunx", [CLAUDE_CLI_PACKAGE, ...args], {
|
|
152
166
|
cwd: worktreePath,
|
|
@@ -161,6 +175,31 @@ export async function launchClaudeCode(worktreePath, options = {}) {
|
|
|
161
175
|
finally {
|
|
162
176
|
childStdio.cleanup();
|
|
163
177
|
}
|
|
178
|
+
// File-based session detection only - no stdout capture
|
|
179
|
+
// Use only findLatestClaudeSession with short timeout, skip sessionProbe to avoid hanging
|
|
180
|
+
let capturedSessionId = null;
|
|
181
|
+
const finishedAt = Date.now();
|
|
182
|
+
try {
|
|
183
|
+
const latest = await findLatestClaudeSession(worktreePath, {
|
|
184
|
+
since: startedAt,
|
|
185
|
+
until: finishedAt + 30_000,
|
|
186
|
+
preferClosestTo: finishedAt,
|
|
187
|
+
windowMs: 10 * 60 * 1000,
|
|
188
|
+
});
|
|
189
|
+
// Priority: latest on disk > resumeSessionId
|
|
190
|
+
capturedSessionId = latest?.id ?? resumeSessionId ?? null;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
capturedSessionId = resumeSessionId ?? null;
|
|
194
|
+
}
|
|
195
|
+
if (capturedSessionId) {
|
|
196
|
+
console.log(chalk.cyan(`\n 🆔 Session ID: ${capturedSessionId}`));
|
|
197
|
+
console.log(chalk.gray(` Resume command: claude --resume ${capturedSessionId}`));
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
console.log(chalk.yellow("\n ℹ️ Could not determine Claude session ID automatically."));
|
|
201
|
+
}
|
|
202
|
+
return capturedSessionId ? { sessionId: capturedSessionId } : {};
|
|
164
203
|
}
|
|
165
204
|
catch (error) {
|
|
166
205
|
const hasLocalClaude = await isClaudeCommandAvailable();
|
|
@@ -203,7 +242,12 @@ export async function launchClaudeCode(worktreePath, options = {}) {
|
|
|
203
242
|
async function isClaudeCommandAvailable() {
|
|
204
243
|
try {
|
|
205
244
|
const command = process.platform === "win32" ? "where" : "which";
|
|
206
|
-
await execa(command, ["claude"], {
|
|
245
|
+
await execa(command, ["claude"], {
|
|
246
|
+
shell: true,
|
|
247
|
+
stdin: "ignore",
|
|
248
|
+
stdout: "ignore",
|
|
249
|
+
stderr: "ignore",
|
|
250
|
+
});
|
|
207
251
|
return true;
|
|
208
252
|
}
|
|
209
253
|
catch {
|
package/dist/claude.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,kBAAkB,GAAG,kCAAkC,CAAC;AAC9D,MAAM,OAAO,WAAY,SAAQ,KAAK;IAG3B;IAFT,YACE,OAAe,EACR,KAAe;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,UAAK,GAAL,KAAK,CAAU;QAGtB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,YAAoB,EACpB,UAOI,EAAE;IAEN,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,oCAAoC;QACpC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,YAAY,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,yBAAyB,YAAY,EAAE,CAAC,CAAC,CAAC;QAEjE,MAAM,IAAI,GAAa,EAAE,CAAC;QAE1B,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC9C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC5D,CAAC;aAAM,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,OAAO,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,eAAe,GACnB,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YACtD,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE;YAC1B,CAAC,CAAC,IAAI,CAAC;QAEX,wBAAwB;QACxB,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,UAAU;gBACb,IAAI,eAAe,EAAE,CAAC;oBACpB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;oBACvC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,sCAAsC,eAAe,EAAE,CAAC,CACpE,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,wEAAwE,CACzE,CACF,CAAC;gBACJ,CAAC;gBACD,MAAM;YACR,KAAK,QAAQ;gBACX,qDAAqD;gBACrD,6DAA6D;gBAC7D,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,4EAA4E,CAC7E,CACF,CAAC;gBACF,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAC/D,CAAC;gBAEF,yCAAyC;gBACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA4CE;gBACF,0CAA0C;gBAC1C,IAAI,eAAe,EAAE,CAAC;oBACpB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;oBACvC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,kCAAkC,eAAe,EAAE,CAAC,CAChE,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAClB,CAAC;gBACD,MAAM;YACR,KAAK,QAAQ,CAAC;YACd;gBACE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBACtD,MAAM;QACV,CAAC;QAED,mDAAmD;QACnD,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;QAED,0BAA0B;QAC1B,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,mCAAmC,CAAC,CAAC,CAAC;YAE/D,wEAAwE;YACxE,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,6DAA6D,CAC9D,CACF,CAAC;YACJ,CAAC;QACH,CAAC;QACD,oDAAoD;QACpD,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAEzD,QAAQ,CAAC,WAAW,EAAE,CAAC;QAEvB,MAAM,OAAO,GAAG;YACd,GAAG,OAAO,CAAC,GAAG;YACd,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;SAChC,CAAC;QACF,MAAM,SAAS,GACb,OAAO,CAAC,eAAe,IAAI,CAAC,OAAO,CAAC,UAAU;YAC5C,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE;YACjC,CAAC,CAAC,OAAO,CAAC;QAEd,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;QAEtC,+CAA+C;QAC/C,MAAM,cAAc,GAAG,MAAM,wBAAwB,EAAE,CAAC;QAExD,IAAI,CAAC;YACH,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAC3D,CAAC;gBACF,MAAM,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE;oBAC1B,GAAG,EAAE,YAAY;oBACjB,KAAK,EAAE,IAAI;oBACX,KAAK,EAAE,UAAU,CAAC,KAAK;oBACvB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,GAAG,EAAE,SAAS;iBACR,CAAC,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CACR,6DAA6D,CAC9D,CACF,CAAC;gBACF,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,+EAA+E,CAChF,CACF,CAAC;gBACF,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CAAC,oDAAoD,CAAC,CACnE,CAAC;gBACF,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,0DAA0D,CAC3D,CACF,CAAC;gBACF,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,wDAAwD,CACzD,CACF,CAAC;gBACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC1D,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,kBAAkB,EAAE,GAAG,IAAI,CAAC,EAAE;oBACjD,GAAG,EAAE,YAAY;oBACjB,KAAK,EAAE,IAAI;oBACX,KAAK,EAAE,UAAU,CAAC,KAAK;oBACvB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,GAAG,EAAE,SAAS;iBACR,CAAC,CAAC;YACZ,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;QAED,wDAAwD;QACxD,0FAA0F;QAC1F,IAAI,iBAAiB,GAAkB,IAAI,CAAC;QAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC,YAAY,EAAE;gBACzD,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,UAAU,GAAG,MAAM;gBAC1B,eAAe,EAAE,UAAU;gBAC3B,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;aACzB,CAAC,CAAC;YACH,6CAA6C;YAC7C,iBAAiB,GAAG,MAAM,EAAE,EAAE,IAAI,eAAe,IAAI,IAAI,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB,GAAG,eAAe,IAAI,IAAI,CAAC;QAC9C,CAAC;QAED,IAAI,iBAAiB,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,iBAAiB,EAAE,CAAC,CAAC,CAAC;YACpE,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,sCAAsC,iBAAiB,EAAE,CAAC,CACtE,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,+DAA+D,CAChE,CACF,CAAC;QACJ,CAAC;QAED,OAAO,iBAAiB,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,MAAM,cAAc,GAAG,MAAM,wBAAwB,EAAE,CAAC;QACxD,IAAI,YAAoB,CAAC;QAEzB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5B,IAAI,cAAc,EAAE,CAAC;gBACnB,YAAY;oBACV,4EAA4E,CAAC;YACjF,CAAC;iBAAM,CAAC;gBACN,YAAY;oBACV,yFAAyF,CAAC;YAC9F,CAAC;QACH,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,iCAAiC,KAAK,CAAC,OAAO,IAAI,eAAe,EAAE,CAAC;QACrF,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC,CAAC;YAC/D,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,MAAM,CACV,iFAAiF,CAClF,CACF,CAAC;gBACF,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,MAAM,CAAC,kDAAkD,CAAC,CACjE,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,MAAM,CACV,2DAA2D,CAC5D,CACF,CAAC;gBACF,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,MAAM,CACV,oFAAoF,CACrF,CACF,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,MAAM,CAAC,oDAAoD,CAAC,CACnE,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,WAAW,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,wBAAwB;IACrC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QACjE,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE;YAC/B,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,kBAAkB,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC,CAAC;YAC5D,OAAO,CAAC,KAAK,CACX,KAAK,CAAC,IAAI,CACR,qEAAqE,CACtE,CACF,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -11,11 +11,18 @@ export interface SelectionResult {
|
|
|
11
11
|
skipPermissions: boolean;
|
|
12
12
|
model?: string | null;
|
|
13
13
|
inferenceLevel?: InferenceLevel;
|
|
14
|
+
sessionId?: string | null;
|
|
14
15
|
}
|
|
15
16
|
export interface AppProps {
|
|
16
17
|
onExit: (result?: SelectionResult) => void;
|
|
17
18
|
loadingIndicatorDelay?: number;
|
|
18
19
|
}
|
|
20
|
+
export interface SessionOption {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
toolLabel: string;
|
|
23
|
+
branch: string;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
}
|
|
19
26
|
/**
|
|
20
27
|
* App - Top-level component for Ink.js UI
|
|
21
28
|
* Integrates ErrorBoundary, data fetching, screen navigation, and all screens
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../src/cli/ui/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AASf,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../src/cli/ui/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AASf,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAC;AAW9E,OAAO,KAAK,EACV,MAAM,EAGN,cAAc,EAEf,MAAM,aAAa,CAAC;AA6CrB,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,eAAe,KAAK,IAAI,CAAC;IAC3C,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,EAAE,MAAM,EAAE,qBAA2B,EAAE,EAAE,QAAQ,qBA8qCpE"}
|
|
@@ -7,16 +7,20 @@ import { BranchActionSelectorScreen } from "../screens/BranchActionSelectorScree
|
|
|
7
7
|
import { AIToolSelectorScreen } from "./screens/AIToolSelectorScreen.js";
|
|
8
8
|
import { SessionSelectorScreen } from "./screens/SessionSelectorScreen.js";
|
|
9
9
|
import { ExecutionModeSelectorScreen } from "./screens/ExecutionModeSelectorScreen.js";
|
|
10
|
+
import { BranchQuickStartScreen } from "./screens/BranchQuickStartScreen.js";
|
|
10
11
|
import { ModelSelectorScreen, } from "./screens/ModelSelectorScreen.js";
|
|
11
12
|
import { useGitData } from "../hooks/useGitData.js";
|
|
12
13
|
import { useScreenState } from "../hooks/useScreenState.js";
|
|
13
14
|
import { formatBranchItems } from "../utils/branchFormatter.js";
|
|
14
15
|
import { calculateStatistics } from "../utils/statisticsCalculator.js";
|
|
15
16
|
import { getRepositoryRoot, deleteBranch } from "../../../git.js";
|
|
17
|
+
import { loadSession } from "../../../config/index.js";
|
|
16
18
|
import { createWorktree, generateWorktreePath, getMergedPRWorktrees, isProtectedBranchName, removeWorktree, switchToProtectedBranch, } from "../../../worktree.js";
|
|
17
19
|
import { getPackageVersion } from "../../../utils.js";
|
|
18
20
|
import { resolveBaseBranchLabel, resolveBaseBranchRef, } from "../utils/baseBranch.js";
|
|
19
21
|
import { getDefaultInferenceForModel, getDefaultModelOption, } from "../utils/modelOptions.js";
|
|
22
|
+
import { resolveContinueSessionId, findLatestBranchSessionsByTool, } from "../utils/continueSession.js";
|
|
23
|
+
import { findLatestCodexSession, findLatestCodexSessionId, findLatestClaudeSession, findLatestGeminiSession, } from "../../../utils/session.js";
|
|
20
24
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧"];
|
|
21
25
|
const COMPLETION_HOLD_DURATION_MS = 3000;
|
|
22
26
|
const PROTECTED_BRANCH_WARNING = "Root branches operate directly in the repository root. Create a new branch if you need a dedicated worktree.";
|
|
@@ -41,6 +45,14 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
41
45
|
const { currentScreen, navigateTo, goBack } = useScreenState();
|
|
42
46
|
// Version state
|
|
43
47
|
const [version, setVersion] = useState(null);
|
|
48
|
+
const [repoRoot, setRepoRoot] = useState(null);
|
|
49
|
+
const [sessionOptions, setSessionOptions] = useState([]);
|
|
50
|
+
const [sessionLoading, setSessionLoading] = useState(false);
|
|
51
|
+
const [sessionError, setSessionError] = useState(null);
|
|
52
|
+
const [pendingExecution, setPendingExecution] = useState(null);
|
|
53
|
+
const [continueSessionId, setContinueSessionId] = useState(null);
|
|
54
|
+
const [branchQuickStart, setBranchQuickStart] = useState([]);
|
|
55
|
+
const [branchQuickStartLoading, setBranchQuickStartLoading] = useState(false);
|
|
44
56
|
// Selection state (for branch → tool → mode flow)
|
|
45
57
|
const [selectedBranch, setSelectedBranch] = useState(null);
|
|
46
58
|
const [creationSourceBranch, setCreationSourceBranch] = useState(null);
|
|
@@ -63,6 +75,12 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
63
75
|
.then(setVersion)
|
|
64
76
|
.catch(() => setVersion(null));
|
|
65
77
|
}, []);
|
|
78
|
+
// Fetch repository root once for session lookups
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
getRepositoryRoot()
|
|
81
|
+
.then(setRepoRoot)
|
|
82
|
+
.catch(() => setRepoRoot(null));
|
|
83
|
+
}, []);
|
|
66
84
|
useEffect(() => {
|
|
67
85
|
if (!cleanupInputLocked) {
|
|
68
86
|
spinnerFrameIndexRef.current = 0;
|
|
@@ -110,6 +128,201 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
110
128
|
setHiddenBranches(filtered);
|
|
111
129
|
}
|
|
112
130
|
}, [branches, hiddenBranches]);
|
|
131
|
+
// Load available sessions when entering session selector
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (currentScreen !== "session-selector") {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!selectedTool || !selectedBranch) {
|
|
137
|
+
setSessionOptions([]);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
setSessionLoading(true);
|
|
141
|
+
setSessionError(null);
|
|
142
|
+
(async () => {
|
|
143
|
+
try {
|
|
144
|
+
const root = repoRoot ?? (await getRepositoryRoot());
|
|
145
|
+
if (!repoRoot && root) {
|
|
146
|
+
setRepoRoot(root);
|
|
147
|
+
}
|
|
148
|
+
const sessionData = root ? await loadSession(root) : null;
|
|
149
|
+
const history = sessionData?.history ?? [];
|
|
150
|
+
const filtered = history
|
|
151
|
+
.filter((entry) => entry.sessionId &&
|
|
152
|
+
entry.toolId === selectedTool &&
|
|
153
|
+
entry.branch === selectedBranch.name)
|
|
154
|
+
.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0))
|
|
155
|
+
.map((entry) => ({
|
|
156
|
+
sessionId: entry.sessionId,
|
|
157
|
+
toolLabel: entry.toolLabel,
|
|
158
|
+
branch: entry.branch,
|
|
159
|
+
timestamp: entry.timestamp,
|
|
160
|
+
}));
|
|
161
|
+
setSessionOptions(filtered);
|
|
162
|
+
}
|
|
163
|
+
catch (_err) {
|
|
164
|
+
setSessionOptions([]);
|
|
165
|
+
setSessionError("セッション一覧の取得に失敗しました");
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
setSessionLoading(false);
|
|
169
|
+
}
|
|
170
|
+
})();
|
|
171
|
+
}, [currentScreen, selectedTool, selectedBranch, repoRoot]);
|
|
172
|
+
// Load quick start options for selected branch (latest per tool)
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!selectedBranch) {
|
|
175
|
+
setBranchQuickStart([]);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
let cancelled = false;
|
|
179
|
+
setBranchQuickStartLoading(true);
|
|
180
|
+
(async () => {
|
|
181
|
+
try {
|
|
182
|
+
const root = repoRoot ?? (await getRepositoryRoot());
|
|
183
|
+
if (!repoRoot && root) {
|
|
184
|
+
setRepoRoot(root);
|
|
185
|
+
}
|
|
186
|
+
if (!root) {
|
|
187
|
+
if (!cancelled)
|
|
188
|
+
setBranchQuickStart([]);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const sessionData = await loadSession(root);
|
|
192
|
+
const history = sessionData?.history ?? [];
|
|
193
|
+
const combinedHistory = [...history];
|
|
194
|
+
if (sessionData?.lastSessionId &&
|
|
195
|
+
sessionData.lastBranch === selectedBranch.name &&
|
|
196
|
+
sessionData.lastUsedTool) {
|
|
197
|
+
const synthetic = {
|
|
198
|
+
branch: sessionData.lastBranch,
|
|
199
|
+
worktreePath: sessionData.lastWorktreePath ?? null,
|
|
200
|
+
toolId: sessionData.lastUsedTool,
|
|
201
|
+
toolLabel: sessionData.toolLabel ?? sessionData.lastUsedTool,
|
|
202
|
+
sessionId: sessionData.lastSessionId,
|
|
203
|
+
mode: sessionData.mode ?? null,
|
|
204
|
+
model: sessionData.model ?? null,
|
|
205
|
+
reasoningLevel: sessionData.reasoningLevel ?? null,
|
|
206
|
+
skipPermissions: sessionData.skipPermissions ?? null,
|
|
207
|
+
timestamp: sessionData.timestamp ?? Date.now(),
|
|
208
|
+
};
|
|
209
|
+
combinedHistory.push(synthetic);
|
|
210
|
+
}
|
|
211
|
+
const latestPerTool = findLatestBranchSessionsByTool(combinedHistory, selectedBranch.name, selectedWorktreePath);
|
|
212
|
+
const mapped = await Promise.all(latestPerTool.map(async (entry) => {
|
|
213
|
+
let sessionId = entry.sessionId ?? null;
|
|
214
|
+
// For Codex, prefer a newer filesystem session over stale history
|
|
215
|
+
if (entry.toolId === "codex-cli") {
|
|
216
|
+
try {
|
|
217
|
+
const latestCodex = await findLatestCodexSession();
|
|
218
|
+
const historyTs = entry.timestamp ?? 0;
|
|
219
|
+
if (latestCodex &&
|
|
220
|
+
(!sessionId || latestCodex.mtime > historyTs)) {
|
|
221
|
+
sessionId = latestCodex.id;
|
|
222
|
+
}
|
|
223
|
+
// Fallback when filesystem unavailable and history missing
|
|
224
|
+
if (!sessionId) {
|
|
225
|
+
const latestId = await findLatestCodexSessionId();
|
|
226
|
+
if (latestId)
|
|
227
|
+
sessionId = latestId;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// ignore lookup failure
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// For Claude Code, prefer the newest session file in the worktree even if history is stale.
|
|
235
|
+
if (entry.toolId === "claude-code") {
|
|
236
|
+
try {
|
|
237
|
+
const worktree = selectedWorktreePath ??
|
|
238
|
+
selectedBranch?.displayName ??
|
|
239
|
+
workingDirectory;
|
|
240
|
+
// Always resolve freshest on-disk session for this worktree (no window restriction)
|
|
241
|
+
const latestAny = await findLatestClaudeSession(worktree);
|
|
242
|
+
sessionId = latestAny?.id ?? sessionId ?? null;
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// ignore lookup failure
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// For Gemini, prefer newest session file (Gemini keeps per-project chats)
|
|
249
|
+
if (entry.toolId === "gemini-cli") {
|
|
250
|
+
try {
|
|
251
|
+
const gemSession = await findLatestGeminiSession(selectedWorktreePath ??
|
|
252
|
+
selectedBranch?.displayName ??
|
|
253
|
+
workingDirectory, {
|
|
254
|
+
...(entry.timestamp !== null && entry.timestamp !== undefined
|
|
255
|
+
? { since: entry.timestamp - 60_000, preferClosestTo: entry.timestamp }
|
|
256
|
+
: {}),
|
|
257
|
+
windowMs: 60 * 60 * 1000,
|
|
258
|
+
});
|
|
259
|
+
if (gemSession?.id)
|
|
260
|
+
sessionId = gemSession.id;
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
// ignore
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
toolId: entry.toolId,
|
|
268
|
+
toolLabel: entry.toolLabel,
|
|
269
|
+
model: entry.model ?? null,
|
|
270
|
+
inferenceLevel: (entry.reasoningLevel ??
|
|
271
|
+
sessionData?.reasoningLevel ??
|
|
272
|
+
null),
|
|
273
|
+
sessionId,
|
|
274
|
+
skipPermissions: entry.skipPermissions ?? sessionData?.skipPermissions ?? null,
|
|
275
|
+
timestamp: entry.timestamp ?? null,
|
|
276
|
+
};
|
|
277
|
+
}));
|
|
278
|
+
if (!cancelled) {
|
|
279
|
+
setBranchQuickStart(mapped);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
if (!cancelled)
|
|
284
|
+
setBranchQuickStart([]);
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
if (!cancelled)
|
|
288
|
+
setBranchQuickStartLoading(false);
|
|
289
|
+
}
|
|
290
|
+
})();
|
|
291
|
+
return () => {
|
|
292
|
+
cancelled = true;
|
|
293
|
+
};
|
|
294
|
+
}, [selectedBranch, repoRoot]);
|
|
295
|
+
// Load last session ID for "Continue" label when entering execution mode selector
|
|
296
|
+
useEffect(() => {
|
|
297
|
+
if (currentScreen !== "execution-mode-selector") {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (!selectedTool || !selectedBranch) {
|
|
301
|
+
setContinueSessionId(null);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
(async () => {
|
|
305
|
+
try {
|
|
306
|
+
const root = repoRoot ?? (await getRepositoryRoot());
|
|
307
|
+
if (!repoRoot && root) {
|
|
308
|
+
setRepoRoot(root);
|
|
309
|
+
}
|
|
310
|
+
const sessionData = root ? await loadSession(root) : null;
|
|
311
|
+
const history = sessionData?.history ?? [];
|
|
312
|
+
const found = await resolveContinueSessionId({
|
|
313
|
+
history,
|
|
314
|
+
sessionData,
|
|
315
|
+
branch: selectedBranch.name,
|
|
316
|
+
toolId: selectedTool,
|
|
317
|
+
repoRoot: root,
|
|
318
|
+
});
|
|
319
|
+
setContinueSessionId(found ?? null);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
setContinueSessionId(null);
|
|
323
|
+
}
|
|
324
|
+
})();
|
|
325
|
+
}, [currentScreen, selectedTool, selectedBranch, repoRoot]);
|
|
113
326
|
// Update preferred tool when branch or data changes
|
|
114
327
|
useEffect(() => {
|
|
115
328
|
if (!selectedBranch)
|
|
@@ -126,6 +339,12 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
126
339
|
}
|
|
127
340
|
}, []);
|
|
128
341
|
const visibleBranches = useMemo(() => branches.filter((branch) => !hiddenBranches.includes(branch.name)), [branches, hiddenBranches]);
|
|
342
|
+
const selectedWorktreePath = useMemo(() => {
|
|
343
|
+
if (!selectedBranch)
|
|
344
|
+
return null;
|
|
345
|
+
const wt = worktrees.find((w) => w.branch === selectedBranch.name);
|
|
346
|
+
return wt?.path ?? null;
|
|
347
|
+
}, [selectedBranch, worktrees]);
|
|
129
348
|
// Helper function to create content-based hash for branches
|
|
130
349
|
const branchHash = useMemo(() => visibleBranches
|
|
131
350
|
.map((b) => `${b.name}-${b.type}-${b.isCurrent}`)
|
|
@@ -288,7 +507,10 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
288
507
|
color: "green",
|
|
289
508
|
});
|
|
290
509
|
refresh();
|
|
291
|
-
|
|
510
|
+
const nextScreen = branchQuickStart.length || branchQuickStartLoading
|
|
511
|
+
? "branch-quick-start"
|
|
512
|
+
: "ai-tool-selector";
|
|
513
|
+
navigateTo(nextScreen);
|
|
292
514
|
}
|
|
293
515
|
catch (error) {
|
|
294
516
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -298,17 +520,30 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
298
520
|
});
|
|
299
521
|
console.error("Failed to switch protected branch:", error);
|
|
300
522
|
}
|
|
301
|
-
}, [
|
|
523
|
+
}, [
|
|
524
|
+
branchQuickStart,
|
|
525
|
+
branchQuickStartLoading,
|
|
526
|
+
navigateTo,
|
|
527
|
+
refresh,
|
|
528
|
+
selectedBranch,
|
|
529
|
+
setCleanupFooterMessage,
|
|
530
|
+
]);
|
|
302
531
|
const handleUseExistingBranch = useCallback(() => {
|
|
303
532
|
if (selectedBranch && isProtectedSelection(selectedBranch)) {
|
|
304
533
|
void handleProtectedBranchSwitch();
|
|
305
534
|
return;
|
|
306
535
|
}
|
|
307
|
-
|
|
536
|
+
if (branchQuickStart.length) {
|
|
537
|
+
navigateTo("branch-quick-start");
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
navigateTo("ai-tool-selector");
|
|
541
|
+
}
|
|
308
542
|
}, [
|
|
309
543
|
handleProtectedBranchSwitch,
|
|
310
544
|
isProtectedSelection,
|
|
311
545
|
navigateTo,
|
|
546
|
+
branchQuickStart.length,
|
|
312
547
|
selectedBranch,
|
|
313
548
|
]);
|
|
314
549
|
const handleCreateNewBranch = useCallback(() => {
|
|
@@ -529,35 +764,27 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
529
764
|
}));
|
|
530
765
|
navigateTo("execution-mode-selector");
|
|
531
766
|
}, [navigateTo, selectedTool]);
|
|
532
|
-
|
|
533
|
-
const handleSessionSelect = useCallback((_session) => {
|
|
534
|
-
// TODO: Load selected session and navigate to next screen
|
|
535
|
-
// For now, just go back to branch list
|
|
536
|
-
goBack();
|
|
537
|
-
}, [goBack]);
|
|
538
|
-
// Handle execution mode and skipPermissions selection
|
|
539
|
-
const handleModeSelect = useCallback((result) => {
|
|
540
|
-
// All selections complete - exit with result
|
|
767
|
+
const completeSelection = useCallback((executionMode, skip, sessionId) => {
|
|
541
768
|
if (selectedBranch && selectedTool) {
|
|
542
769
|
const defaultModel = getDefaultModelOption(selectedTool);
|
|
543
770
|
const resolvedModel = selectedModel?.model ?? defaultModel?.id ?? null;
|
|
544
771
|
const resolvedInference = selectedModel?.inferenceLevel ??
|
|
545
772
|
getDefaultInferenceForModel(defaultModel ?? undefined);
|
|
546
|
-
const modelForPayload = resolvedModel;
|
|
547
773
|
const payload = {
|
|
548
774
|
branch: selectedBranch.name,
|
|
549
775
|
displayName: selectedBranch.displayName,
|
|
550
776
|
branchType: selectedBranch.branchType,
|
|
551
777
|
tool: selectedTool,
|
|
552
|
-
mode:
|
|
553
|
-
skipPermissions:
|
|
554
|
-
...(
|
|
778
|
+
mode: executionMode,
|
|
779
|
+
skipPermissions: skip,
|
|
780
|
+
...(resolvedModel !== undefined ? { model: resolvedModel } : {}),
|
|
555
781
|
...(resolvedInference !== undefined
|
|
556
782
|
? { inferenceLevel: resolvedInference }
|
|
557
783
|
: {}),
|
|
558
784
|
...(selectedBranch.remoteBranch
|
|
559
785
|
? { remoteBranch: selectedBranch.remoteBranch }
|
|
560
786
|
: {}),
|
|
787
|
+
...(sessionId ? { sessionId } : {}),
|
|
561
788
|
};
|
|
562
789
|
onExit(payload);
|
|
563
790
|
exit();
|
|
@@ -571,6 +798,59 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
571
798
|
getDefaultModelOption,
|
|
572
799
|
getDefaultInferenceForModel,
|
|
573
800
|
]);
|
|
801
|
+
// Handle session selection
|
|
802
|
+
const handleSessionSelect = useCallback((session) => {
|
|
803
|
+
const execution = pendingExecution ?? {
|
|
804
|
+
mode: "resume",
|
|
805
|
+
skipPermissions: false,
|
|
806
|
+
};
|
|
807
|
+
completeSelection(execution.mode, execution.skipPermissions, session);
|
|
808
|
+
}, [pendingExecution, completeSelection]);
|
|
809
|
+
const handleQuickStartSelect = useCallback((action, toolId) => {
|
|
810
|
+
if (action === "manual" || !branchQuickStart.length) {
|
|
811
|
+
navigateTo("ai-tool-selector");
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const selected = branchQuickStart.find((opt) => opt.toolId === toolId) ??
|
|
815
|
+
branchQuickStart[0];
|
|
816
|
+
if (!selected) {
|
|
817
|
+
navigateTo("ai-tool-selector");
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
setSelectedTool(selected.toolId);
|
|
821
|
+
setPreferredToolId(selected.toolId);
|
|
822
|
+
setSelectedModel(selected.model
|
|
823
|
+
? {
|
|
824
|
+
model: selected.model,
|
|
825
|
+
inferenceLevel: selected.inferenceLevel ?? undefined,
|
|
826
|
+
}
|
|
827
|
+
: null);
|
|
828
|
+
const skip = selected.skipPermissions ?? false;
|
|
829
|
+
if (action === "reuse-continue") {
|
|
830
|
+
const hasSession = Boolean(selected.sessionId);
|
|
831
|
+
const mode = hasSession ? "resume" : "continue";
|
|
832
|
+
completeSelection(mode, skip, selected.sessionId ?? null);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
// "Start new with previous settings" skips the execution mode screen and launches immediately
|
|
836
|
+
completeSelection("normal", skip, null);
|
|
837
|
+
}, [
|
|
838
|
+
branchQuickStart,
|
|
839
|
+
navigateTo,
|
|
840
|
+
setPreferredToolId,
|
|
841
|
+
setSelectedModel,
|
|
842
|
+
setSelectedTool,
|
|
843
|
+
completeSelection,
|
|
844
|
+
]);
|
|
845
|
+
// Handle execution mode and skipPermissions selection
|
|
846
|
+
const handleModeSelect = useCallback((result) => {
|
|
847
|
+
if (result.mode === "resume") {
|
|
848
|
+
setPendingExecution(result);
|
|
849
|
+
navigateTo("session-selector");
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
completeSelection(result.mode, result.skipPermissions, null);
|
|
853
|
+
}, [completeSelection, navigateTo]);
|
|
574
854
|
// Render screen based on currentScreen
|
|
575
855
|
const renderScreen = () => {
|
|
576
856
|
switch (currentScreen) {
|
|
@@ -596,6 +876,15 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
596
876
|
}
|
|
597
877
|
return React.createElement(BranchActionSelectorScreen, { ...baseProps });
|
|
598
878
|
}
|
|
879
|
+
case "branch-quick-start":
|
|
880
|
+
return (React.createElement(BranchQuickStartScreen, { branchName: selectedBranch?.displayName ?? "", previousOptions: branchQuickStart.map((opt) => ({
|
|
881
|
+
toolId: opt.toolId,
|
|
882
|
+
toolLabel: opt.toolLabel,
|
|
883
|
+
model: opt.model ?? null,
|
|
884
|
+
inferenceLevel: opt.inferenceLevel ?? null,
|
|
885
|
+
skipPermissions: opt.skipPermissions ?? null,
|
|
886
|
+
sessionId: opt.sessionId ?? null,
|
|
887
|
+
})), loading: branchQuickStartLoading, onBack: goBack, onSelect: handleQuickStartSelect, version: version }));
|
|
599
888
|
case "ai-tool-selector":
|
|
600
889
|
return (React.createElement(AIToolSelectorScreen, { onBack: goBack, onSelect: handleToolSelect, version: version, initialToolId: selectedTool ?? preferredToolId ?? null }));
|
|
601
890
|
case "model-selector":
|
|
@@ -606,9 +895,9 @@ export function App({ onExit, loadingIndicatorDelay = 300 }) {
|
|
|
606
895
|
return (React.createElement(ModelSelectorScreen, { tool: selectedTool, onBack: goBack, onSelect: handleModelSelect, version: version, initialSelection: selectedModel }));
|
|
607
896
|
case "session-selector":
|
|
608
897
|
// TODO: Implement session data fetching
|
|
609
|
-
return (React.createElement(SessionSelectorScreen, { sessions:
|
|
898
|
+
return (React.createElement(SessionSelectorScreen, { sessions: sessionOptions, loading: sessionLoading, errorMessage: sessionError, onBack: goBack, onSelect: handleSessionSelect, version: version }));
|
|
610
899
|
case "execution-mode-selector":
|
|
611
|
-
return (React.createElement(ExecutionModeSelectorScreen, { onBack: goBack, onSelect: handleModeSelect, version: version }));
|
|
900
|
+
return (React.createElement(ExecutionModeSelectorScreen, { onBack: goBack, onSelect: handleModeSelect, version: version, continueSessionId: continueSessionId }));
|
|
612
901
|
default:
|
|
613
902
|
return (React.createElement(BranchListScreen, { branches: branchItems, stats: stats, onSelect: handleSelect, onQuit: handleQuit, onRefresh: refresh, loading: loading, error: error, lastUpdated: lastUpdated, loadingIndicatorDelay: loadingIndicatorDelay, version: version, workingDirectory: workingDirectory }));
|
|
614
903
|
}
|