@akiojin/gwt 4.11.6 → 4.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.
Files changed (172) hide show
  1. package/bin/gwt.js +1 -1
  2. package/dist/claude.d.ts +1 -0
  3. package/dist/claude.d.ts.map +1 -1
  4. package/dist/claude.js +50 -24
  5. package/dist/claude.js.map +1 -1
  6. package/dist/cli/ui/App.solid.d.ts.map +1 -1
  7. package/dist/cli/ui/App.solid.js +247 -49
  8. package/dist/cli/ui/App.solid.js.map +1 -1
  9. package/dist/cli/ui/components/solid/QuickStartStep.d.ts.map +1 -1
  10. package/dist/cli/ui/components/solid/QuickStartStep.js +35 -22
  11. package/dist/cli/ui/components/solid/QuickStartStep.js.map +1 -1
  12. package/dist/cli/ui/components/solid/SelectInput.d.ts.map +1 -1
  13. package/dist/cli/ui/components/solid/SelectInput.js +2 -1
  14. package/dist/cli/ui/components/solid/SelectInput.js.map +1 -1
  15. package/dist/cli/ui/components/solid/WizardController.d.ts.map +1 -1
  16. package/dist/cli/ui/components/solid/WizardController.js +19 -11
  17. package/dist/cli/ui/components/solid/WizardController.js.map +1 -1
  18. package/dist/cli/ui/components/solid/WizardSteps.d.ts.map +1 -1
  19. package/dist/cli/ui/components/solid/WizardSteps.js +26 -69
  20. package/dist/cli/ui/components/solid/WizardSteps.js.map +1 -1
  21. package/dist/cli/ui/core/theme.d.ts +9 -0
  22. package/dist/cli/ui/core/theme.d.ts.map +1 -1
  23. package/dist/cli/ui/core/theme.js +21 -0
  24. package/dist/cli/ui/core/theme.js.map +1 -1
  25. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts +9 -2
  26. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts.map +1 -1
  27. package/dist/cli/ui/screens/solid/BranchListScreen.js +101 -28
  28. package/dist/cli/ui/screens/solid/BranchListScreen.js.map +1 -1
  29. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts +2 -1
  30. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts.map +1 -1
  31. package/dist/cli/ui/screens/solid/ConfirmScreen.js +11 -3
  32. package/dist/cli/ui/screens/solid/ConfirmScreen.js.map +1 -1
  33. package/dist/cli/ui/screens/solid/EnvironmentScreen.d.ts.map +1 -1
  34. package/dist/cli/ui/screens/solid/EnvironmentScreen.js +9 -10
  35. package/dist/cli/ui/screens/solid/EnvironmentScreen.js.map +1 -1
  36. package/dist/cli/ui/screens/solid/LogScreen.d.ts +7 -1
  37. package/dist/cli/ui/screens/solid/LogScreen.d.ts.map +1 -1
  38. package/dist/cli/ui/screens/solid/LogScreen.js +254 -16
  39. package/dist/cli/ui/screens/solid/LogScreen.js.map +1 -1
  40. package/dist/cli/ui/screens/solid/ProfileEnvScreen.d.ts.map +1 -1
  41. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js +8 -5
  42. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js.map +1 -1
  43. package/dist/cli/ui/screens/solid/SelectorScreen.d.ts.map +1 -1
  44. package/dist/cli/ui/screens/solid/SelectorScreen.js +12 -4
  45. package/dist/cli/ui/screens/solid/SelectorScreen.js.map +1 -1
  46. package/dist/cli/ui/types.d.ts +1 -0
  47. package/dist/cli/ui/types.d.ts.map +1 -1
  48. package/dist/cli/ui/utils/branchFormatter.d.ts +1 -0
  49. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  50. package/dist/cli/ui/utils/branchFormatter.js +29 -7
  51. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  52. package/dist/cli/ui/utils/continueSession.d.ts +14 -0
  53. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  54. package/dist/cli/ui/utils/continueSession.js +61 -3
  55. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  56. package/dist/cli/ui/utils/versionCache.d.ts +37 -0
  57. package/dist/cli/ui/utils/versionCache.d.ts.map +1 -0
  58. package/dist/cli/ui/utils/versionCache.js +70 -0
  59. package/dist/cli/ui/utils/versionCache.js.map +1 -0
  60. package/dist/cli/ui/utils/versionFetcher.d.ts +41 -0
  61. package/dist/cli/ui/utils/versionFetcher.d.ts.map +1 -0
  62. package/dist/cli/ui/utils/versionFetcher.js +89 -0
  63. package/dist/cli/ui/utils/versionFetcher.js.map +1 -0
  64. package/dist/codex.d.ts +1 -0
  65. package/dist/codex.d.ts.map +1 -1
  66. package/dist/codex.js +48 -19
  67. package/dist/codex.js.map +1 -1
  68. package/dist/config/index.d.ts.map +1 -1
  69. package/dist/config/index.js +10 -1
  70. package/dist/config/index.js.map +1 -1
  71. package/dist/gemini.d.ts +1 -0
  72. package/dist/gemini.d.ts.map +1 -1
  73. package/dist/gemini.js +36 -3
  74. package/dist/gemini.js.map +1 -1
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +32 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/launcher.d.ts.map +1 -1
  79. package/dist/launcher.js +43 -8
  80. package/dist/launcher.js.map +1 -1
  81. package/dist/logging/agentOutput.d.ts +21 -0
  82. package/dist/logging/agentOutput.d.ts.map +1 -0
  83. package/dist/logging/agentOutput.js +164 -0
  84. package/dist/logging/agentOutput.js.map +1 -0
  85. package/dist/logging/formatter.d.ts.map +1 -1
  86. package/dist/logging/formatter.js +18 -4
  87. package/dist/logging/formatter.js.map +1 -1
  88. package/dist/logging/logger.d.ts.map +1 -1
  89. package/dist/logging/logger.js +2 -0
  90. package/dist/logging/logger.js.map +1 -1
  91. package/dist/logging/reader.d.ts +21 -0
  92. package/dist/logging/reader.d.ts.map +1 -1
  93. package/dist/logging/reader.js +79 -0
  94. package/dist/logging/reader.js.map +1 -1
  95. package/dist/opentui/index.solid.js +2306 -653
  96. package/dist/services/dependency-installer.js +2 -2
  97. package/dist/services/dependency-installer.js.map +1 -1
  98. package/dist/utils/session/common.d.ts +8 -0
  99. package/dist/utils/session/common.d.ts.map +1 -1
  100. package/dist/utils/session/common.js +22 -0
  101. package/dist/utils/session/common.js.map +1 -1
  102. package/dist/utils/session/parsers/claude.d.ts +10 -4
  103. package/dist/utils/session/parsers/claude.d.ts.map +1 -1
  104. package/dist/utils/session/parsers/claude.js +64 -18
  105. package/dist/utils/session/parsers/claude.js.map +1 -1
  106. package/dist/utils/session/parsers/codex.d.ts.map +1 -1
  107. package/dist/utils/session/parsers/codex.js +48 -28
  108. package/dist/utils/session/parsers/codex.js.map +1 -1
  109. package/dist/utils/session/parsers/gemini.d.ts.map +1 -1
  110. package/dist/utils/session/parsers/gemini.js +43 -6
  111. package/dist/utils/session/parsers/gemini.js.map +1 -1
  112. package/dist/utils/session/parsers/opencode.d.ts.map +1 -1
  113. package/dist/utils/session/parsers/opencode.js +43 -6
  114. package/dist/utils/session/parsers/opencode.js.map +1 -1
  115. package/dist/utils/session/types.d.ts +7 -0
  116. package/dist/utils/session/types.d.ts.map +1 -1
  117. package/dist/web/client/src/components/ui/alert.d.ts +1 -1
  118. package/dist/worktree.d.ts +4 -1
  119. package/dist/worktree.d.ts.map +1 -1
  120. package/dist/worktree.js +21 -15
  121. package/dist/worktree.js.map +1 -1
  122. package/package.json +2 -1
  123. package/src/claude.ts +64 -28
  124. package/src/cli/ui/App.solid.tsx +324 -51
  125. package/src/cli/ui/__tests__/solid/AppSolid.cleanup.test.tsx +830 -1
  126. package/src/cli/ui/__tests__/solid/BranchListScreen.test.tsx +105 -5
  127. package/src/cli/ui/__tests__/solid/ConfirmScreen.test.tsx +77 -0
  128. package/src/cli/ui/__tests__/solid/LogScreen.test.tsx +351 -0
  129. package/src/cli/ui/__tests__/solid/components/QuickStartStep.test.tsx +73 -2
  130. package/src/cli/ui/__tests__/solid/components/WizardSteps.test.tsx +4 -1
  131. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +72 -45
  132. package/src/cli/ui/components/solid/QuickStartStep.tsx +35 -23
  133. package/src/cli/ui/components/solid/SearchInput.tsx +1 -1
  134. package/src/cli/ui/components/solid/SelectInput.tsx +4 -0
  135. package/src/cli/ui/components/solid/WizardController.tsx +20 -11
  136. package/src/cli/ui/components/solid/WizardSteps.tsx +29 -86
  137. package/src/cli/ui/core/theme.ts +32 -0
  138. package/src/cli/ui/hooks/solid/useAsyncOperation.ts +8 -6
  139. package/src/cli/ui/hooks/solid/useGitOperations.ts +6 -5
  140. package/src/cli/ui/screens/solid/BranchListScreen.tsx +135 -32
  141. package/src/cli/ui/screens/solid/ConfirmScreen.tsx +20 -8
  142. package/src/cli/ui/screens/solid/EnvironmentScreen.tsx +22 -20
  143. package/src/cli/ui/screens/solid/LogScreen.tsx +364 -35
  144. package/src/cli/ui/screens/solid/ProfileEnvScreen.tsx +19 -15
  145. package/src/cli/ui/screens/solid/SelectorScreen.tsx +25 -14
  146. package/src/cli/ui/screens/solid/SettingsScreen.tsx +5 -3
  147. package/src/cli/ui/types.ts +1 -0
  148. package/src/cli/ui/utils/__tests__/branchFormatter.test.ts +53 -6
  149. package/src/cli/ui/utils/branchFormatter.ts +35 -7
  150. package/src/cli/ui/utils/continueSession.ts +90 -3
  151. package/src/cli/ui/utils/versionCache.ts +93 -0
  152. package/src/cli/ui/utils/versionFetcher.ts +120 -0
  153. package/src/codex.ts +62 -20
  154. package/src/config/__tests__/saveSession.test.ts +2 -2
  155. package/src/config/index.ts +11 -1
  156. package/src/gemini.ts +50 -4
  157. package/src/index.test.ts +16 -10
  158. package/src/index.ts +38 -1
  159. package/src/launcher.ts +49 -8
  160. package/src/logging/agentOutput.ts +216 -0
  161. package/src/logging/formatter.ts +23 -4
  162. package/src/logging/logger.ts +2 -0
  163. package/src/logging/reader.ts +117 -0
  164. package/src/services/__tests__/BatchMergeService.test.ts +34 -14
  165. package/src/services/dependency-installer.ts +2 -2
  166. package/src/utils/session/common.ts +28 -0
  167. package/src/utils/session/parsers/claude.ts +79 -29
  168. package/src/utils/session/parsers/codex.ts +50 -26
  169. package/src/utils/session/parsers/gemini.ts +46 -5
  170. package/src/utils/session/parsers/opencode.ts +46 -5
  171. package/src/utils/session/types.ts +4 -0
  172. package/src/worktree.ts +28 -15
@@ -28,6 +28,164 @@ var __export = (target, all) => {
28
28
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
29
  var __require = import.meta.require;
30
30
 
31
+ // src/utils/npmRegistry.ts
32
+ function isPrerelease(version) {
33
+ return /-(alpha|beta|rc|canary|next|dev|pre)\b/i.test(version);
34
+ }
35
+ async function fetchPackageVersions(packageName, limit = DEFAULT_LIMIT, timeoutMs = DEFAULT_TIMEOUT_MS) {
36
+ try {
37
+ const controller = new AbortController;
38
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
39
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {
40
+ signal: controller.signal,
41
+ headers: {
42
+ Accept: "application/json"
43
+ }
44
+ });
45
+ clearTimeout(timeoutId);
46
+ if (!response.ok) {
47
+ return [];
48
+ }
49
+ const data = await response.json();
50
+ if (!data.versions || !data.time) {
51
+ return [];
52
+ }
53
+ const versionsWithTime = [];
54
+ for (const version of Object.keys(data.versions)) {
55
+ const publishedAt = data.time[version];
56
+ if (publishedAt) {
57
+ versionsWithTime.push({ version, publishedAt });
58
+ }
59
+ }
60
+ versionsWithTime.sort((a, b) => {
61
+ const dateA = new Date(a.publishedAt).getTime();
62
+ const dateB = new Date(b.publishedAt).getTime();
63
+ return dateB - dateA;
64
+ });
65
+ const topVersions = versionsWithTime.slice(0, limit);
66
+ return topVersions.map((v) => ({
67
+ version: v.version,
68
+ isPrerelease: isPrerelease(v.version),
69
+ publishedAt: v.publishedAt
70
+ }));
71
+ } catch {
72
+ return [];
73
+ }
74
+ }
75
+ function parsePackageCommand(command) {
76
+ if (command.startsWith("@")) {
77
+ const firstSlash = command.indexOf("/");
78
+ if (firstSlash === -1) {
79
+ return { packageName: command, version: null };
80
+ }
81
+ const afterScope = command.substring(firstSlash + 1);
82
+ const atIndex2 = afterScope.indexOf("@");
83
+ if (atIndex2 === -1) {
84
+ return { packageName: command, version: null };
85
+ }
86
+ const packageName2 = command.substring(0, firstSlash + 1 + atIndex2);
87
+ const version2 = afterScope.substring(atIndex2 + 1);
88
+ return { packageName: packageName2, version: version2 };
89
+ }
90
+ const atIndex = command.indexOf("@");
91
+ if (atIndex === -1) {
92
+ return { packageName: command, version: null };
93
+ }
94
+ const packageName = command.substring(0, atIndex);
95
+ const version = command.substring(atIndex + 1);
96
+ return { packageName, version };
97
+ }
98
+ var DEFAULT_TIMEOUT_MS = 3000, DEFAULT_LIMIT = 10;
99
+
100
+ // src/shared/codingAgentConstants.ts
101
+ var CLAUDE_PERMISSION_SKIP_ARGS, CODEX_DEFAULT_ARGS;
102
+ var init_codingAgentConstants = __esm(() => {
103
+ CLAUDE_PERMISSION_SKIP_ARGS = [
104
+ "--dangerously-skip-permissions"
105
+ ];
106
+ CODEX_DEFAULT_ARGS = [
107
+ "--enable",
108
+ "web_search_request",
109
+ "--model=gpt-5.2-codex",
110
+ "--sandbox",
111
+ "workspace-write",
112
+ "-c",
113
+ "model_reasoning_effort=high",
114
+ "-c",
115
+ "model_reasoning_summaries=detailed",
116
+ "-c",
117
+ "sandbox_workspace_write.network_access=true",
118
+ "-c",
119
+ "shell_environment_policy.inherit=all",
120
+ "-c",
121
+ "shell_environment_policy.ignore_default_excludes=true",
122
+ "-c",
123
+ "shell_environment_policy.experimental_use_profile=true"
124
+ ];
125
+ });
126
+
127
+ // src/config/builtin-coding-agents.ts
128
+ var CLAUDE_CODE_TOOL, CODEX_CLI_TOOL, GEMINI_CLI_TOOL, OPENCODE_TOOL, BUILTIN_CODING_AGENTS;
129
+ var init_builtin_coding_agents = __esm(() => {
130
+ init_codingAgentConstants();
131
+ CLAUDE_CODE_TOOL = {
132
+ id: "claude-code",
133
+ displayName: "Claude Code",
134
+ type: "bunx",
135
+ command: "@anthropic-ai/claude-code",
136
+ modeArgs: {
137
+ normal: [],
138
+ continue: ["-c"],
139
+ resume: ["-r"]
140
+ },
141
+ permissionSkipArgs: Array.from(CLAUDE_PERMISSION_SKIP_ARGS),
142
+ env: {
143
+ ENABLE_LSP_TOOL: "true"
144
+ }
145
+ };
146
+ CODEX_CLI_TOOL = {
147
+ id: "codex-cli",
148
+ displayName: "Codex",
149
+ type: "bunx",
150
+ command: "@openai/codex",
151
+ defaultArgs: Array.from(CODEX_DEFAULT_ARGS),
152
+ modeArgs: {
153
+ normal: [],
154
+ continue: ["resume", "--last"],
155
+ resume: ["resume"]
156
+ }
157
+ };
158
+ GEMINI_CLI_TOOL = {
159
+ id: "gemini-cli",
160
+ displayName: "Gemini",
161
+ type: "bunx",
162
+ command: "@google/gemini-cli",
163
+ modeArgs: {
164
+ normal: [],
165
+ continue: ["-r", "latest"],
166
+ resume: ["-r", "latest"]
167
+ },
168
+ permissionSkipArgs: ["-y"]
169
+ };
170
+ OPENCODE_TOOL = {
171
+ id: "opencode",
172
+ displayName: "OpenCode",
173
+ type: "bunx",
174
+ command: "opencode-ai",
175
+ modeArgs: {
176
+ normal: [],
177
+ continue: ["-c"],
178
+ resume: ["-s"]
179
+ }
180
+ };
181
+ BUILTIN_CODING_AGENTS = [
182
+ CLAUDE_CODE_TOOL,
183
+ CODEX_CLI_TOOL,
184
+ GEMINI_CLI_TOOL,
185
+ OPENCODE_TOOL
186
+ ];
187
+ });
188
+
31
189
  // node_modules/is-plain-obj/index.js
32
190
  function isPlainObject(value) {
33
191
  if (typeof value !== "object" || value === null) {
@@ -3129,12 +3287,12 @@ var init_exports = __esm(() => {
3129
3287
  });
3130
3288
 
3131
3289
  // node_modules/get-stream/source/index.js
3132
- import { on } from "events";
3290
+ import { on as on2 } from "events";
3133
3291
  import { finished } from "stream/promises";
3134
3292
  var init_source = __esm(() => {
3135
3293
  init_stream();
3136
3294
  init_exports();
3137
- Object.assign(nodeImports, { on, finished });
3295
+ Object.assign(nodeImports, { on: on2, finished });
3138
3296
  });
3139
3297
 
3140
3298
  // node_modules/execa/lib/io/max-buffer.js
@@ -5044,7 +5202,7 @@ var init_main_sync = __esm(() => {
5044
5202
  });
5045
5203
 
5046
5204
  // node_modules/execa/lib/ipc/get-one.js
5047
- import { once as once5, on as on2 } from "events";
5205
+ import { once as once5, on as on3 } from "events";
5048
5206
  var getOneMessage = ({ anyProcess, channel, isSubprocess, ipc }, { reference = true, filter } = {}) => {
5049
5207
  validateIpcMethod({
5050
5208
  methodName: "getOneMessage",
@@ -5081,7 +5239,7 @@ var getOneMessage = ({ anyProcess, channel, isSubprocess, ipc }, { reference = t
5081
5239
  const [message] = await once5(ipcEmitter, "message", { signal });
5082
5240
  return message;
5083
5241
  }
5084
- for await (const [message] of on2(ipcEmitter, "message", { signal })) {
5242
+ for await (const [message] of on3(ipcEmitter, "message", { signal })) {
5085
5243
  if (filter(message)) {
5086
5244
  return message;
5087
5245
  }
@@ -5099,7 +5257,7 @@ var init_get_one = __esm(() => {
5099
5257
  });
5100
5258
 
5101
5259
  // node_modules/execa/lib/ipc/get-each.js
5102
- import { once as once6, on as on3 } from "events";
5260
+ import { once as once6, on as on4 } from "events";
5103
5261
  var getEachMessage = ({ anyProcess, channel, isSubprocess, ipc }, { reference = true } = {}) => loopOnMessages({
5104
5262
  anyProcess,
5105
5263
  channel,
@@ -5148,7 +5306,7 @@ var getEachMessage = ({ anyProcess, channel, isSubprocess, ipc }, { reference =
5148
5306
  } catch {}
5149
5307
  }, iterateOnMessages = async function* ({ anyProcess, channel, ipcEmitter, isSubprocess, shouldAwait, controller, state, reference }) {
5150
5308
  try {
5151
- for await (const [message] of on3(ipcEmitter, "message", { signal: controller.signal })) {
5309
+ for await (const [message] of on4(ipcEmitter, "message", { signal: controller.signal })) {
5152
5310
  throwIfStrictError(state);
5153
5311
  yield message;
5154
5312
  }
@@ -5316,7 +5474,7 @@ var init_handle_async = __esm(() => {
5316
5474
  });
5317
5475
 
5318
5476
  // node_modules/@sindresorhus/merge-streams/index.js
5319
- import { on as on4, once as once7 } from "events";
5477
+ import { on as on5, once as once7 } from "events";
5320
5478
  import { PassThrough as PassThroughStream, getDefaultHighWaterMark as getDefaultHighWaterMark2 } from "stream";
5321
5479
  import { finished as finished2 } from "stream/promises";
5322
5480
  function mergeStreams(streams) {
@@ -5364,7 +5522,7 @@ var getHighWaterMark = (streams, objectMode) => {
5364
5522
  throw error;
5365
5523
  }
5366
5524
  }, onInputStreamsUnpipe = async (passThroughStream, streams, unpipeEvent, { signal }) => {
5367
- for await (const [unpipedStream] of on4(passThroughStream, "unpipe", { signal })) {
5525
+ for await (const [unpipedStream] of on5(passThroughStream, "unpipe", { signal })) {
5368
5526
  if (streams.has(unpipedStream)) {
5369
5527
  unpipedStream.emit(unpipeEvent);
5370
5528
  }
@@ -6087,7 +6245,7 @@ var init_setup = __esm(() => {
6087
6245
  });
6088
6246
 
6089
6247
  // node_modules/execa/lib/io/iterate.js
6090
- import { on as on5 } from "events";
6248
+ import { on as on6 } from "events";
6091
6249
  import { getDefaultHighWaterMark as getDefaultHighWaterMark3 } from "stream";
6092
6250
  var iterateOnSubprocessStream = ({ subprocessStdout, subprocess, binary, shouldEncode, encoding, preserveNewlines }) => {
6093
6251
  const controller = new AbortController;
@@ -6129,7 +6287,7 @@ var iterateOnSubprocessStream = ({ subprocessStdout, subprocess, binary, shouldE
6129
6287
  controller.abort();
6130
6288
  }
6131
6289
  }, iterateOnStream = ({ stream, controller, binary, shouldEncode, encoding, shouldSplit, preserveNewlines }) => {
6132
- const onStdoutChunk = on5(stream, "data", {
6290
+ const onStdoutChunk = on6(stream, "data", {
6133
6291
  signal: controller.signal,
6134
6292
  highWaterMark: HIGH_WATER_MARK,
6135
6293
  highWatermark: HIGH_WATER_MARK
@@ -7209,6 +7367,182 @@ var init_execa = __esm(() => {
7209
7367
  } = getIpcExport());
7210
7368
  });
7211
7369
 
7370
+ // src/utils/command.ts
7371
+ import { existsSync as existsSync3 } from "fs";
7372
+ import { homedir } from "os";
7373
+ import { join as join2 } from "path";
7374
+ async function getCommandVersion(commandPath) {
7375
+ try {
7376
+ const result = await execa(commandPath, ["--version"], {
7377
+ timeout: 3000,
7378
+ stdin: "ignore",
7379
+ stdout: "pipe",
7380
+ stderr: "ignore"
7381
+ });
7382
+ const match = result.stdout.match(/(\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?)/);
7383
+ return match ? `v${match[1]}` : null;
7384
+ } catch {
7385
+ return null;
7386
+ }
7387
+ }
7388
+ async function findCommand(commandName) {
7389
+ const cached = commandLookupCache.get(commandName);
7390
+ if (cached) {
7391
+ return cached;
7392
+ }
7393
+ let lookupResult = null;
7394
+ try {
7395
+ const lookupCommand = process.platform === "win32" ? "where" : "which";
7396
+ const execResult = await execa(lookupCommand, [commandName], {
7397
+ shell: true,
7398
+ stdin: "ignore",
7399
+ stdout: "pipe",
7400
+ stderr: "ignore"
7401
+ });
7402
+ const foundPath = execResult.stdout.trim().split(`
7403
+ `)[0];
7404
+ if (foundPath) {
7405
+ lookupResult = { available: true, path: foundPath, source: "installed" };
7406
+ }
7407
+ } catch {}
7408
+ if (!lookupResult) {
7409
+ const knownPaths = KNOWN_INSTALL_PATHS[commandName];
7410
+ if (knownPaths) {
7411
+ const pathsToCheck = process.platform === "win32" ? knownPaths.win32 : knownPaths.unix;
7412
+ for (const p of pathsToCheck) {
7413
+ if (p && existsSync3(p)) {
7414
+ lookupResult = { available: true, path: p, source: "installed" };
7415
+ break;
7416
+ }
7417
+ }
7418
+ }
7419
+ }
7420
+ if (!lookupResult) {
7421
+ lookupResult = { available: true, path: null, source: "bunx" };
7422
+ }
7423
+ if (lookupResult.source === "installed" && lookupResult.path) {
7424
+ lookupResult.version = await getCommandVersion(lookupResult.path);
7425
+ } else {
7426
+ lookupResult.version = null;
7427
+ }
7428
+ commandLookupCache.set(commandName, lookupResult);
7429
+ return lookupResult;
7430
+ }
7431
+ var KNOWN_INSTALL_PATHS, commandLookupCache;
7432
+ var init_command3 = __esm(() => {
7433
+ init_execa();
7434
+ KNOWN_INSTALL_PATHS = {
7435
+ claude: {
7436
+ unix: [
7437
+ join2(homedir(), ".bun", "bin", "claude"),
7438
+ join2(homedir(), ".local", "bin", "claude"),
7439
+ "/usr/local/bin/claude"
7440
+ ],
7441
+ win32: [
7442
+ join2(process.env.LOCALAPPDATA ?? "", "Programs", "claude", "claude.exe"),
7443
+ join2(homedir(), ".bun", "bin", "claude.exe")
7444
+ ]
7445
+ },
7446
+ codex: {
7447
+ unix: [
7448
+ join2(homedir(), ".bun", "bin", "codex"),
7449
+ join2(homedir(), ".local", "bin", "codex"),
7450
+ "/usr/local/bin/codex"
7451
+ ],
7452
+ win32: [join2(homedir(), ".bun", "bin", "codex.exe")]
7453
+ },
7454
+ gemini: {
7455
+ unix: [
7456
+ join2(homedir(), ".bun", "bin", "gemini"),
7457
+ join2(homedir(), ".local", "bin", "gemini"),
7458
+ "/usr/local/bin/gemini"
7459
+ ],
7460
+ win32: [join2(homedir(), ".bun", "bin", "gemini.exe")]
7461
+ },
7462
+ opencode: {
7463
+ unix: [
7464
+ join2(homedir(), ".bun", "bin", "opencode"),
7465
+ join2(homedir(), ".local", "bin", "opencode"),
7466
+ "/usr/local/bin/opencode"
7467
+ ],
7468
+ win32: [join2(homedir(), ".bun", "bin", "opencode.exe")]
7469
+ }
7470
+ };
7471
+ commandLookupCache = new Map;
7472
+ });
7473
+
7474
+ // src/cli/ui/utils/versionFetcher.ts
7475
+ var exports_versionFetcher = {};
7476
+ __export(exports_versionFetcher, {
7477
+ versionInfoToSelectItem: () => versionInfoToSelectItem,
7478
+ getPackageNameForAgent: () => getPackageNameForAgent,
7479
+ getBunxAgentIds: () => getBunxAgentIds,
7480
+ fetchVersionOptionsForAgent: () => fetchVersionOptionsForAgent,
7481
+ fetchInstalledVersionForAgent: () => fetchInstalledVersionForAgent,
7482
+ createInstalledOption: () => createInstalledOption
7483
+ });
7484
+ function getPackageNameForAgent(agentId) {
7485
+ const agent = BUILTIN_CODING_AGENTS.find((a2) => a2.id === agentId);
7486
+ if (!agent || agent.type !== "bunx") {
7487
+ return null;
7488
+ }
7489
+ const { packageName } = parsePackageCommand(agent.command);
7490
+ return packageName;
7491
+ }
7492
+ async function fetchVersionOptionsForAgent(agentId) {
7493
+ const packageName = getPackageNameForAgent(agentId);
7494
+ if (!packageName) {
7495
+ return [];
7496
+ }
7497
+ return fetchPackageVersions(packageName);
7498
+ }
7499
+ async function fetchInstalledVersionForAgent(agentId) {
7500
+ const commandName = AGENT_COMMAND_MAP[agentId];
7501
+ if (!commandName) {
7502
+ return null;
7503
+ }
7504
+ const result = await findCommand(commandName);
7505
+ if (result.source !== "installed" || !result.path) {
7506
+ return null;
7507
+ }
7508
+ const version = result.version?.replace(/^v/, "") ?? "unknown";
7509
+ return {
7510
+ version,
7511
+ path: result.path
7512
+ };
7513
+ }
7514
+ function versionInfoToSelectItem(v) {
7515
+ const item = {
7516
+ label: v.isPrerelease ? `${v.version} (pre)` : v.version,
7517
+ value: v.version
7518
+ };
7519
+ if (v.publishedAt) {
7520
+ item.description = new Date(v.publishedAt).toLocaleDateString();
7521
+ }
7522
+ return item;
7523
+ }
7524
+ function createInstalledOption(installed) {
7525
+ return {
7526
+ label: `installed@${installed.version}`,
7527
+ value: "installed",
7528
+ description: installed.path
7529
+ };
7530
+ }
7531
+ function getBunxAgentIds() {
7532
+ return BUILTIN_CODING_AGENTS.filter((a2) => a2.type === "bunx").map((a2) => a2.id);
7533
+ }
7534
+ var AGENT_COMMAND_MAP;
7535
+ var init_versionFetcher = __esm(() => {
7536
+ init_builtin_coding_agents();
7537
+ init_command3();
7538
+ AGENT_COMMAND_MAP = {
7539
+ "claude-code": "claude",
7540
+ "codex-cli": "codex",
7541
+ "gemini-cli": "gemini",
7542
+ opencode: "opencode"
7543
+ };
7544
+ });
7545
+
7212
7546
  // node_modules/pino-std-serializers/lib/err-helpers.js
7213
7547
  var require_err_helpers = __commonJS((exports, module2) => {
7214
7548
  var isErrorLike = (err) => {
@@ -42788,6 +43122,9 @@ function createResource(pSource, pFetcher, pOptions) {
42788
43122
  mutate: setValue
42789
43123
  }];
42790
43124
  }
43125
+ function batch(fn) {
43126
+ return runUpdates(fn, false);
43127
+ }
42791
43128
  function untrack(fn) {
42792
43129
  if (!ExternalSourceConfig && Listener === null)
42793
43130
  return fn();
@@ -42801,6 +43138,27 @@ function untrack(fn) {
42801
43138
  Listener = listener;
42802
43139
  }
42803
43140
  }
43141
+ function on(deps, fn, options) {
43142
+ const isArray = Array.isArray(deps);
43143
+ let prevInput;
43144
+ let defer = options && options.defer;
43145
+ return (prevValue) => {
43146
+ let input;
43147
+ if (isArray) {
43148
+ input = Array(deps.length);
43149
+ for (let i = 0;i < deps.length; i++)
43150
+ input[i] = deps[i]();
43151
+ } else
43152
+ input = deps();
43153
+ if (defer) {
43154
+ defer = false;
43155
+ return prevValue;
43156
+ }
43157
+ const result = untrack(() => fn(input, prevInput, prevValue));
43158
+ prevInput = input;
43159
+ return result;
43160
+ };
43161
+ }
42804
43162
  function onMount(fn) {
42805
43163
  createEffect(() => untrack(fn));
42806
43164
  }
@@ -44312,14 +44670,36 @@ function mapToolLabel(toolId, toolLabel) {
44312
44670
  return toolLabel;
44313
44671
  return "Custom";
44314
44672
  }
44673
+ var LOCAL_DATE_TIME_FORMATTER = new Intl.DateTimeFormat(undefined, {
44674
+ year: "numeric",
44675
+ month: "2-digit",
44676
+ day: "2-digit",
44677
+ hour: "2-digit",
44678
+ minute: "2-digit",
44679
+ hour12: false
44680
+ });
44681
+ var formatLocalDateTimeParts = (date) => {
44682
+ const parts = LOCAL_DATE_TIME_FORMATTER.formatToParts(date);
44683
+ const get = (type) => parts.find((part) => part.type === type)?.value;
44684
+ const year = get("year");
44685
+ const month = get("month");
44686
+ const day = get("day");
44687
+ const hour = get("hour");
44688
+ const minute = get("minute");
44689
+ if (!year || !month || !day || !hour || !minute) {
44690
+ return LOCAL_DATE_TIME_FORMATTER.format(date);
44691
+ }
44692
+ return `${year}-${month}-${day} ${hour}:${minute}`;
44693
+ };
44694
+ function formatLocalDateTime(timestampMs) {
44695
+ const date = new Date(timestampMs);
44696
+ if (Number.isNaN(date.getTime())) {
44697
+ return "";
44698
+ }
44699
+ return formatLocalDateTimeParts(date);
44700
+ }
44315
44701
  function formatTimestamp(ts) {
44316
- const date = new Date(ts);
44317
- const year = date.getFullYear();
44318
- const month = String(date.getMonth() + 1).padStart(2, "0");
44319
- const day = String(date.getDate()).padStart(2, "0");
44320
- const hours = String(date.getHours()).padStart(2, "0");
44321
- const minutes = String(date.getMinutes()).padStart(2, "0");
44322
- return `${year}-${month}-${day} ${hours}:${minutes}`;
44702
+ return formatLocalDateTime(ts);
44323
44703
  }
44324
44704
  function buildLastToolUsageLabel(usage) {
44325
44705
  if (!usage)
@@ -44616,6 +44996,98 @@ function Header({
44616
44996
  })();
44617
44997
  }
44618
44998
 
44999
+ // src/cli/ui/core/theme.ts
45000
+ var colors = {
45001
+ primary: "cyan",
45002
+ secondary: "gray",
45003
+ success: "green",
45004
+ warning: "yellow",
45005
+ error: "red",
45006
+ info: "blue",
45007
+ text: "white",
45008
+ textDim: "gray",
45009
+ textMuted: "gray",
45010
+ selected: "cyan",
45011
+ highlighted: "cyan",
45012
+ disabled: "gray",
45013
+ selectionBackground: "cyan",
45014
+ selectionText: "black",
45015
+ branchFeature: "green",
45016
+ branchBugfix: "yellow",
45017
+ branchHotfix: "red",
45018
+ branchRelease: "magenta",
45019
+ branchMain: "cyan",
45020
+ branchDevelop: "blue",
45021
+ branchOther: "gray",
45022
+ syncUpToDate: "green",
45023
+ syncAhead: "cyan",
45024
+ syncBehind: "yellow",
45025
+ syncDiverged: "red",
45026
+ syncNoUpstream: "gray",
45027
+ worktreeActive: "green",
45028
+ worktreeInaccessible: "red",
45029
+ progressFilled: "green",
45030
+ progressEmpty: "gray",
45031
+ progressPhase: "yellow",
45032
+ progressTime: "magenta"
45033
+ };
45034
+ var logLevelColors = {
45035
+ TRACE: colors.textDim,
45036
+ DEBUG: colors.info,
45037
+ INFO: colors.success,
45038
+ WARN: colors.warning,
45039
+ ERROR: colors.error,
45040
+ FATAL: colors.error
45041
+ };
45042
+ var selectionStyle = {
45043
+ fg: colors.selectionText,
45044
+ bg: colors.selectionBackground
45045
+ };
45046
+ var getLogLevelColor = (label) => {
45047
+ if (!label) {
45048
+ return colors.text;
45049
+ }
45050
+ const normalized = label.toUpperCase();
45051
+ return logLevelColors[normalized] ?? colors.text;
45052
+ };
45053
+ var icons = {
45054
+ pointer: ">",
45055
+ pointerBold: ">",
45056
+ arrowUp: "^",
45057
+ arrowDown: "v",
45058
+ arrowLeft: "<",
45059
+ arrowRight: ">",
45060
+ success: "OK",
45061
+ error: "X",
45062
+ warning: "!",
45063
+ info: "i",
45064
+ skipped: "-",
45065
+ worktree: "W",
45066
+ worktreeActive: "A",
45067
+ worktreeInaccessible: "X",
45068
+ changes: "C",
45069
+ unpushed: "U",
45070
+ current: "*",
45071
+ pr: "PR",
45072
+ merged: "M",
45073
+ syncUpToDate: "=",
45074
+ syncAhead: ">",
45075
+ syncBehind: "<",
45076
+ syncDiverged: "!",
45077
+ spinnerFrames: ["|", "/", "-", "\\"],
45078
+ bullet: "*",
45079
+ divider: "-",
45080
+ verticalLine: "|",
45081
+ corner: "+",
45082
+ branch: "+"
45083
+ };
45084
+ var statsItemsConfig = {
45085
+ local: { label: "Local", icon: "L", color: colors.primary },
45086
+ remote: { label: "Remote", icon: "R", color: colors.info },
45087
+ worktree: { label: "Worktrees", icon: icons.worktree, color: colors.success },
45088
+ changes: { label: "Changes", icon: icons.changes, color: colors.warning }
45089
+ };
45090
+
44619
45091
  // src/cli/ui/screens/solid/BranchListScreen.tsx
44620
45092
  var VIEW_MODES = ["all", "local", "remote"];
44621
45093
  var getCharWidth = (char) => stringWidth(char);
@@ -44724,8 +45196,8 @@ var fitSegmentsToWidth = (segments, width) => {
44724
45196
  };
44725
45197
  var applySelectionStyle = (segments) => segments.map((segment) => ({
44726
45198
  text: segment.text,
44727
- fg: "black",
44728
- bg: "cyan"
45199
+ fg: selectionStyle.fg,
45200
+ bg: selectionStyle.bg
44729
45201
  }));
44730
45202
  var CLEANUP_SPINNER_FRAMES = ["-", "\\", "|", "/"];
44731
45203
  var CURSOR_FRAMES = ["|", " "];
@@ -44805,7 +45277,13 @@ function BranchListScreen(props) {
44805
45277
  const [filterQuery, setFilterQuery] = createSignal("");
44806
45278
  const [filterMode, setFilterMode] = createSignal(false);
44807
45279
  const [viewMode, setViewMode] = createSignal("all");
44808
- const [selectedIndex, setSelectedIndex] = createSignal(0);
45280
+ const [internalSelectedIndex, setInternalSelectedIndex] = createSignal(props.cursorPosition ?? 0);
45281
+ const selectedIndex = () => props.cursorPosition ?? internalSelectedIndex();
45282
+ const setSelectedIndex = (value) => {
45283
+ const newValue = typeof value === "function" ? value(selectedIndex()) : value;
45284
+ setInternalSelectedIndex(newValue);
45285
+ props.onCursorPositionChange?.(newValue);
45286
+ };
44809
45287
  const [scrollOffset, setScrollOffset] = createSignal(0);
44810
45288
  const [cleanupSpinnerIndex, setCleanupSpinnerIndex] = createSignal(0);
44811
45289
  const [cursorIndex, setCursorIndex] = createSignal(0);
@@ -44833,7 +45311,14 @@ function BranchListScreen(props) {
44833
45311
  return Object.values(props.cleanupUI.indicators).some((indicator) => indicator.isSpinning);
44834
45312
  });
44835
45313
  const hasSpinningFooter = createMemo(() => props.cleanupUI?.footerMessage?.isSpinning ?? false);
44836
- const cleanupSpinnerActive = createMemo(() => hasSpinningIndicator() || hasSpinningFooter() || props.cleanupUI?.safetyLoading === true);
45314
+ const safetyPendingActive = createMemo(() => {
45315
+ const pending = props.cleanupUI?.safetyPendingBranches;
45316
+ if (pending) {
45317
+ return pending.size > 0;
45318
+ }
45319
+ return props.cleanupUI?.safetyLoading === true;
45320
+ });
45321
+ const cleanupSpinnerActive = createMemo(() => hasSpinningIndicator() || hasSpinningFooter() || safetyPendingActive());
44837
45322
  createEffect(() => {
44838
45323
  let intervalTimer;
44839
45324
  if (cleanupSpinnerActive()) {
@@ -44884,12 +45369,12 @@ function BranchListScreen(props) {
44884
45369
  }
44885
45370
  return maxWidth;
44886
45371
  });
44887
- createEffect(() => {
44888
- filterQuery();
44889
- viewMode();
45372
+ createEffect(on(() => [filterQuery(), viewMode()], () => {
44890
45373
  setSelectedIndex(0);
44891
45374
  setScrollOffset(0);
44892
- });
45375
+ }, {
45376
+ defer: true
45377
+ }));
44893
45378
  createEffect(() => {
44894
45379
  const total = filteredBranches().length;
44895
45380
  if (total === 0) {
@@ -44937,18 +45422,16 @@ function BranchListScreen(props) {
44937
45422
  if (!timestamp || Number.isNaN(timestamp)) {
44938
45423
  return "---";
44939
45424
  }
44940
- const date = new Date(timestamp * 1000);
44941
- const year = date.getFullYear();
44942
- const month = String(date.getMonth() + 1).padStart(2, "0");
44943
- const day = String(date.getDate()).padStart(2, "0");
44944
- const hours = String(date.getHours()).padStart(2, "0");
44945
- const minutes = String(date.getMinutes()).padStart(2, "0");
44946
- return `${year}-${month}-${day} ${hours}:${minutes}`;
45425
+ const formatted = formatLocalDateTime(timestamp * 1000);
45426
+ return formatted || "---";
44947
45427
  };
44948
45428
  useKeyboard((key) => {
44949
45429
  if (props.wizardVisible) {
44950
45430
  return;
44951
45431
  }
45432
+ if (props.confirmVisible) {
45433
+ return;
45434
+ }
44952
45435
  if (props.helpVisible) {
44953
45436
  return;
44954
45437
  }
@@ -45054,7 +45537,8 @@ function BranchListScreen(props) {
45054
45537
  } else if (key.name === "p" || key.sequence === "p") {
45055
45538
  props.onOpenProfiles?.();
45056
45539
  } else if (key.name === "l" || key.sequence === "l") {
45057
- props.onOpenLogs?.();
45540
+ const selected = filteredBranches()[selectedIndex()] ?? null;
45541
+ props.onOpenLogs?.(selected);
45058
45542
  }
45059
45543
  });
45060
45544
  const visibleBranches = createMemo(() => {
@@ -45082,19 +45566,24 @@ function BranchListScreen(props) {
45082
45566
  let worktreeColor = "gray";
45083
45567
  if (branch.worktreeStatus === "active") {
45084
45568
  worktreeIcon = "w";
45085
- worktreeColor = "green";
45569
+ worktreeColor = "brightGreen";
45086
45570
  } else if (branch.worktreeStatus === "inaccessible") {
45087
45571
  worktreeIcon = "x";
45088
45572
  worktreeColor = "red";
45089
45573
  }
45090
45574
  const isRemoteBranch = branch.type === "remote";
45091
45575
  const safetyLoading = props.cleanupUI?.safetyLoading === true;
45576
+ const safetyPendingBranches = props.cleanupUI?.safetyPendingBranches;
45577
+ const isSafetyPending = safetyPendingBranches ? safetyPendingBranches.has(branch.name) : safetyLoading;
45092
45578
  const spinnerFrame = cleanupSpinnerFrame();
45093
45579
  let safeIcon = " ";
45094
45580
  let safeColor;
45095
45581
  if (isRemoteBranch) {
45096
45582
  safeIcon = " ";
45097
45583
  safeColor = undefined;
45584
+ } else if (isSafetyPending) {
45585
+ safeIcon = spinnerFrame ?? "-";
45586
+ safeColor = undefined;
45098
45587
  } else if (hasUncommitted) {
45099
45588
  safeIcon = "!";
45100
45589
  safeColor = "red";
@@ -45104,12 +45593,9 @@ function BranchListScreen(props) {
45104
45593
  } else if (branch.isUnmerged) {
45105
45594
  safeIcon = "*";
45106
45595
  safeColor = "yellow";
45107
- } else if (safetyLoading) {
45108
- safeIcon = spinnerFrame ?? "-";
45109
- safeColor = undefined;
45110
45596
  } else if (branch.safeToCleanup === true) {
45111
- safeIcon = " ";
45112
- safeColor = undefined;
45597
+ safeIcon = "o";
45598
+ safeColor = "brightGreen";
45113
45599
  } else {
45114
45600
  safeIcon = "!";
45115
45601
  safeColor = "red";
@@ -45316,6 +45802,63 @@ function BranchListScreen(props) {
45316
45802
  }
45317
45803
  return fitSegmentsToWidth(segments, layoutWidth());
45318
45804
  });
45805
+ const statusLegendSegments = createMemo(() => {
45806
+ const segments = [];
45807
+ const separator = " ";
45808
+ appendSegment(segments, {
45809
+ text: "Legend: ",
45810
+ attributes: TextAttributes.DIM
45811
+ });
45812
+ appendSegment(segments, {
45813
+ text: "o",
45814
+ fg: "brightGreen",
45815
+ attributes: TextAttributes.BOLD
45816
+ });
45817
+ appendSegment(segments, {
45818
+ text: " Safe",
45819
+ fg: "brightGreen"
45820
+ });
45821
+ appendSegment(segments, {
45822
+ text: separator,
45823
+ attributes: TextAttributes.DIM
45824
+ });
45825
+ appendSegment(segments, {
45826
+ text: "!",
45827
+ fg: "red",
45828
+ attributes: TextAttributes.BOLD
45829
+ });
45830
+ appendSegment(segments, {
45831
+ text: " Uncommitted",
45832
+ fg: "red"
45833
+ });
45834
+ appendSegment(segments, {
45835
+ text: separator,
45836
+ attributes: TextAttributes.DIM
45837
+ });
45838
+ appendSegment(segments, {
45839
+ text: "!",
45840
+ fg: "yellow",
45841
+ attributes: TextAttributes.BOLD
45842
+ });
45843
+ appendSegment(segments, {
45844
+ text: " Unpushed",
45845
+ fg: "yellow"
45846
+ });
45847
+ appendSegment(segments, {
45848
+ text: separator,
45849
+ attributes: TextAttributes.DIM
45850
+ });
45851
+ appendSegment(segments, {
45852
+ text: "*",
45853
+ fg: "yellow",
45854
+ attributes: TextAttributes.BOLD
45855
+ });
45856
+ appendSegment(segments, {
45857
+ text: " Unmerged",
45858
+ fg: "yellow"
45859
+ });
45860
+ return fitSegmentsToWidth(segments, layoutWidth());
45861
+ });
45319
45862
  const footerSegments = createMemo(() => {
45320
45863
  const segments = [];
45321
45864
  const separator = " ";
@@ -45411,50 +45954,46 @@ function BranchListScreen(props) {
45411
45954
  }
45412
45955
  }, () => props.loadingIndicatorDelay !== undefined ? {
45413
45956
  delay: props.loadingIndicatorDelay
45414
- } : {})) : (() => {
45415
- var _el$7 = createElement("text");
45416
- insert(_el$7, () => padLine("", layoutWidth()));
45417
- return _el$7;
45418
- })();
45957
+ } : {})) : renderSegmentLine(statusLegendSegments());
45419
45958
  })(), null);
45420
45959
  insert(_el$5, (() => {
45421
45960
  var _c$2 = memo2(() => !!props.error);
45422
45961
  return () => _c$2() && [(() => {
45962
+ var _el$7 = createElement("text");
45963
+ setProp(_el$7, "fg", "red");
45964
+ insert(_el$7, () => padLine(`Error: ${props.error.message}`, layoutWidth()));
45965
+ effect((_$p) => setProp(_el$7, "attributes", TextAttributes.BOLD, _$p));
45966
+ return _el$7;
45967
+ })(), memo2(() => memo2(() => !!(process.env.DEBUG && props.error.stack))() && [(() => {
45423
45968
  var _el$8 = createElement("text");
45424
- setProp(_el$8, "fg", "red");
45425
- insert(_el$8, () => padLine(`Error: ${props.error.message}`, layoutWidth()));
45426
- effect((_$p) => setProp(_el$8, "attributes", TextAttributes.BOLD, _$p));
45969
+ insert(_el$8, () => padLine("", layoutWidth()));
45970
+ effect((_$p) => setProp(_el$8, "attributes", TextAttributes.DIM, _$p));
45427
45971
  return _el$8;
45428
- })(), memo2(() => memo2(() => !!(process.env.DEBUG && props.error.stack))() && [(() => {
45972
+ })(), memo2(() => props.error.stack.split(`
45973
+ `).map((line) => (() => {
45429
45974
  var _el$9 = createElement("text");
45430
- insert(_el$9, () => padLine("", layoutWidth()));
45975
+ setProp(_el$9, "fg", "gray");
45976
+ insert(_el$9, () => padLine(line, layoutWidth()));
45431
45977
  effect((_$p) => setProp(_el$9, "attributes", TextAttributes.DIM, _$p));
45432
45978
  return _el$9;
45433
- })(), memo2(() => props.error.stack.split(`
45434
- `).map((line) => (() => {
45435
- var _el$0 = createElement("text");
45436
- setProp(_el$0, "fg", "gray");
45437
- insert(_el$0, () => padLine(line, layoutWidth()));
45438
- effect((_$p) => setProp(_el$0, "attributes", TextAttributes.DIM, _$p));
45439
- return _el$0;
45440
45979
  })()))])];
45441
45980
  })(), null);
45442
45981
  insert(_el$5, (() => {
45443
45982
  var _c$3 = memo2(() => !!(!props.loading && !props.error && props.branches.length === 0));
45444
45983
  return () => _c$3() && (() => {
45445
- var _el$1 = createElement("text");
45446
- insert(_el$1, () => padLine("No branches found", layoutWidth()));
45447
- effect((_$p) => setProp(_el$1, "attributes", TextAttributes.DIM, _$p));
45448
- return _el$1;
45984
+ var _el$0 = createElement("text");
45985
+ insert(_el$0, () => padLine("No branches found", layoutWidth()));
45986
+ effect((_$p) => setProp(_el$0, "attributes", TextAttributes.DIM, _$p));
45987
+ return _el$0;
45449
45988
  })();
45450
45989
  })(), null);
45451
45990
  insert(_el$5, (() => {
45452
45991
  var _c$4 = memo2(() => !!(!props.loading && !props.error && props.branches.length > 0 && filteredBranches().length === 0 && filterQuery()));
45453
45992
  return () => _c$4() && (() => {
45454
- var _el$10 = createElement("text");
45455
- insert(_el$10, () => padLine("No branches match your filter", layoutWidth()));
45456
- effect((_$p) => setProp(_el$10, "attributes", TextAttributes.DIM, _$p));
45457
- return _el$10;
45993
+ var _el$1 = createElement("text");
45994
+ insert(_el$1, () => padLine("No branches match your filter", layoutWidth()));
45995
+ effect((_$p) => setProp(_el$1, "attributes", TextAttributes.DIM, _$p));
45996
+ return _el$1;
45458
45997
  })();
45459
45998
  })(), null);
45460
45999
  insert(_el$5, (() => {
@@ -45465,20 +46004,20 @@ function BranchListScreen(props) {
45465
46004
  })), memo2(() => Array.from({
45466
46005
  length: Math.max(0, contentHeight() - visibleBranches().length)
45467
46006
  }).map(() => (() => {
45468
- var _el$11 = createElement("text");
45469
- insert(_el$11, () => padLine("", listWidth()));
45470
- return _el$11;
46007
+ var _el$10 = createElement("text");
46008
+ insert(_el$10, () => padLine("", listWidth()));
46009
+ return _el$10;
45471
46010
  })()))];
45472
46011
  })(), null);
45473
46012
  insert(_el$4, (() => {
45474
46013
  var _c$6 = memo2(() => !!props.cleanupUI?.footerMessage);
45475
46014
  return () => _c$6() && (() => {
45476
- var _el$12 = createElement("text");
45477
- spread(_el$12, mergeProps3(() => props.cleanupUI.footerMessage.color ? {
46015
+ var _el$11 = createElement("text");
46016
+ spread(_el$11, mergeProps3(() => props.cleanupUI.footerMessage.color ? {
45478
46017
  fg: props.cleanupUI.footerMessage.color
45479
46018
  } : {}), true);
45480
- insert(_el$12, () => padLine(props.cleanupUI.footerMessage.isSpinning && cleanupSpinnerFrame() ? `${cleanupSpinnerFrame()} ${props.cleanupUI.footerMessage.text}` : props.cleanupUI.footerMessage.text, layoutWidth()));
45481
- return _el$12;
46019
+ insert(_el$11, () => padLine(props.cleanupUI.footerMessage.isSpinning && cleanupSpinnerFrame() ? `${cleanupSpinnerFrame()} ${props.cleanupUI.footerMessage.text}` : props.cleanupUI.footerMessage.text, layoutWidth()));
46020
+ return _el$11;
45482
46021
  })();
45483
46022
  })(), _el$6);
45484
46023
  insert(_el$6, () => padLine(`Worktree: ${selectedWorktreeLabel()}`, layoutWidth()));
@@ -46009,6 +46548,15 @@ function SelectInput(props) {
46009
46548
  },
46010
46549
  get height() {
46011
46550
  return computedHeight();
46551
+ },
46552
+ get selectedBackgroundColor() {
46553
+ return selectionStyle.bg;
46554
+ },
46555
+ get selectedTextColor() {
46556
+ return selectionStyle.fg;
46557
+ },
46558
+ get selectedDescriptionColor() {
46559
+ return selectionStyle.fg;
46012
46560
  }
46013
46561
  }, () => props.selectedIndex !== undefined && {
46014
46562
  selectedIndex: props.selectedIndex
@@ -46040,30 +46588,46 @@ function QuickStartStep(props) {
46040
46588
  props.onChooseDifferent();
46041
46589
  }
46042
46590
  });
46043
- const latestEntry = createMemo(() => props.history[0] ?? null);
46591
+ const buildSettingsDescription = (entry) => {
46592
+ const parts = [];
46593
+ if (entry.toolLabel) {
46594
+ parts.push(entry.toolLabel);
46595
+ } else if (entry.toolId) {
46596
+ parts.push(entry.toolId);
46597
+ }
46598
+ if (entry.model) {
46599
+ parts.push(entry.model);
46600
+ }
46601
+ if (entry.toolId === "codex-cli" && entry.reasoningLevel) {
46602
+ parts.push(entry.reasoningLevel);
46603
+ }
46604
+ return parts.join(", ");
46605
+ };
46044
46606
  const items = createMemo(() => {
46045
46607
  const result = [];
46046
- const entry = latestEntry();
46047
- if (entry) {
46048
- const reasoningInfo = entry.reasoningLevel ? `, ${entry.reasoningLevel}` : "";
46049
- const versionInfo = entry.toolVersion ? `@${entry.toolVersion}` : "";
46050
- const settingsDesc = `${entry.toolLabel}${versionInfo}, ${entry.model}${reasoningInfo}`;
46051
- const resumeDesc = entry.sessionId ? `${settingsDesc}, session ${entry.sessionId}` : settingsDesc;
46052
- result.push({
46053
- label: "Resume session (previous settings)",
46054
- value: `resume-${entry.toolId}`,
46055
- description: resumeDesc,
46056
- action: "resume",
46057
- entry
46058
- });
46608
+ props.history.forEach((entry, index) => {
46609
+ if (!entry)
46610
+ return;
46611
+ const settingsDesc = buildSettingsDescription(entry);
46612
+ const sessionId = entry.sessionId && entry.sessionId.trim().length > 0 ? entry.sessionId.trim() : null;
46613
+ const suffix = `${entry.toolId ?? "tool"}-${index}`;
46614
+ if (sessionId) {
46615
+ result.push({
46616
+ label: "Resume session (previous settings)",
46617
+ value: `resume-${suffix}`,
46618
+ description: `${settingsDesc} | Session: ${sessionId}`,
46619
+ action: "resume",
46620
+ entry
46621
+ });
46622
+ }
46059
46623
  result.push({
46060
46624
  label: "Start new (previous settings)",
46061
- value: `start-new-${entry.toolId}`,
46625
+ value: `start-new-${suffix}`,
46062
46626
  description: settingsDesc,
46063
46627
  action: "start-new",
46064
46628
  entry
46065
46629
  });
46066
- }
46630
+ });
46067
46631
  result.push({
46068
46632
  label: "Choose different settings...",
46069
46633
  value: "choose-different",
@@ -46366,260 +46930,31 @@ function normalizeModelId(tool, model) {
46366
46930
  return trimmed;
46367
46931
  }
46368
46932
 
46369
- // src/utils/npmRegistry.ts
46370
- var DEFAULT_TIMEOUT_MS = 3000;
46371
- var DEFAULT_LIMIT = 10;
46372
- function isPrerelease(version) {
46373
- return /-(alpha|beta|rc|canary|next|dev|pre)\b/i.test(version);
46933
+ // src/cli/ui/utils/versionCache.ts
46934
+ var versionCache = new Map;
46935
+ function getVersionCache(agentId) {
46936
+ const cached = versionCache.get(agentId);
46937
+ return cached !== undefined ? cached : null;
46374
46938
  }
46375
- async function fetchPackageVersions(packageName, limit = DEFAULT_LIMIT, timeoutMs = DEFAULT_TIMEOUT_MS) {
46376
- try {
46377
- const controller = new AbortController;
46378
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
46379
- const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {
46380
- signal: controller.signal,
46381
- headers: {
46382
- Accept: "application/json"
46383
- }
46384
- });
46385
- clearTimeout(timeoutId);
46386
- if (!response.ok) {
46387
- return [];
46388
- }
46389
- const data = await response.json();
46390
- if (!data.versions || !data.time) {
46391
- return [];
46392
- }
46393
- const versionsWithTime = [];
46394
- for (const version of Object.keys(data.versions)) {
46395
- const publishedAt = data.time[version];
46396
- if (publishedAt) {
46397
- versionsWithTime.push({ version, publishedAt });
46398
- }
46399
- }
46400
- versionsWithTime.sort((a, b) => {
46401
- const dateA = new Date(a.publishedAt).getTime();
46402
- const dateB = new Date(b.publishedAt).getTime();
46403
- return dateB - dateA;
46404
- });
46405
- const topVersions = versionsWithTime.slice(0, limit);
46406
- return topVersions.map((v) => ({
46407
- version: v.version,
46408
- isPrerelease: isPrerelease(v.version),
46409
- publishedAt: v.publishedAt
46410
- }));
46411
- } catch {
46412
- return [];
46413
- }
46414
- }
46415
- function parsePackageCommand(command) {
46416
- if (command.startsWith("@")) {
46417
- const firstSlash = command.indexOf("/");
46418
- if (firstSlash === -1) {
46419
- return { packageName: command, version: null };
46420
- }
46421
- const afterScope = command.substring(firstSlash + 1);
46422
- const atIndex2 = afterScope.indexOf("@");
46423
- if (atIndex2 === -1) {
46424
- return { packageName: command, version: null };
46425
- }
46426
- const packageName2 = command.substring(0, firstSlash + 1 + atIndex2);
46427
- const version2 = afterScope.substring(atIndex2 + 1);
46428
- return { packageName: packageName2, version: version2 };
46429
- }
46430
- const atIndex = command.indexOf("@");
46431
- if (atIndex === -1) {
46432
- return { packageName: command, version: null };
46433
- }
46434
- const packageName = command.substring(0, atIndex);
46435
- const version = command.substring(atIndex + 1);
46436
- return { packageName, version };
46437
- }
46438
-
46439
- // src/shared/codingAgentConstants.ts
46440
- var CLAUDE_PERMISSION_SKIP_ARGS = [
46441
- "--dangerously-skip-permissions"
46442
- ];
46443
- var CODEX_DEFAULT_ARGS = [
46444
- "--enable",
46445
- "web_search_request",
46446
- "--model=gpt-5.2-codex",
46447
- "--sandbox",
46448
- "workspace-write",
46449
- "-c",
46450
- "model_reasoning_effort=high",
46451
- "-c",
46452
- "model_reasoning_summaries=detailed",
46453
- "-c",
46454
- "sandbox_workspace_write.network_access=true",
46455
- "-c",
46456
- "shell_environment_policy.inherit=all",
46457
- "-c",
46458
- "shell_environment_policy.ignore_default_excludes=true",
46459
- "-c",
46460
- "shell_environment_policy.experimental_use_profile=true"
46461
- ];
46462
-
46463
- // src/config/builtin-coding-agents.ts
46464
- var CLAUDE_CODE_TOOL = {
46465
- id: "claude-code",
46466
- displayName: "Claude Code",
46467
- type: "bunx",
46468
- command: "@anthropic-ai/claude-code",
46469
- modeArgs: {
46470
- normal: [],
46471
- continue: ["-c"],
46472
- resume: ["-r"]
46473
- },
46474
- permissionSkipArgs: Array.from(CLAUDE_PERMISSION_SKIP_ARGS),
46475
- env: {
46476
- ENABLE_LSP_TOOL: "true"
46477
- }
46478
- };
46479
- var CODEX_CLI_TOOL = {
46480
- id: "codex-cli",
46481
- displayName: "Codex",
46482
- type: "bunx",
46483
- command: "@openai/codex",
46484
- defaultArgs: Array.from(CODEX_DEFAULT_ARGS),
46485
- modeArgs: {
46486
- normal: [],
46487
- continue: ["resume", "--last"],
46488
- resume: ["resume"]
46489
- }
46490
- };
46491
- var GEMINI_CLI_TOOL = {
46492
- id: "gemini-cli",
46493
- displayName: "Gemini",
46494
- type: "bunx",
46495
- command: "@google/gemini-cli",
46496
- modeArgs: {
46497
- normal: [],
46498
- continue: ["-r", "latest"],
46499
- resume: ["-r", "latest"]
46500
- },
46501
- permissionSkipArgs: ["-y"]
46502
- };
46503
- var OPENCODE_TOOL = {
46504
- id: "opencode",
46505
- displayName: "OpenCode",
46506
- type: "bunx",
46507
- command: "opencode-ai",
46508
- modeArgs: {
46509
- normal: [],
46510
- continue: ["-c"],
46511
- resume: ["-s"]
46512
- }
46513
- };
46514
- var BUILTIN_CODING_AGENTS = [
46515
- CLAUDE_CODE_TOOL,
46516
- CODEX_CLI_TOOL,
46517
- GEMINI_CLI_TOOL,
46518
- OPENCODE_TOOL
46519
- ];
46520
-
46521
- // src/utils/command.ts
46522
- init_execa();
46523
- import { existsSync as existsSync3 } from "fs";
46524
- import { homedir } from "os";
46525
- import { join as join2 } from "path";
46526
- var KNOWN_INSTALL_PATHS = {
46527
- claude: {
46528
- unix: [
46529
- join2(homedir(), ".bun", "bin", "claude"),
46530
- join2(homedir(), ".local", "bin", "claude"),
46531
- "/usr/local/bin/claude"
46532
- ],
46533
- win32: [
46534
- join2(process.env.LOCALAPPDATA ?? "", "Programs", "claude", "claude.exe"),
46535
- join2(homedir(), ".bun", "bin", "claude.exe")
46536
- ]
46537
- },
46538
- codex: {
46539
- unix: [
46540
- join2(homedir(), ".bun", "bin", "codex"),
46541
- join2(homedir(), ".local", "bin", "codex"),
46542
- "/usr/local/bin/codex"
46543
- ],
46544
- win32: [join2(homedir(), ".bun", "bin", "codex.exe")]
46545
- },
46546
- gemini: {
46547
- unix: [
46548
- join2(homedir(), ".bun", "bin", "gemini"),
46549
- join2(homedir(), ".local", "bin", "gemini"),
46550
- "/usr/local/bin/gemini"
46551
- ],
46552
- win32: [join2(homedir(), ".bun", "bin", "gemini.exe")]
46553
- },
46554
- opencode: {
46555
- unix: [
46556
- join2(homedir(), ".bun", "bin", "opencode"),
46557
- join2(homedir(), ".local", "bin", "opencode"),
46558
- "/usr/local/bin/opencode"
46559
- ],
46560
- win32: [join2(homedir(), ".bun", "bin", "opencode.exe")]
46561
- }
46562
- };
46563
- var commandLookupCache = new Map;
46564
- async function getCommandVersion(commandPath) {
46565
- try {
46566
- const result = await execa(commandPath, ["--version"], {
46567
- timeout: 3000,
46568
- stdin: "ignore",
46569
- stdout: "pipe",
46570
- stderr: "ignore"
46571
- });
46572
- const match = result.stdout.match(/(\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?)/);
46573
- return match ? `v${match[1]}` : null;
46574
- } catch {
46575
- return null;
46576
- }
46577
- }
46578
- async function findCommand(commandName) {
46579
- const cached = commandLookupCache.get(commandName);
46580
- if (cached) {
46581
- return cached;
46582
- }
46583
- let lookupResult = null;
46584
- try {
46585
- const lookupCommand = process.platform === "win32" ? "where" : "which";
46586
- const execResult = await execa(lookupCommand, [commandName], {
46587
- shell: true,
46588
- stdin: "ignore",
46589
- stdout: "pipe",
46590
- stderr: "ignore"
46591
- });
46592
- const foundPath = execResult.stdout.trim().split(`
46593
- `)[0];
46594
- if (foundPath) {
46595
- lookupResult = { available: true, path: foundPath, source: "installed" };
46596
- }
46597
- } catch {}
46598
- if (!lookupResult) {
46599
- const knownPaths = KNOWN_INSTALL_PATHS[commandName];
46600
- if (knownPaths) {
46601
- const pathsToCheck = process.platform === "win32" ? knownPaths.win32 : knownPaths.unix;
46602
- for (const p of pathsToCheck) {
46603
- if (p && existsSync3(p)) {
46604
- lookupResult = { available: true, path: p, source: "installed" };
46605
- break;
46606
- }
46607
- }
46608
- }
46609
- }
46610
- if (!lookupResult) {
46611
- lookupResult = { available: true, path: null, source: "bunx" };
46612
- }
46613
- if (lookupResult.source === "installed" && lookupResult.path) {
46614
- lookupResult.version = await getCommandVersion(lookupResult.path);
46615
- } else {
46616
- lookupResult.version = null;
46939
+ async function prefetchAgentVersions(agentIds, fetchFn) {
46940
+ const fetcher = fetchFn ?? (async (agentId) => {
46941
+ const { fetchVersionOptionsForAgent: fetchVersionOptionsForAgent2 } = await Promise.resolve().then(() => (init_versionFetcher(), exports_versionFetcher));
46942
+ return fetchVersionOptionsForAgent2(agentId);
46943
+ });
46944
+ const results = await Promise.allSettled(agentIds.map(async (agentId) => {
46945
+ try {
46946
+ const versions = await fetcher(agentId);
46947
+ versionCache.set(agentId, versions);
46948
+ } catch {}
46949
+ }));
46950
+ for (let i2 = 0;i2 < results.length; i2++) {
46951
+ const result = results[i2];
46952
+ if (result && result.status === "rejected") {}
46617
46953
  }
46618
- commandLookupCache.set(commandName, lookupResult);
46619
- return lookupResult;
46620
46954
  }
46621
46955
 
46622
46956
  // src/cli/ui/components/solid/WizardSteps.tsx
46957
+ init_versionFetcher();
46623
46958
  var useEnsureSelectionVisible = (options) => {
46624
46959
  const scroll = useWizardScroll();
46625
46960
  const ensureIndexVisible = (index) => {
@@ -46929,9 +47264,16 @@ function AgentSelectStep(props) {
46929
47264
  return isSelected() ? (() => {
46930
47265
  var _el$39 = createElement("text"), _el$40 = createTextNode(`> `);
46931
47266
  insertNode(_el$39, _el$40);
46932
- setProp(_el$39, "bg", "cyan");
46933
- setProp(_el$39, "fg", "black");
46934
47267
  insert(_el$39, () => agent.label, null);
47268
+ effect((_p$) => {
47269
+ var _v$9 = selectionStyle.bg, _v$0 = selectionStyle.fg;
47270
+ _v$9 !== _p$.e && (_p$.e = setProp(_el$39, "bg", _v$9, _p$.e));
47271
+ _v$0 !== _p$.t && (_p$.t = setProp(_el$39, "fg", _v$0, _p$.t));
47272
+ return _p$;
47273
+ }, {
47274
+ e: undefined,
47275
+ t: undefined
47276
+ });
46935
47277
  return _el$39;
46936
47278
  })() : (() => {
46937
47279
  var _el$41 = createElement("text"), _el$42 = createTextNode(` `);
@@ -47023,9 +47365,9 @@ function ModelSelectStep(props) {
47023
47365
  insertNode(_el$48, createTextNode(` `));
47024
47366
  insertNode(_el$50, createTextNode(`[Enter] Select [Esc] Back`));
47025
47367
  effect((_p$) => {
47026
- var _v$9 = TextAttributes.BOLD, _v$0 = TextAttributes.DIM;
47027
- _v$9 !== _p$.e && (_p$.e = setProp(_el$44, "attributes", _v$9, _p$.e));
47028
- _v$0 !== _p$.t && (_p$.t = setProp(_el$50, "attributes", _v$0, _p$.t));
47368
+ var _v$1 = TextAttributes.BOLD, _v$10 = TextAttributes.DIM;
47369
+ _v$1 !== _p$.e && (_p$.e = setProp(_el$44, "attributes", _v$1, _p$.e));
47370
+ _v$10 !== _p$.t && (_p$.t = setProp(_el$50, "attributes", _v$10, _p$.t));
47029
47371
  return _p$;
47030
47372
  }, {
47031
47373
  e: undefined,
@@ -47092,9 +47434,9 @@ function ReasoningLevelStep(props) {
47092
47434
  insertNode(_el$57, createTextNode(` `));
47093
47435
  insertNode(_el$59, createTextNode(`[Enter] Select [Esc] Back`));
47094
47436
  effect((_p$) => {
47095
- var _v$1 = TextAttributes.BOLD, _v$10 = TextAttributes.DIM;
47096
- _v$1 !== _p$.e && (_p$.e = setProp(_el$53, "attributes", _v$1, _p$.e));
47097
- _v$10 !== _p$.t && (_p$.t = setProp(_el$59, "attributes", _v$10, _p$.t));
47437
+ var _v$11 = TextAttributes.BOLD, _v$12 = TextAttributes.DIM;
47438
+ _v$11 !== _p$.e && (_p$.e = setProp(_el$53, "attributes", _v$11, _p$.e));
47439
+ _v$12 !== _p$.t && (_p$.t = setProp(_el$59, "attributes", _v$12, _p$.t));
47098
47440
  return _p$;
47099
47441
  }, {
47100
47442
  e: undefined,
@@ -47157,9 +47499,9 @@ function ExecutionModeStep(props) {
47157
47499
  insertNode(_el$66, createTextNode(` `));
47158
47500
  insertNode(_el$68, createTextNode(`[Enter] Select [Esc] Back`));
47159
47501
  effect((_p$) => {
47160
- var _v$11 = TextAttributes.BOLD, _v$12 = TextAttributes.DIM;
47161
- _v$11 !== _p$.e && (_p$.e = setProp(_el$62, "attributes", _v$11, _p$.e));
47162
- _v$12 !== _p$.t && (_p$.t = setProp(_el$68, "attributes", _v$12, _p$.t));
47502
+ var _v$13 = TextAttributes.BOLD, _v$14 = TextAttributes.DIM;
47503
+ _v$13 !== _p$.e && (_p$.e = setProp(_el$62, "attributes", _v$13, _p$.e));
47504
+ _v$14 !== _p$.t && (_p$.t = setProp(_el$68, "attributes", _v$14, _p$.t));
47163
47505
  return _p$;
47164
47506
  }, {
47165
47507
  e: undefined,
@@ -47218,9 +47560,9 @@ function SkipPermissionsStep(props) {
47218
47560
  insertNode(_el$75, createTextNode(` `));
47219
47561
  insertNode(_el$77, createTextNode(`[Enter] Select [Esc] Back`));
47220
47562
  effect((_p$) => {
47221
- var _v$13 = TextAttributes.BOLD, _v$14 = TextAttributes.DIM;
47222
- _v$13 !== _p$.e && (_p$.e = setProp(_el$71, "attributes", _v$13, _p$.e));
47223
- _v$14 !== _p$.t && (_p$.t = setProp(_el$77, "attributes", _v$14, _p$.t));
47563
+ var _v$15 = TextAttributes.BOLD, _v$16 = TextAttributes.DIM;
47564
+ _v$15 !== _p$.e && (_p$.e = setProp(_el$71, "attributes", _v$15, _p$.e));
47565
+ _v$16 !== _p$.t && (_p$.t = setProp(_el$77, "attributes", _v$16, _p$.t));
47224
47566
  return _p$;
47225
47567
  }, {
47226
47568
  e: undefined,
@@ -47234,60 +47576,20 @@ var LATEST_OPTION = {
47234
47576
  value: "latest",
47235
47577
  description: "Always fetch latest version"
47236
47578
  };
47237
- function getPackageNameForAgent(agentId) {
47238
- const agent = BUILTIN_CODING_AGENTS.find((a2) => a2.id === agentId);
47239
- if (!agent || agent.type !== "bunx") {
47240
- return null;
47241
- }
47242
- const {
47243
- packageName
47244
- } = parsePackageCommand(agent.command);
47245
- return packageName;
47246
- }
47247
- async function fetchVersionOptions(agentId) {
47248
- const packageName = getPackageNameForAgent(agentId);
47249
- if (!packageName) {
47250
- return [];
47251
- }
47252
- const versions = await fetchPackageVersions(packageName);
47253
- return versions.map((v) => {
47254
- const item = {
47255
- label: v.isPrerelease ? `${v.version} (pre)` : v.version,
47256
- value: v.version
47257
- };
47258
- if (v.publishedAt) {
47259
- item.description = new Date(v.publishedAt).toLocaleDateString();
47260
- }
47261
- return item;
47262
- });
47263
- }
47264
- var AGENT_COMMAND_MAP = {
47265
- "claude-code": "claude",
47266
- "codex-cli": "codex",
47267
- "gemini-cli": "gemini",
47268
- opencode: "opencode"
47269
- };
47270
- async function fetchInstalledOption(agentId) {
47271
- const commandName = AGENT_COMMAND_MAP[agentId];
47272
- if (!commandName) {
47273
- return null;
47274
- }
47275
- const result = await findCommand(commandName);
47276
- if (result.source !== "installed" || !result.path) {
47277
- return null;
47278
- }
47279
- const version = result.version?.replace(/^v/, "") ?? "unknown";
47280
- return {
47281
- label: `installed@${version}`,
47282
- value: "installed",
47283
- description: result.path
47284
- };
47285
- }
47286
47579
  function VersionSelectStep(props) {
47287
47580
  const [selectedIndex, setSelectedIndex] = createSignal(0);
47288
47581
  const [selectRef, setSelectRef] = createSignal(undefined);
47289
- const [versionOptions] = createResource(() => props.agentId, fetchVersionOptions);
47290
- const [installedOption] = createResource(() => props.agentId, fetchInstalledOption);
47582
+ const cachedVersions = () => {
47583
+ const cached = getVersionCache(props.agentId);
47584
+ if (!cached) {
47585
+ return [];
47586
+ }
47587
+ return cached.map(versionInfoToSelectItem);
47588
+ };
47589
+ const [installedOption] = createResource(() => props.agentId, async (agentId) => {
47590
+ const installed = await fetchInstalledVersionForAgent(agentId);
47591
+ return installed ? createInstalledOption(installed) : null;
47592
+ });
47291
47593
  const allOptions = () => {
47292
47594
  const options = [];
47293
47595
  const installed = installedOption();
@@ -47295,8 +47597,8 @@ function VersionSelectStep(props) {
47295
47597
  options.push(installed);
47296
47598
  }
47297
47599
  options.push(LATEST_OPTION);
47298
- const dynamic = versionOptions() ?? [];
47299
- options.push(...dynamic);
47600
+ const cached = cachedVersions();
47601
+ options.push(...cached);
47300
47602
  return options;
47301
47603
  };
47302
47604
  useEnsureSelectionVisible({
@@ -47327,11 +47629,9 @@ function VersionSelectStep(props) {
47327
47629
  }
47328
47630
  };
47329
47631
  const statusText = () => {
47330
- if (versionOptions.loading) {
47331
- return "Fetching versions...";
47332
- }
47333
- if (versionOptions.error) {
47334
- return "Could not fetch versions";
47632
+ const cached = cachedVersions();
47633
+ if (cached.length === 0) {
47634
+ return "Version list unavailable (using latest)";
47335
47635
  }
47336
47636
  return null;
47337
47637
  };
@@ -47368,9 +47668,9 @@ function VersionSelectStep(props) {
47368
47668
  insertNode(_el$84, createTextNode(` `));
47369
47669
  insertNode(_el$86, createTextNode(`[Enter] Select [Esc] Back`));
47370
47670
  effect((_p$) => {
47371
- var _v$15 = TextAttributes.BOLD, _v$16 = TextAttributes.DIM;
47372
- _v$15 !== _p$.e && (_p$.e = setProp(_el$80, "attributes", _v$15, _p$.e));
47373
- _v$16 !== _p$.t && (_p$.t = setProp(_el$86, "attributes", _v$16, _p$.t));
47671
+ var _v$17 = TextAttributes.BOLD, _v$18 = TextAttributes.DIM;
47672
+ _v$17 !== _p$.e && (_p$.e = setProp(_el$80, "attributes", _v$17, _p$.e));
47673
+ _v$18 !== _p$.t && (_p$.t = setProp(_el$86, "attributes", _v$18, _p$.t));
47374
47674
  return _p$;
47375
47675
  }, {
47376
47676
  e: undefined,
@@ -47392,7 +47692,7 @@ function WizardController(props) {
47392
47692
  const [selectedModel, setSelectedModel] = createSignal("");
47393
47693
  const [reasoningLevel, setReasoningLevel] = createSignal(undefined);
47394
47694
  const [executionMode, setExecutionMode] = createSignal("normal");
47395
- const [isInitialFrame, setIsInitialFrame] = createSignal(true);
47695
+ const [isTransitioning, setIsTransitioning] = createSignal(true);
47396
47696
  function getInitialStep() {
47397
47697
  if (props.history.length > 0) {
47398
47698
  return "quick-start";
@@ -47411,20 +47711,23 @@ function WizardController(props) {
47411
47711
  setReasoningLevel(undefined);
47412
47712
  setExecutionMode("normal");
47413
47713
  };
47714
+ const startTransition2 = () => {
47715
+ setIsTransitioning(true);
47716
+ setTimeout(() => setIsTransitioning(false), 50);
47717
+ };
47414
47718
  let prevVisible = false;
47415
47719
  createEffect(() => {
47416
47720
  const visible = props.visible;
47417
47721
  if (visible && !prevVisible) {
47418
47722
  resetWizard();
47419
- setIsInitialFrame(true);
47420
- setTimeout(() => setIsInitialFrame(false), 150);
47723
+ startTransition2();
47421
47724
  }
47422
47725
  prevVisible = visible;
47423
47726
  });
47424
47727
  useKeyboard((key) => {
47425
47728
  if (!props.visible)
47426
47729
  return;
47427
- if (isInitialFrame() && key.name === "return") {
47730
+ if (isTransitioning() && key.name === "return") {
47428
47731
  return;
47429
47732
  }
47430
47733
  if (key.name === "escape") {
@@ -47434,6 +47737,7 @@ function WizardController(props) {
47434
47737
  const goToStep = (nextStep) => {
47435
47738
  setStepHistory((prev) => [...prev, step()]);
47436
47739
  setStep(nextStep);
47740
+ startTransition2();
47437
47741
  };
47438
47742
  const goBack = () => {
47439
47743
  const history = stepHistory();
@@ -47444,6 +47748,7 @@ function WizardController(props) {
47444
47748
  const previousStep = history[history.length - 1] ?? "agent-select";
47445
47749
  setStepHistory(history.slice(0, -1));
47446
47750
  setStep(previousStep);
47751
+ startTransition2();
47447
47752
  };
47448
47753
  const needsReasoningLevel = createMemo(() => {
47449
47754
  return selectedAgent() === "codex-cli";
@@ -47479,7 +47784,7 @@ function WizardController(props) {
47479
47784
  goToStep("version-select");
47480
47785
  };
47481
47786
  const handleVersionSelect = (version) => {
47482
- setSelectedVersion(version === "installed" ? null : version);
47787
+ setSelectedVersion(version);
47483
47788
  goToStep("model-select");
47484
47789
  };
47485
47790
  const handleModelSelect = (model) => {
@@ -47526,7 +47831,7 @@ function WizardController(props) {
47526
47831
  };
47527
47832
  const renderStep = () => {
47528
47833
  const currentStep = step();
47529
- const focused = !isInitialFrame();
47834
+ const focused = !isTransitioning();
47530
47835
  if (currentStep === "action-select") {
47531
47836
  return createComponent2(ActionSelectStep, {
47532
47837
  get branchName() {
@@ -47780,6 +48085,10 @@ function useScrollableList(options) {
47780
48085
 
47781
48086
  // src/cli/ui/screens/solid/SelectorScreen.tsx
47782
48087
  var clamp2 = (value, min, max) => Math.min(Math.max(value, min), max);
48088
+ var padLine2 = (value, width) => {
48089
+ const padding = Math.max(0, width - stringWidth(value));
48090
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
48091
+ };
47783
48092
  function SelectorScreen({
47784
48093
  title,
47785
48094
  description,
@@ -47898,30 +48207,34 @@ function SelectorScreen({
47898
48207
  insert(_el$5, () => list.visibleItems().map((item, index) => {
47899
48208
  const absoluteIndex = list.scrollOffset() + index;
47900
48209
  const isSelected = absoluteIndex === selectedIndex();
48210
+ const descriptionText = showDescription && item.description ? ` - ${item.description}` : "";
48211
+ const fullLine = `${item.label}${descriptionText}`;
47901
48212
  return (() => {
47902
- var _el$6 = createElement("box"), _el$7 = createElement("text");
47903
- insertNode(_el$6, _el$7);
48213
+ var _el$6 = createElement("box");
47904
48214
  setProp(_el$6, "flexDirection", "row");
47905
- spread(_el$7, mergeProps3(() => isSelected ? {
47906
- fg: "cyan",
47907
- attributes: TextAttributes.BOLD
47908
- } : {}), true);
47909
- insert(_el$7, () => item.label);
47910
- insert(_el$6, (() => {
47911
- var _c$2 = memo2(() => !!(showDescription && item.description));
47912
- return () => _c$2() ? (() => {
47913
- var _el$8 = createElement("text");
47914
- spread(_el$8, mergeProps3({
47915
- get attributes() {
47916
- return TextAttributes.DIM;
47917
- }
47918
- }, isSelected ? {
47919
- fg: "cyan"
47920
- } : {}), true);
47921
- insert(_el$8, () => ` - ${item.description}`);
47922
- return _el$8;
47923
- })() : null;
47924
- })(), null);
48215
+ insert(_el$6, isSelected ? (() => {
48216
+ var _el$7 = createElement("text");
48217
+ insert(_el$7, () => padLine2(fullLine, terminal().columns));
48218
+ effect((_p$) => {
48219
+ var _v$ = selectionStyle.fg, _v$2 = selectionStyle.bg;
48220
+ _v$ !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$, _p$.e));
48221
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "bg", _v$2, _p$.t));
48222
+ return _p$;
48223
+ }, {
48224
+ e: undefined,
48225
+ t: undefined
48226
+ });
48227
+ return _el$7;
48228
+ })() : [(() => {
48229
+ var _el$8 = createElement("text");
48230
+ insert(_el$8, () => item.label);
48231
+ return _el$8;
48232
+ })(), memo2(() => memo2(() => !!(showDescription && item.description))() ? (() => {
48233
+ var _el$9 = createElement("text");
48234
+ insert(_el$9, () => ` - ${item.description}`);
48235
+ effect((_$p) => setProp(_el$9, "attributes", TextAttributes.DIM, _$p));
48236
+ return _el$9;
48237
+ })() : null)]);
47925
48238
  return _el$6;
47926
48239
  })();
47927
48240
  }));
@@ -47939,53 +48252,237 @@ function SelectorScreen({
47939
48252
  }
47940
48253
 
47941
48254
  // src/cli/ui/screens/solid/LogScreen.tsx
47942
- function LogScreen({
47943
- entries,
47944
- loading = false,
47945
- error = null,
47946
- onBack,
47947
- onSelect,
47948
- onCopy,
47949
- onPickDate,
47950
- notification,
47951
- version,
47952
- selectedDate,
47953
- helpVisible = false
47954
- }) {
48255
+ var LEVEL_ORDER = ["ALL", "INFO+", "WARN+", "ERROR+"];
48256
+ var LEVEL_THRESHOLDS = {
48257
+ ALL: 0,
48258
+ "INFO+": 30,
48259
+ "WARN+": 40,
48260
+ "ERROR+": 50
48261
+ };
48262
+ var LEVEL_LABEL_VALUES = {
48263
+ TRACE: 10,
48264
+ DEBUG: 20,
48265
+ INFO: 30,
48266
+ WARN: 40,
48267
+ ERROR: 50,
48268
+ FATAL: 60
48269
+ };
48270
+ var measureWidth2 = (value) => stringWidth(value);
48271
+ var truncateToWidth3 = (value, maxWidth) => {
48272
+ if (maxWidth <= 0) {
48273
+ return "";
48274
+ }
48275
+ if (measureWidth2(value) <= maxWidth) {
48276
+ return value;
48277
+ }
48278
+ const ellipsis = "...";
48279
+ const ellipsisWidth = measureWidth2(ellipsis);
48280
+ if (ellipsisWidth >= maxWidth) {
48281
+ return ellipsis.slice(0, maxWidth);
48282
+ }
48283
+ let currentWidth = 0;
48284
+ let result = "";
48285
+ for (const char of Array.from(value)) {
48286
+ const charWidth = measureWidth2(char);
48287
+ if (currentWidth + charWidth + ellipsisWidth > maxWidth) {
48288
+ break;
48289
+ }
48290
+ result += char;
48291
+ currentWidth += charWidth;
48292
+ }
48293
+ return `${result}${ellipsis}`;
48294
+ };
48295
+ var padToWidth = (value, width) => {
48296
+ if (width <= 0) {
48297
+ return "";
48298
+ }
48299
+ const truncated = truncateToWidth3(value, width);
48300
+ const padding = Math.max(0, width - measureWidth2(truncated));
48301
+ return `${truncated}${" ".repeat(padding)}`;
48302
+ };
48303
+ var appendSegment2 = (segments, segment) => {
48304
+ if (!segment.text) {
48305
+ return;
48306
+ }
48307
+ segments.push(segment);
48308
+ };
48309
+ var measureSegmentsWidth2 = (segments) => segments.reduce((total, segment) => total + measureWidth2(segment.text), 0);
48310
+ var truncateSegmentsToWidth2 = (segments, maxWidth) => {
48311
+ if (maxWidth <= 0) {
48312
+ return [];
48313
+ }
48314
+ const result = [];
48315
+ let currentWidth = 0;
48316
+ for (const segment of segments) {
48317
+ if (currentWidth >= maxWidth) {
48318
+ break;
48319
+ }
48320
+ const segmentWidth = measureWidth2(segment.text);
48321
+ if (currentWidth + segmentWidth <= maxWidth) {
48322
+ result.push(segment);
48323
+ currentWidth += segmentWidth;
48324
+ continue;
48325
+ }
48326
+ const remaining = maxWidth - currentWidth;
48327
+ const truncated = truncateToWidth3(segment.text, remaining);
48328
+ if (truncated) {
48329
+ result.push({
48330
+ ...segment,
48331
+ text: truncated
48332
+ });
48333
+ currentWidth += measureWidth2(truncated);
48334
+ }
48335
+ break;
48336
+ }
48337
+ return result;
48338
+ };
48339
+ var segmentsToText = (segments) => segments.map((segment) => segment.text).join("");
48340
+ var padLine3 = (value, width) => {
48341
+ const padding = Math.max(0, width - measureWidth2(value));
48342
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
48343
+ };
48344
+ var resolveEntryLevel = (entry) => {
48345
+ const rawLevel = entry.raw?.level;
48346
+ if (typeof rawLevel === "number") {
48347
+ return rawLevel;
48348
+ }
48349
+ const label = entry.levelLabel?.toUpperCase() ?? "";
48350
+ return LEVEL_LABEL_VALUES[label] ?? 0;
48351
+ };
48352
+ function LogScreen(props) {
48353
+ const merged = mergeProps({
48354
+ loading: false,
48355
+ error: null,
48356
+ branchLabel: null,
48357
+ sourceLabel: null,
48358
+ tailing: false,
48359
+ helpVisible: false
48360
+ }, props);
47955
48361
  const terminal = useTerminalSize();
48362
+ const [filterQuery, setFilterQuery] = createSignal("");
48363
+ const [filterMode, setFilterMode] = createSignal(false);
48364
+ const [levelMode, setLevelMode] = createSignal("ALL");
48365
+ const filteredEntries = createMemo(() => {
48366
+ let result = merged.entries;
48367
+ const threshold = LEVEL_THRESHOLDS[levelMode()];
48368
+ if (threshold > 0) {
48369
+ result = result.filter((entry) => resolveEntryLevel(entry) >= threshold);
48370
+ }
48371
+ const query = filterQuery().trim().toLowerCase();
48372
+ if (!query) {
48373
+ return result;
48374
+ }
48375
+ return result.filter((entry) => {
48376
+ const target = `${entry.category} ${entry.levelLabel} ${entry.message}`.toLowerCase();
48377
+ return target.includes(query);
48378
+ });
48379
+ });
48380
+ const totalCount = createMemo(() => merged.entries.length);
48381
+ const filteredCount = createMemo(() => filteredEntries().length);
48382
+ const showFilterCount = createMemo(() => filterMode() || filterQuery().trim().length > 0);
48383
+ const levelWidth = createMemo(() => {
48384
+ const MIN = 5;
48385
+ return Math.max(MIN, ...filteredEntries().map((entry) => measureWidth2(entry.levelLabel)));
48386
+ });
48387
+ const categoryWidth = createMemo(() => {
48388
+ const MIN = 4;
48389
+ const MAX = 20;
48390
+ const maxWidth = Math.max(MIN, ...filteredEntries().map((entry) => measureWidth2(entry.category)));
48391
+ return Math.min(MAX, maxWidth);
48392
+ });
47956
48393
  const listHeight = createMemo(() => {
47957
48394
  const headerRows = 2;
47958
- const infoRows = 1;
48395
+ const infoRows = 3;
47959
48396
  const footerRows = 1;
47960
- const notificationRows = notification ? 1 : 0;
48397
+ const notificationRows = merged.notification ? 1 : 0;
47961
48398
  const reserved = headerRows + infoRows + footerRows + notificationRows;
47962
48399
  return Math.max(1, terminal().rows - reserved);
47963
48400
  });
47964
48401
  const list = useScrollableList({
47965
- items: () => entries,
48402
+ items: filteredEntries,
47966
48403
  visibleCount: listHeight
47967
48404
  });
47968
- const currentEntry = createMemo(() => entries[list.selectedIndex()]);
48405
+ const currentEntry = createMemo(() => filteredEntries()[list.selectedIndex()]);
48406
+ createEffect(() => {
48407
+ filterQuery();
48408
+ levelMode();
48409
+ list.setSelectedIndex(0);
48410
+ list.setScrollOffset(0);
48411
+ });
47969
48412
  const updateSelectedIndex = (value) => {
47970
48413
  list.setSelectedIndex(value);
47971
48414
  };
47972
48415
  useKeyboard((key) => {
47973
- if (helpVisible) {
48416
+ if (merged.helpVisible) {
48417
+ return;
48418
+ }
48419
+ if (filterMode()) {
48420
+ if (key.name === "down") {
48421
+ updateSelectedIndex((prev) => prev + 1);
48422
+ return;
48423
+ }
48424
+ if (key.name === "up") {
48425
+ updateSelectedIndex((prev) => prev - 1);
48426
+ return;
48427
+ }
48428
+ if (key.name === "escape") {
48429
+ if (filterQuery()) {
48430
+ setFilterQuery("");
48431
+ } else {
48432
+ setFilterMode(false);
48433
+ }
48434
+ return;
48435
+ }
48436
+ if (key.name === "backspace") {
48437
+ setFilterQuery((prev) => prev.slice(0, -1));
48438
+ return;
48439
+ }
48440
+ if (key.name === "return" || key.name === "linefeed") {
48441
+ setFilterMode(false);
48442
+ return;
48443
+ }
48444
+ if (key.sequence && key.sequence.length === 1 && !key.ctrl && !key.meta && !key.super && !key.hyper) {
48445
+ setFilterQuery((prev) => prev + key.sequence);
48446
+ }
47974
48447
  return;
47975
48448
  }
47976
48449
  if (key.name === "escape" || key.name === "q") {
47977
- onBack();
48450
+ merged.onBack();
47978
48451
  return;
47979
48452
  }
47980
48453
  if (key.name === "c") {
47981
48454
  const entry = currentEntry();
47982
48455
  if (entry) {
47983
- onCopy(entry);
48456
+ merged.onCopy(entry);
47984
48457
  }
47985
48458
  return;
47986
48459
  }
47987
48460
  if (key.name === "d") {
47988
- onPickDate?.();
48461
+ merged.onPickDate?.();
48462
+ return;
48463
+ }
48464
+ if (key.name === "f") {
48465
+ setFilterMode(true);
48466
+ return;
48467
+ }
48468
+ if (key.name === "v") {
48469
+ setLevelMode((prev) => {
48470
+ const index = LEVEL_ORDER.indexOf(prev);
48471
+ const nextIndex = (index + 1) % LEVEL_ORDER.length;
48472
+ return LEVEL_ORDER[nextIndex] ?? "ALL";
48473
+ });
48474
+ return;
48475
+ }
48476
+ if (key.name === "r") {
48477
+ merged.onReload?.();
48478
+ return;
48479
+ }
48480
+ if (key.name === "t") {
48481
+ merged.onToggleTail?.();
48482
+ return;
48483
+ }
48484
+ if (key.name === "x") {
48485
+ merged.onReset?.();
47989
48486
  return;
47990
48487
  }
47991
48488
  if (key.name === "down") {
@@ -48009,13 +48506,13 @@ function LogScreen({
48009
48506
  return;
48010
48507
  }
48011
48508
  if (key.name === "end") {
48012
- updateSelectedIndex(entries.length - 1);
48509
+ updateSelectedIndex(filteredEntries().length - 1);
48013
48510
  return;
48014
48511
  }
48015
48512
  if (key.name === "return" || key.name === "linefeed") {
48016
48513
  const entry = currentEntry();
48017
48514
  if (entry) {
48018
- onSelect(entry);
48515
+ merged.onSelect(entry);
48019
48516
  }
48020
48517
  }
48021
48518
  });
@@ -48029,92 +48526,227 @@ function LogScreen({
48029
48526
  }, {
48030
48527
  key: "d",
48031
48528
  description: "Date"
48529
+ }, {
48530
+ key: "f",
48531
+ description: "Filter"
48532
+ }, {
48533
+ key: "v",
48534
+ description: "Level"
48535
+ }, {
48536
+ key: "r",
48537
+ description: "Reload"
48538
+ }, {
48539
+ key: "t",
48540
+ description: "Tail"
48541
+ }, {
48542
+ key: "x",
48543
+ description: "Reset"
48032
48544
  }, {
48033
48545
  key: "esc",
48034
48546
  description: "Back"
48035
48547
  }];
48036
48548
  return actions;
48037
48549
  });
48550
+ const formatEntrySegments = (entry) => {
48551
+ const levelText = padToWidth(entry.levelLabel, levelWidth());
48552
+ const categoryText = padToWidth(entry.category, categoryWidth());
48553
+ const segments = [];
48554
+ appendSegment2(segments, {
48555
+ text: `[${entry.timeLabel}] `
48556
+ });
48557
+ appendSegment2(segments, {
48558
+ text: "["
48559
+ });
48560
+ appendSegment2(segments, {
48561
+ text: levelText,
48562
+ fg: getLogLevelColor(entry.levelLabel)
48563
+ });
48564
+ appendSegment2(segments, {
48565
+ text: "] "
48566
+ });
48567
+ appendSegment2(segments, {
48568
+ text: "["
48569
+ });
48570
+ appendSegment2(segments, {
48571
+ text: categoryText
48572
+ });
48573
+ appendSegment2(segments, {
48574
+ text: "] "
48575
+ });
48576
+ appendSegment2(segments, {
48577
+ text: entry.message
48578
+ });
48579
+ const maxWidth = terminal().columns;
48580
+ if (measureSegmentsWidth2(segments) <= maxWidth) {
48581
+ return segments;
48582
+ }
48583
+ return truncateSegmentsToWidth2(segments, maxWidth);
48584
+ };
48585
+ const filterLabel = createMemo(() => {
48586
+ if (filterQuery()) {
48587
+ return filterQuery();
48588
+ }
48589
+ return filterMode() ? "(type to filter)" : "(press f to filter)";
48590
+ });
48038
48591
  return (() => {
48039
- var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$6 = createElement("text"), _el$8 = createElement("text"), _el$9 = createElement("box");
48592
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$6 = createElement("text"), _el$8 = createElement("text"), _el$9 = createElement("box"), _el$0 = createElement("text"), _el$10 = createElement("text"), _el$11 = createElement("text"), _el$13 = createElement("text"), _el$14 = createElement("text"), _el$16 = createElement("text"), _el$17 = createElement("text"), _el$19 = createElement("text"), _el$20 = createElement("box"), _el$21 = createElement("text"), _el$23 = createElement("text"), _el$24 = createElement("box");
48040
48593
  insertNode(_el$, _el$2);
48041
48594
  insertNode(_el$, _el$9);
48595
+ insertNode(_el$, _el$20);
48596
+ insertNode(_el$, _el$24);
48042
48597
  setProp(_el$, "flexDirection", "column");
48043
48598
  insert(_el$, createComponent2(Header, {
48044
48599
  title: "gwt - Log Viewer",
48045
48600
  titleColor: "cyan",
48046
- version
48601
+ get version() {
48602
+ return merged.version;
48603
+ }
48047
48604
  }), _el$2);
48048
- insert(_el$, notification ? (() => {
48049
- var _el$0 = createElement("text");
48050
- insert(_el$0, () => notification.message);
48051
- effect((_$p) => setProp(_el$0, "fg", notification.tone === "error" ? "red" : "green", _$p));
48052
- return _el$0;
48053
- })() : null, _el$2);
48605
+ insert(_el$, (() => {
48606
+ var _c$ = memo2(() => !!merged.notification);
48607
+ return () => _c$() ? (() => {
48608
+ var _el$25 = createElement("text");
48609
+ insert(_el$25, () => merged.notification.message);
48610
+ effect((_$p) => setProp(_el$25, "fg", merged.notification.tone === "error" ? "red" : "green", _$p));
48611
+ return _el$25;
48612
+ })() : null;
48613
+ })(), _el$2);
48054
48614
  insertNode(_el$2, _el$3);
48055
48615
  insertNode(_el$2, _el$5);
48056
48616
  insertNode(_el$2, _el$6);
48057
48617
  insertNode(_el$2, _el$8);
48058
48618
  setProp(_el$2, "flexDirection", "row");
48059
- insertNode(_el$3, createTextNode(`Date: `));
48060
- insert(_el$5, selectedDate ?? "---");
48061
- insertNode(_el$6, createTextNode(` Total: `));
48062
- insert(_el$8, () => entries.length);
48063
- setProp(_el$9, "flexDirection", "column");
48064
- setProp(_el$9, "flexGrow", 1);
48065
- insert(_el$9, () => loading ? (() => {
48066
- var _el$1 = createElement("text");
48067
- insertNode(_el$1, createTextNode(`Loading logs...`));
48068
- setProp(_el$1, "fg", "gray");
48069
- return _el$1;
48070
- })() : entries.length === 0 ? (() => {
48071
- var _el$11 = createElement("text");
48072
- insertNode(_el$11, createTextNode(`No logs available.`));
48073
- setProp(_el$11, "fg", "gray");
48074
- return _el$11;
48075
- })() : (() => {
48076
- var _el$13 = createElement("box");
48077
- setProp(_el$13, "flexDirection", "column");
48078
- insert(_el$13, () => list.visibleItems().map((entry, index) => {
48079
- const absoluteIndex = list.scrollOffset() + index;
48080
- const isSelected = absoluteIndex === list.selectedIndex();
48081
- return (() => {
48082
- var _el$14 = createElement("text");
48083
- spread(_el$14, mergeProps3(() => isSelected ? {
48084
- fg: "cyan",
48085
- attributes: TextAttributes.BOLD
48086
- } : {}), true);
48087
- insert(_el$14, () => entry.summary);
48088
- return _el$14;
48089
- })();
48090
- }));
48091
- return _el$13;
48619
+ insertNode(_el$3, createTextNode(`Branch: `));
48620
+ insert(_el$5, () => merged.branchLabel ?? "(none)");
48621
+ insertNode(_el$6, createTextNode(` Source: `));
48622
+ insert(_el$8, () => merged.sourceLabel ?? "(none)");
48623
+ insertNode(_el$9, _el$0);
48624
+ insertNode(_el$9, _el$10);
48625
+ insertNode(_el$9, _el$11);
48626
+ insertNode(_el$9, _el$13);
48627
+ insertNode(_el$9, _el$14);
48628
+ insertNode(_el$9, _el$16);
48629
+ insertNode(_el$9, _el$17);
48630
+ insertNode(_el$9, _el$19);
48631
+ setProp(_el$9, "flexDirection", "row");
48632
+ insertNode(_el$0, createTextNode(`Date: `));
48633
+ insert(_el$10, () => merged.selectedDate ?? "---");
48634
+ insertNode(_el$11, createTextNode(` Total: `));
48635
+ insert(_el$13, totalCount);
48636
+ insertNode(_el$14, createTextNode(` Level: `));
48637
+ insert(_el$16, levelMode);
48638
+ insertNode(_el$17, createTextNode(` Tail: `));
48639
+ insert(_el$19, () => merged.tailing ? "ON" : "OFF");
48640
+ insertNode(_el$20, _el$21);
48641
+ insertNode(_el$20, _el$23);
48642
+ setProp(_el$20, "flexDirection", "row");
48643
+ insertNode(_el$21, createTextNode(`Filter: `));
48644
+ insert(_el$23, filterLabel);
48645
+ insert(_el$20, (() => {
48646
+ var _c$2 = memo2(() => !!showFilterCount());
48647
+ return () => _c$2() ? (() => {
48648
+ var _el$26 = createElement("text");
48649
+ insert(_el$26, () => ` (Showing ${filteredCount()} of ${totalCount()})`);
48650
+ effect((_$p) => setProp(_el$26, "attributes", TextAttributes.DIM, _$p));
48651
+ return _el$26;
48652
+ })() : null;
48653
+ })(), null);
48654
+ setProp(_el$24, "flexDirection", "column");
48655
+ setProp(_el$24, "flexGrow", 1);
48656
+ insert(_el$24, (() => {
48657
+ var _c$3 = memo2(() => !!merged.loading);
48658
+ return () => _c$3() ? (() => {
48659
+ var _el$27 = createElement("text");
48660
+ insertNode(_el$27, createTextNode(`Loading logs...`));
48661
+ setProp(_el$27, "fg", "gray");
48662
+ return _el$27;
48663
+ })() : memo2(() => filteredEntries().length === 0)() ? (() => {
48664
+ var _el$29 = createElement("text");
48665
+ insertNode(_el$29, createTextNode(`No logs available.`));
48666
+ setProp(_el$29, "fg", "gray");
48667
+ return _el$29;
48668
+ })() : (() => {
48669
+ var _el$31 = createElement("box");
48670
+ setProp(_el$31, "flexDirection", "column");
48671
+ insert(_el$31, () => list.visibleItems().map((entry, index) => {
48672
+ const absoluteIndex = list.scrollOffset() + index;
48673
+ const isSelected = absoluteIndex === list.selectedIndex();
48674
+ const segments = formatEntrySegments(entry);
48675
+ const lineText = segmentsToText(segments);
48676
+ const selectedContent = padLine3(lineText, terminal().columns);
48677
+ return (() => {
48678
+ var _el$32 = createElement("text");
48679
+ spread(_el$32, mergeProps3(() => isSelected ? {
48680
+ fg: selectionStyle.fg,
48681
+ bg: selectionStyle.bg
48682
+ } : {}, {
48683
+ wrapMode: "none",
48684
+ get width() {
48685
+ return terminal().columns;
48686
+ }
48687
+ }), true);
48688
+ insert(_el$32, () => isSelected ? selectedContent : segments.map((segment) => segment.fg ? (() => {
48689
+ var _el$33 = createElement("span");
48690
+ insert(_el$33, () => segment.text);
48691
+ effect((_$p) => setProp(_el$33, "style", {
48692
+ fg: segment.fg
48693
+ }, _$p));
48694
+ return _el$33;
48695
+ })() : segment.text));
48696
+ return _el$32;
48697
+ })();
48698
+ }));
48699
+ return _el$31;
48700
+ })();
48701
+ })(), null);
48702
+ insert(_el$24, (() => {
48703
+ var _c$4 = memo2(() => !!merged.error);
48704
+ return () => _c$4() ? (() => {
48705
+ var _el$34 = createElement("text");
48706
+ setProp(_el$34, "fg", "red");
48707
+ insert(_el$34, () => merged.error);
48708
+ return _el$34;
48709
+ })() : null;
48092
48710
  })(), null);
48093
- insert(_el$9, error ? (() => {
48094
- var _el$15 = createElement("text");
48095
- setProp(_el$15, "fg", "red");
48096
- insert(_el$15, error);
48097
- return _el$15;
48098
- })() : null, null);
48099
48711
  insert(_el$, createComponent2(Footer, {
48100
48712
  get actions() {
48101
48713
  return footerActions();
48102
48714
  }
48103
48715
  }), null);
48104
48716
  effect((_p$) => {
48105
- var _v$ = terminal().rows, _v$2 = TextAttributes.DIM, _v$3 = TextAttributes.BOLD, _v$4 = TextAttributes.DIM, _v$5 = TextAttributes.BOLD;
48717
+ var _v$ = terminal().rows, _v$2 = TextAttributes.DIM, _v$3 = TextAttributes.BOLD, _v$4 = TextAttributes.DIM, _v$5 = TextAttributes.BOLD, _v$6 = TextAttributes.DIM, _v$7 = TextAttributes.BOLD, _v$8 = TextAttributes.DIM, _v$9 = TextAttributes.BOLD, _v$0 = TextAttributes.DIM, _v$1 = TextAttributes.BOLD, _v$10 = TextAttributes.DIM, _v$11 = TextAttributes.BOLD, _v$12 = TextAttributes.DIM, _v$13 = TextAttributes.BOLD;
48106
48718
  _v$ !== _p$.e && (_p$.e = setProp(_el$, "height", _v$, _p$.e));
48107
48719
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$2, _p$.t));
48108
48720
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "attributes", _v$3, _p$.a));
48109
48721
  _v$4 !== _p$.o && (_p$.o = setProp(_el$6, "attributes", _v$4, _p$.o));
48110
48722
  _v$5 !== _p$.i && (_p$.i = setProp(_el$8, "attributes", _v$5, _p$.i));
48723
+ _v$6 !== _p$.n && (_p$.n = setProp(_el$0, "attributes", _v$6, _p$.n));
48724
+ _v$7 !== _p$.s && (_p$.s = setProp(_el$10, "attributes", _v$7, _p$.s));
48725
+ _v$8 !== _p$.h && (_p$.h = setProp(_el$11, "attributes", _v$8, _p$.h));
48726
+ _v$9 !== _p$.r && (_p$.r = setProp(_el$13, "attributes", _v$9, _p$.r));
48727
+ _v$0 !== _p$.d && (_p$.d = setProp(_el$14, "attributes", _v$0, _p$.d));
48728
+ _v$1 !== _p$.l && (_p$.l = setProp(_el$16, "attributes", _v$1, _p$.l));
48729
+ _v$10 !== _p$.u && (_p$.u = setProp(_el$17, "attributes", _v$10, _p$.u));
48730
+ _v$11 !== _p$.c && (_p$.c = setProp(_el$19, "attributes", _v$11, _p$.c));
48731
+ _v$12 !== _p$.w && (_p$.w = setProp(_el$21, "attributes", _v$12, _p$.w));
48732
+ _v$13 !== _p$.m && (_p$.m = setProp(_el$23, "attributes", _v$13, _p$.m));
48111
48733
  return _p$;
48112
48734
  }, {
48113
48735
  e: undefined,
48114
48736
  t: undefined,
48115
48737
  a: undefined,
48116
48738
  o: undefined,
48117
- i: undefined
48739
+ i: undefined,
48740
+ n: undefined,
48741
+ s: undefined,
48742
+ h: undefined,
48743
+ r: undefined,
48744
+ d: undefined,
48745
+ l: undefined,
48746
+ u: undefined,
48747
+ c: undefined,
48748
+ w: undefined,
48749
+ m: undefined
48118
48750
  });
48119
48751
  return _el$;
48120
48752
  })();
@@ -48473,9 +49105,16 @@ function ConfirmScreen({
48473
49105
  yesLabel = "Yes",
48474
49106
  noLabel = "No",
48475
49107
  defaultNo = false,
48476
- helpVisible = false
49108
+ helpVisible = false,
49109
+ width
48477
49110
  }) {
48478
49111
  const [selectedIndex, setSelectedIndex] = createSignal(defaultNo ? 1 : 0);
49112
+ const terminal = useTerminalSize();
49113
+ const contentWidth = () => Math.max(0, width ?? terminal().columns);
49114
+ const padLine4 = (value, width2) => {
49115
+ const padding = Math.max(0, width2 - stringWidth(value));
49116
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
49117
+ };
48479
49118
  const confirm = (confirmed) => {
48480
49119
  onConfirm(confirmed);
48481
49120
  };
@@ -48502,25 +49141,34 @@ function ConfirmScreen({
48502
49141
  confirm(selectedIndex() === 0);
48503
49142
  }
48504
49143
  });
48505
- const renderOption = (label, isSelected) => (() => {
49144
+ const renderOption = (label, isSelected) => isSelected ? (() => {
48506
49145
  var _el$ = createElement("text");
48507
- spread(_el$, mergeProps3(() => isSelected ? {
48508
- fg: "cyan",
48509
- attributes: TextAttributes.BOLD
48510
- } : {}), true);
48511
- insert(_el$, label);
49146
+ insert(_el$, () => padLine4(label, contentWidth()));
49147
+ effect((_p$) => {
49148
+ var _v$ = selectionStyle.fg, _v$2 = selectionStyle.bg;
49149
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "fg", _v$, _p$.e));
49150
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$, "bg", _v$2, _p$.t));
49151
+ return _p$;
49152
+ }, {
49153
+ e: undefined,
49154
+ t: undefined
49155
+ });
48512
49156
  return _el$;
49157
+ })() : (() => {
49158
+ var _el$2 = createElement("text");
49159
+ insert(_el$2, label);
49160
+ return _el$2;
48513
49161
  })();
48514
49162
  return (() => {
48515
- var _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("box");
48516
- insertNode(_el$2, _el$3);
48517
- insertNode(_el$2, _el$4);
48518
- setProp(_el$2, "flexDirection", "column");
48519
- insert(_el$3, message);
48520
- setProp(_el$4, "flexDirection", "column");
48521
- insert(_el$4, () => renderOption(yesLabel, selectedIndex() === 0), null);
48522
- insert(_el$4, () => renderOption(noLabel, selectedIndex() === 1), null);
48523
- return _el$2;
49163
+ var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$5 = createElement("box");
49164
+ insertNode(_el$3, _el$4);
49165
+ insertNode(_el$3, _el$5);
49166
+ setProp(_el$3, "flexDirection", "column");
49167
+ insert(_el$4, message);
49168
+ setProp(_el$5, "flexDirection", "column");
49169
+ insert(_el$5, () => renderOption(yesLabel, selectedIndex() === 0), null);
49170
+ insert(_el$5, () => renderOption(noLabel, selectedIndex() === 1), null);
49171
+ return _el$3;
48524
49172
  })();
48525
49173
  }
48526
49174
 
@@ -48535,6 +49183,10 @@ function EnvironmentScreen({
48535
49183
  helpVisible = false
48536
49184
  }) {
48537
49185
  const terminal = useTerminalSize();
49186
+ const padLine4 = (value, width) => {
49187
+ const padding = Math.max(0, width - stringWidth(value));
49188
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
49189
+ };
48538
49190
  const listHeight = createMemo(() => {
48539
49191
  const headerRows = 2;
48540
49192
  const footerRows = 1;
@@ -48630,27 +49282,36 @@ function EnvironmentScreen({
48630
49282
  const absoluteIndex = list.scrollOffset() + index;
48631
49283
  const isSelected = absoluteIndex === list.selectedIndex();
48632
49284
  const isHighlighted = highlightSet().has(variable.key);
48633
- const attributes = isSelected ? TextAttributes.BOLD : undefined;
48634
- const keyColor = isSelected ? "cyan" : isHighlighted ? "yellow" : undefined;
48635
- const valueColor = isSelected ? "cyan" : undefined;
49285
+ const keyColor = isHighlighted ? "yellow" : undefined;
48636
49286
  return (() => {
48637
- var _el$6 = createElement("box"), _el$7 = createElement("text"), _el$8 = createElement("text"), _el$9 = createTextNode(`=`);
48638
- insertNode(_el$6, _el$7);
48639
- insertNode(_el$6, _el$8);
49287
+ var _el$6 = createElement("box");
48640
49288
  setProp(_el$6, "flexDirection", "row");
48641
- spread(_el$7, mergeProps3(keyColor ? {
48642
- fg: keyColor
48643
- } : {}, attributes !== undefined ? {
48644
- attributes
48645
- } : {}), true);
48646
- insert(_el$7, () => variable.key);
48647
- insertNode(_el$8, _el$9);
48648
- spread(_el$8, mergeProps3(valueColor ? {
48649
- fg: valueColor
48650
- } : {}, attributes !== undefined ? {
48651
- attributes
48652
- } : {}), true);
48653
- insert(_el$8, () => variable.value, null);
49289
+ insert(_el$6, isSelected ? (() => {
49290
+ var _el$7 = createElement("text");
49291
+ insert(_el$7, () => padLine4(`${variable.key}=${variable.value}`, terminal().columns));
49292
+ effect((_p$) => {
49293
+ var _v$ = selectionStyle.fg, _v$2 = selectionStyle.bg;
49294
+ _v$ !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$, _p$.e));
49295
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "bg", _v$2, _p$.t));
49296
+ return _p$;
49297
+ }, {
49298
+ e: undefined,
49299
+ t: undefined
49300
+ });
49301
+ return _el$7;
49302
+ })() : [(() => {
49303
+ var _el$8 = createElement("text");
49304
+ spread(_el$8, keyColor ? {
49305
+ fg: keyColor
49306
+ } : {}, true);
49307
+ insert(_el$8, () => variable.key);
49308
+ return _el$8;
49309
+ })(), (() => {
49310
+ var _el$9 = createElement("text"), _el$0 = createTextNode(`=`);
49311
+ insertNode(_el$9, _el$0);
49312
+ insert(_el$9, () => variable.value, null);
49313
+ return _el$9;
49314
+ })()]);
48654
49315
  return _el$6;
48655
49316
  })();
48656
49317
  }));
@@ -48752,6 +49413,10 @@ function ProfileEnvScreen({
48752
49413
  helpVisible = false
48753
49414
  }) {
48754
49415
  const terminal = useTerminalSize();
49416
+ const padLine4 = (value, width) => {
49417
+ const padding = Math.max(0, width - stringWidth(value));
49418
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
49419
+ };
48755
49420
  const listHeight = createMemo(() => {
48756
49421
  const headerRows = 2;
48757
49422
  const footerRows = 1;
@@ -48882,26 +49547,32 @@ function ProfileEnvScreen({
48882
49547
  insert(_el$5, () => list.visibleItems().map((variable, index) => {
48883
49548
  const absoluteIndex = list.scrollOffset() + index;
48884
49549
  const isSelected = absoluteIndex === list.selectedIndex();
48885
- const attributes = isSelected ? TextAttributes.BOLD : undefined;
48886
- const color = isSelected ? "cyan" : undefined;
48887
49550
  return (() => {
48888
- var _el$6 = createElement("box"), _el$7 = createElement("text"), _el$8 = createElement("text"), _el$9 = createTextNode(`=`);
48889
- insertNode(_el$6, _el$7);
48890
- insertNode(_el$6, _el$8);
49551
+ var _el$6 = createElement("box");
48891
49552
  setProp(_el$6, "flexDirection", "row");
48892
- spread(_el$7, mergeProps3(color ? {
48893
- fg: color
48894
- } : {}, attributes !== undefined ? {
48895
- attributes
48896
- } : {}), true);
48897
- insert(_el$7, () => variable.key);
48898
- insertNode(_el$8, _el$9);
48899
- spread(_el$8, mergeProps3(color ? {
48900
- fg: color
48901
- } : {}, attributes !== undefined ? {
48902
- attributes
48903
- } : {}), true);
48904
- insert(_el$8, () => variable.value, null);
49553
+ insert(_el$6, isSelected ? (() => {
49554
+ var _el$7 = createElement("text");
49555
+ insert(_el$7, () => padLine4(`${variable.key}=${variable.value}`, terminal().columns));
49556
+ effect((_p$) => {
49557
+ var _v$ = selectionStyle.fg, _v$2 = selectionStyle.bg;
49558
+ _v$ !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$, _p$.e));
49559
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "bg", _v$2, _p$.t));
49560
+ return _p$;
49561
+ }, {
49562
+ e: undefined,
49563
+ t: undefined
49564
+ });
49565
+ return _el$7;
49566
+ })() : [(() => {
49567
+ var _el$8 = createElement("text");
49568
+ insert(_el$8, () => variable.key);
49569
+ return _el$8;
49570
+ })(), (() => {
49571
+ var _el$9 = createElement("text"), _el$0 = createTextNode(`=`);
49572
+ insertNode(_el$9, _el$0);
49573
+ insert(_el$9, () => variable.value, null);
49574
+ return _el$9;
49575
+ })()]);
48905
49576
  return _el$6;
48906
49577
  })();
48907
49578
  }));
@@ -49000,7 +49671,8 @@ function createLogger(config = {}) {
49000
49671
  if (config.sync) {
49001
49672
  const destinationStream2 = import_pino.default.destination({
49002
49673
  dest: destination,
49003
- sync: true
49674
+ sync: true,
49675
+ append: true
49004
49676
  });
49005
49677
  return import_pino.default(options, destinationStream2);
49006
49678
  }
@@ -49024,7 +49696,8 @@ function createLogger(config = {}) {
49024
49696
  }
49025
49697
  const destinationStream = import_pino.default.destination({
49026
49698
  dest: destination,
49027
- sync: false
49699
+ sync: false,
49700
+ append: true
49028
49701
  });
49029
49702
  return import_pino.default(options, destinationStream);
49030
49703
  }
@@ -49819,6 +50492,9 @@ async function listWorktrees() {
49819
50492
  throw new WorktreeError("Failed to list worktrees", error);
49820
50493
  }
49821
50494
  }
50495
+ async function listAllWorktrees() {
50496
+ return listWorktrees();
50497
+ }
49822
50498
  async function listAdditionalWorktrees() {
49823
50499
  try {
49824
50500
  const [allWorktrees, repoRoot] = await Promise.all([
@@ -49962,7 +50638,8 @@ async function getWorktreesWithPRStatus() {
49962
50638
  }
49963
50639
  async function getOrphanedLocalBranchStatuses({
49964
50640
  baseBranch,
49965
- repoRoot
50641
+ repoRoot,
50642
+ onProgress
49966
50643
  }) {
49967
50644
  try {
49968
50645
  const [localBranches, worktrees] = await Promise.all([
@@ -49991,8 +50668,7 @@ async function getOrphanedLocalBranchStatuses({
49991
50668
  hasUnpushed = true;
49992
50669
  }
49993
50670
  const { upstream, hasUpstream } = await resolveUpstreamStatus(localBranch.name, repoRoot);
49994
- const comparisonBase = upstream ?? baseBranch;
49995
- const hasUniqueCommits = await branchHasUniqueCommitsComparedToBase(localBranch.name, comparisonBase, repoRoot);
50671
+ const hasUniqueCommits = await branchHasUniqueCommitsComparedToBase(localBranch.name, baseBranch, repoRoot);
49996
50672
  const reasons = buildCleanupReasons({
49997
50673
  hasUniqueCommits,
49998
50674
  hasUncommitted: false,
@@ -50002,7 +50678,7 @@ async function getOrphanedLocalBranchStatuses({
50002
50678
  if (process.env.DEBUG_CLEANUP) {
50003
50679
  console.log(source_default.gray(`Debug: Checking orphaned branch ${localBranch.name} -> reasons: ${reasons.join(", ")}`));
50004
50680
  }
50005
- statuses.push({
50681
+ const status = {
50006
50682
  worktreePath: null,
50007
50683
  branch: localBranch.name,
50008
50684
  hasUncommittedChanges: false,
@@ -50013,7 +50689,9 @@ async function getOrphanedLocalBranchStatuses({
50013
50689
  hasUpstream,
50014
50690
  upstream,
50015
50691
  reasons
50016
- });
50692
+ };
50693
+ statuses.push(status);
50694
+ onProgress?.(status);
50017
50695
  }
50018
50696
  }
50019
50697
  if (process.env.DEBUG_CLEANUP) {
@@ -50028,7 +50706,9 @@ async function getOrphanedLocalBranchStatuses({
50028
50706
  return [];
50029
50707
  }
50030
50708
  }
50031
- async function getCleanupStatus() {
50709
+ async function getCleanupStatus({
50710
+ onProgress
50711
+ } = {}) {
50032
50712
  const [config, repoRoot, worktreesWithPR] = await Promise.all([
50033
50713
  getConfig(),
50034
50714
  getRepositoryRoot(),
@@ -50037,7 +50717,8 @@ async function getCleanupStatus() {
50037
50717
  const baseBranch = config.defaultBaseBranch || GIT_CONFIG.DEFAULT_BASE_BRANCH;
50038
50718
  const orphanedStatuses = await getOrphanedLocalBranchStatuses({
50039
50719
  baseBranch,
50040
- repoRoot
50720
+ repoRoot,
50721
+ ...onProgress ? { onProgress } : {}
50041
50722
  });
50042
50723
  const statuses = [];
50043
50724
  if (process.env.DEBUG_CLEANUP) {
@@ -50066,8 +50747,7 @@ async function getCleanupStatus() {
50066
50747
  }
50067
50748
  }
50068
50749
  const { upstream, hasUpstream } = await resolveUpstreamStatus(worktree.branch, repoRoot);
50069
- const comparisonBase = upstream ?? baseBranch;
50070
- const hasUniqueCommits = await branchHasUniqueCommitsComparedToBase(worktree.branch, comparisonBase, repoRoot);
50750
+ const hasUniqueCommits = await branchHasUniqueCommitsComparedToBase(worktree.branch, baseBranch, repoRoot);
50071
50751
  const reasons = buildCleanupReasons({
50072
50752
  hasUniqueCommits,
50073
50753
  hasUncommitted,
@@ -50077,7 +50757,7 @@ async function getCleanupStatus() {
50077
50757
  if (process.env.DEBUG_CLEANUP) {
50078
50758
  console.log(source_default.gray(`Debug: Cleanup reasons for ${worktree.branch}: ${reasons.length > 0 ? reasons.join(", ") : "none"}`));
50079
50759
  }
50080
- statuses.push({
50760
+ const status = {
50081
50761
  worktreePath: worktree.worktreePath,
50082
50762
  branch: worktree.branch,
50083
50763
  hasUncommittedChanges: hasUncommitted,
@@ -50090,7 +50770,9 @@ async function getCleanupStatus() {
50090
50770
  isAccessible,
50091
50771
  reasons,
50092
50772
  ...isAccessible ? {} : { invalidReason: "Path not accessible in current environment" }
50093
- });
50773
+ };
50774
+ statuses.push(status);
50775
+ onProgress?.(status);
50094
50776
  }
50095
50777
  statuses.push(...orphanedStatuses);
50096
50778
  if (process.env.DEBUG_CLEANUP) {
@@ -50101,22 +50783,643 @@ async function getCleanupStatus() {
50101
50783
  return statuses;
50102
50784
  }
50103
50785
 
50104
- // src/config/tools.ts
50105
- import { homedir as homedir4 } from "os";
50786
+ // src/utils/session/common.ts
50787
+ import path14 from "path";
50788
+ import { readdir, readFile as readFile2, stat } from "fs/promises";
50789
+ var UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
50790
+ function isValidUuidSessionId(id) {
50791
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
50792
+ }
50793
+ function pickSessionIdFromObject(obj) {
50794
+ if (!obj || typeof obj !== "object")
50795
+ return null;
50796
+ const candidate = obj;
50797
+ const keys = ["sessionId", "session_id", "id", "conversation_id"];
50798
+ for (const key of keys) {
50799
+ const value = candidate[key];
50800
+ if (typeof value === "string" && value.trim().length > 0) {
50801
+ const trimmed = value.trim();
50802
+ if (isValidUuidSessionId(trimmed)) {
50803
+ return trimmed;
50804
+ }
50805
+ }
50806
+ }
50807
+ return null;
50808
+ }
50809
+ function pickCwdFromObject(obj) {
50810
+ if (!obj || typeof obj !== "object")
50811
+ return null;
50812
+ const candidate = obj;
50813
+ const keys = [
50814
+ "cwd",
50815
+ "workingDirectory",
50816
+ "workdir",
50817
+ "directory",
50818
+ "projectPath"
50819
+ ];
50820
+ for (const key of keys) {
50821
+ const value = candidate[key];
50822
+ if (typeof value === "string" && value.trim().length > 0) {
50823
+ return value;
50824
+ }
50825
+ }
50826
+ const payload = candidate["payload"];
50827
+ if (payload && typeof payload === "object") {
50828
+ const nested = pickCwdFromObject(payload);
50829
+ if (nested)
50830
+ return nested;
50831
+ }
50832
+ return null;
50833
+ }
50834
+ function pickSessionIdFromText(content) {
50835
+ try {
50836
+ const parsed = JSON.parse(content);
50837
+ const fromObject = pickSessionIdFromObject(parsed);
50838
+ if (fromObject)
50839
+ return fromObject;
50840
+ } catch {}
50841
+ const lines = content.split(/\r?\n/);
50842
+ for (const line of lines) {
50843
+ const trimmed = line.trim();
50844
+ if (!trimmed)
50845
+ continue;
50846
+ try {
50847
+ const parsedLine = JSON.parse(trimmed);
50848
+ const fromLine = pickSessionIdFromObject(parsedLine);
50849
+ if (fromLine)
50850
+ return fromLine;
50851
+ } catch {}
50852
+ const match2 = trimmed.match(UUID_REGEX);
50853
+ if (match2)
50854
+ return match2[0];
50855
+ }
50856
+ const match = content.match(UUID_REGEX);
50857
+ return match ? match[0] : null;
50858
+ }
50859
+ async function collectFilesIterative(dir, filter) {
50860
+ const results = [];
50861
+ const queue = [dir];
50862
+ while (queue.length > 0) {
50863
+ const currentDir = queue.shift();
50864
+ if (!currentDir)
50865
+ break;
50866
+ try {
50867
+ const entries = await readdir(currentDir, { withFileTypes: true });
50868
+ for (const entry of entries) {
50869
+ const fullPath = path14.join(currentDir, entry.name);
50870
+ if (entry.isDirectory()) {
50871
+ queue.push(fullPath);
50872
+ } else if (entry.isFile() && filter(entry.name)) {
50873
+ try {
50874
+ const info = await stat(fullPath);
50875
+ results.push({ fullPath, mtime: info.mtimeMs });
50876
+ } catch {}
50877
+ }
50878
+ }
50879
+ } catch {}
50880
+ }
50881
+ return results;
50882
+ }
50883
+ async function readSessionIdFromFile(filePath) {
50884
+ try {
50885
+ const basename2 = path14.basename(filePath);
50886
+ const filenameWithoutExt = basename2.replace(/\.(json|jsonl)$/i, "");
50887
+ if (isValidUuidSessionId(filenameWithoutExt)) {
50888
+ return filenameWithoutExt;
50889
+ }
50890
+ const content = await readFile2(filePath, "utf-8");
50891
+ const fromContent = pickSessionIdFromText(content);
50892
+ if (fromContent)
50893
+ return fromContent;
50894
+ const filenameMatch = basename2.match(UUID_REGEX);
50895
+ return filenameMatch ? filenameMatch[0] : null;
50896
+ } catch {
50897
+ return null;
50898
+ }
50899
+ }
50900
+ async function readSessionInfoFromFile(filePath) {
50901
+ try {
50902
+ const content = await readFile2(filePath, "utf-8");
50903
+ try {
50904
+ const parsed = JSON.parse(content);
50905
+ const id = pickSessionIdFromObject(parsed);
50906
+ const cwd = pickCwdFromObject(parsed);
50907
+ if (id || cwd)
50908
+ return { id, cwd };
50909
+ } catch {}
50910
+ const lines = content.split(/\r?\n/);
50911
+ for (const line of lines) {
50912
+ const trimmed = line.trim();
50913
+ if (!trimmed)
50914
+ continue;
50915
+ try {
50916
+ const parsedLine = JSON.parse(trimmed);
50917
+ const id = pickSessionIdFromObject(parsedLine);
50918
+ const cwd = pickCwdFromObject(parsedLine);
50919
+ if (id || cwd)
50920
+ return { id, cwd };
50921
+ } catch {}
50922
+ }
50923
+ const filenameMatch = path14.basename(filePath).match(UUID_REGEX);
50924
+ if (filenameMatch)
50925
+ return { id: filenameMatch[0], cwd: null };
50926
+ } catch {}
50927
+ return { id: null, cwd: null };
50928
+ }
50929
+ async function findNewestSessionIdFromDir(dir, recursive, options = {}) {
50930
+ try {
50931
+ const files = [];
50932
+ const queue = [dir];
50933
+ while (queue.length > 0) {
50934
+ const currentDir = queue.shift();
50935
+ if (!currentDir)
50936
+ break;
50937
+ try {
50938
+ const entries = await readdir(currentDir, { withFileTypes: true });
50939
+ for (const entry of entries) {
50940
+ const fullPath = path14.join(currentDir, entry.name);
50941
+ if (entry.isDirectory()) {
50942
+ if (recursive) {
50943
+ queue.push(fullPath);
50944
+ }
50945
+ continue;
50946
+ }
50947
+ if (!entry.isFile())
50948
+ continue;
50949
+ if (!entry.name.endsWith(".json") && !entry.name.endsWith(".jsonl"))
50950
+ continue;
50951
+ try {
50952
+ const info = await stat(fullPath);
50953
+ files.push({ fullPath, mtime: info.mtimeMs });
50954
+ } catch {}
50955
+ }
50956
+ } catch {}
50957
+ }
50958
+ const filtered = files.filter((f) => {
50959
+ if (options.since !== undefined && f.mtime < options.since)
50960
+ return false;
50961
+ if (options.until !== undefined && f.mtime > options.until)
50962
+ return false;
50963
+ return true;
50964
+ });
50965
+ if (!filtered.length)
50966
+ return null;
50967
+ let pool = filtered.sort((a2, b) => b.mtime - a2.mtime);
50968
+ const ref = options.preferClosestTo;
50969
+ if (typeof ref === "number") {
50970
+ const window = options.windowMs ?? 30 * 60 * 1000;
50971
+ const withinWindow = pool.filter((f) => Math.abs(f.mtime - ref) <= window);
50972
+ if (withinWindow.length) {
50973
+ pool = withinWindow.sort((a2, b) => b.mtime - a2.mtime);
50974
+ }
50975
+ }
50976
+ for (const file of pool) {
50977
+ const id = await readSessionIdFromFile(file.fullPath);
50978
+ if (id)
50979
+ return { id, mtime: file.mtime };
50980
+ }
50981
+ } catch {}
50982
+ return null;
50983
+ }
50984
+ async function readFileContent(filePath) {
50985
+ return readFile2(filePath, "utf-8");
50986
+ }
50987
+ async function checkFileStat(filePath) {
50988
+ try {
50989
+ const info = await stat(filePath);
50990
+ return { mtimeMs: info.mtimeMs };
50991
+ } catch {
50992
+ return null;
50993
+ }
50994
+ }
50995
+ function normalizePath(p) {
50996
+ return path14.normalize(p).replace(/\\/g, "/");
50997
+ }
50998
+ function isPathPrefix(prefix, full) {
50999
+ if (!full.startsWith(prefix))
51000
+ return false;
51001
+ if (full.length === prefix.length)
51002
+ return true;
51003
+ return full[prefix.length] === "/";
51004
+ }
51005
+ function matchesCwd(sessionCwd, targetCwd) {
51006
+ if (!sessionCwd)
51007
+ return false;
51008
+ const normalizedSession = normalizePath(sessionCwd);
51009
+ const normalizedTarget = normalizePath(targetCwd);
51010
+ return normalizedSession === normalizedTarget || isPathPrefix(normalizedTarget, normalizedSession) || isPathPrefix(normalizedSession, normalizedTarget);
51011
+ }
51012
+ function resolveBranchFromCwd(sessionCwd, worktrees) {
51013
+ if (!sessionCwd)
51014
+ return null;
51015
+ const normalizedSession = normalizePath(sessionCwd);
51016
+ let bestMatch = null;
51017
+ for (const worktree of worktrees) {
51018
+ if (!worktree?.path || !worktree.branch)
51019
+ continue;
51020
+ const normalizedPath = normalizePath(worktree.path);
51021
+ if (normalizedSession === normalizedPath || isPathPrefix(normalizedPath, normalizedSession)) {
51022
+ if (!bestMatch || normalizedPath.length > bestMatch.path.length) {
51023
+ bestMatch = { branch: worktree.branch, path: normalizedPath };
51024
+ }
51025
+ }
51026
+ }
51027
+ return bestMatch?.branch ?? null;
51028
+ }
51029
+ // src/utils/session/parsers/claude.ts
50106
51030
  import path15 from "path";
50107
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, rename as rename2 } from "fs/promises";
51031
+ import { homedir as homedir3 } from "os";
51032
+ function encodeClaudeProjectPath(cwd) {
51033
+ const normalized = cwd.replace(/\\/g, "/").replace(/:/g, "");
51034
+ return normalized.replace(/_/g, "-").replace(/\//g, "-");
51035
+ }
51036
+ function generateClaudeProjectPathCandidates(cwd) {
51037
+ const base2 = encodeClaudeProjectPath(cwd);
51038
+ const dotToDash = cwd.replace(/\\/g, "/").replace(/:/g, "").replace(/\./g, "-").replace(/_/g, "-").replace(/\//g, "-");
51039
+ const collapsed = dotToDash.replace(/-+/g, "-");
51040
+ const candidates = [base2, dotToDash, collapsed];
51041
+ return Array.from(new Set(candidates));
51042
+ }
51043
+ function getClaudeRootCandidates() {
51044
+ const roots = [];
51045
+ if (process.env.CLAUDE_CONFIG_DIR) {
51046
+ roots.push(process.env.CLAUDE_CONFIG_DIR);
51047
+ }
51048
+ roots.push(path15.join(homedir3(), ".claude"), path15.join(homedir3(), ".config", "claude"));
51049
+ return roots;
51050
+ }
51051
+ async function findLatestClaudeSession(cwd, options = {}) {
51052
+ const rootCandidates = getClaudeRootCandidates();
51053
+ const branchFilter = typeof options.branch === "string" && options.branch.trim().length > 0 ? options.branch.trim() : null;
51054
+ let cwdCandidates = [];
51055
+ if (branchFilter) {
51056
+ let worktrees = [];
51057
+ if (Array.isArray(options.worktrees) && options.worktrees.length > 0) {
51058
+ worktrees = options.worktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51059
+ } else {
51060
+ try {
51061
+ const allWorktrees = await listAllWorktrees();
51062
+ worktrees = allWorktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51063
+ } catch {
51064
+ worktrees = [];
51065
+ }
51066
+ }
51067
+ const matches = worktrees.filter((entry) => entry.branch === branchFilter).map((entry) => entry.path);
51068
+ if (!matches.length)
51069
+ return null;
51070
+ cwdCandidates = matches;
51071
+ } else {
51072
+ const baseCwd = options.cwd ?? cwd;
51073
+ if (!baseCwd)
51074
+ return null;
51075
+ cwdCandidates = [baseCwd];
51076
+ }
51077
+ const uniqueCwds = Array.from(new Set(cwdCandidates));
51078
+ for (const candidateCwd of uniqueCwds) {
51079
+ const encodedPaths = generateClaudeProjectPathCandidates(candidateCwd);
51080
+ for (const claudeRoot of rootCandidates) {
51081
+ for (const encoded of encodedPaths) {
51082
+ const projectDir = path15.join(claudeRoot, "projects", encoded);
51083
+ const sessionsDir = path15.join(projectDir, "sessions");
51084
+ const session = await findNewestSessionIdFromDir(sessionsDir, false, options);
51085
+ if (session)
51086
+ return session;
51087
+ const rootSession = await findNewestSessionIdFromDir(projectDir, true, options);
51088
+ if (rootSession)
51089
+ return rootSession;
51090
+ }
51091
+ }
51092
+ }
51093
+ try {
51094
+ const historyPath = path15.join(homedir3(), ".claude", "history.jsonl");
51095
+ const historyStat = await checkFileStat(historyPath);
51096
+ if (!historyStat)
51097
+ return null;
51098
+ const content = await readFileContent(historyPath);
51099
+ const lines = content.split(/\r?\n/).filter(Boolean);
51100
+ for (let i2 = lines.length - 1;i2 >= 0; i2 -= 1) {
51101
+ try {
51102
+ const line = lines[i2] ?? "";
51103
+ const parsed = JSON.parse(line);
51104
+ const project = typeof parsed.project === "string" ? parsed.project : null;
51105
+ const sessionId = typeof parsed.sessionId === "string" ? parsed.sessionId : null;
51106
+ const matchesProject = uniqueCwds.some((candidate) => matchesCwd(project, candidate));
51107
+ if (project && sessionId && matchesProject) {
51108
+ return { id: sessionId, mtime: historyStat.mtimeMs };
51109
+ }
51110
+ } catch {}
51111
+ }
51112
+ } catch {}
51113
+ return null;
51114
+ }
51115
+ // src/utils/session/parsers/codex.ts
51116
+ import path16 from "path";
51117
+ import { homedir as homedir4 } from "os";
51118
+ async function findLatestCodexSession(options = {}) {
51119
+ const codexHome = process.env.CODEX_HOME ?? path16.join(homedir4(), ".codex");
51120
+ const baseDir = path16.join(codexHome, "sessions");
51121
+ const candidates = await collectFilesIterative(baseDir, (name) => name.endsWith(".json") || name.endsWith(".jsonl"));
51122
+ if (!candidates.length)
51123
+ return null;
51124
+ let pool = candidates;
51125
+ const sinceVal = options.since;
51126
+ const untilVal = options.until;
51127
+ if (sinceVal !== undefined) {
51128
+ pool = pool.filter((c3) => c3.mtime >= sinceVal);
51129
+ }
51130
+ if (untilVal !== undefined) {
51131
+ pool = pool.filter((c3) => c3.mtime <= untilVal);
51132
+ }
51133
+ if (!pool.length)
51134
+ return null;
51135
+ const ref = options.preferClosestTo;
51136
+ const window = options.windowMs ?? 30 * 60 * 1000;
51137
+ const ordered = [...pool].sort((a2, b) => {
51138
+ if (typeof ref === "number") {
51139
+ const da = Math.abs(a2.mtime - ref);
51140
+ const db = Math.abs(b.mtime - ref);
51141
+ if (da === db)
51142
+ return b.mtime - a2.mtime;
51143
+ if (da <= window || db <= window)
51144
+ return da - db;
51145
+ }
51146
+ return b.mtime - a2.mtime;
51147
+ });
51148
+ const branchFilter = typeof options.branch === "string" && options.branch.trim().length > 0 ? options.branch.trim() : null;
51149
+ const shouldCheckBranch = Boolean(branchFilter);
51150
+ const shouldCheckCwd = Boolean(options.cwd) && !shouldCheckBranch;
51151
+ let worktrees = [];
51152
+ if (shouldCheckBranch) {
51153
+ if (Array.isArray(options.worktrees) && options.worktrees.length > 0) {
51154
+ worktrees = options.worktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51155
+ } else {
51156
+ try {
51157
+ const allWorktrees = await listAllWorktrees();
51158
+ worktrees = allWorktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51159
+ } catch {
51160
+ worktrees = [];
51161
+ }
51162
+ }
51163
+ if (!worktrees.length)
51164
+ return null;
51165
+ }
51166
+ for (const file of ordered) {
51167
+ const filenameMatch = path16.basename(file.fullPath).match(UUID_REGEX);
51168
+ const idFromName = filenameMatch?.[0] ?? null;
51169
+ const needsInfo = shouldCheckBranch || shouldCheckCwd || !idFromName;
51170
+ const info = needsInfo ? await readSessionInfoFromFile(file.fullPath) : null;
51171
+ const sessionCwd = info?.cwd ?? null;
51172
+ if (shouldCheckBranch) {
51173
+ const resolvedBranch = resolveBranchFromCwd(sessionCwd, worktrees);
51174
+ if (resolvedBranch !== branchFilter) {
51175
+ continue;
51176
+ }
51177
+ }
51178
+ if (shouldCheckCwd && options.cwd) {
51179
+ if (!matchesCwd(sessionCwd, options.cwd)) {
51180
+ continue;
51181
+ }
51182
+ }
51183
+ const sessionId = idFromName ?? info?.id ?? null;
51184
+ if (sessionId) {
51185
+ return { id: sessionId, mtime: file.mtime };
51186
+ }
51187
+ }
51188
+ return null;
51189
+ }
51190
+ // src/utils/session/parsers/gemini.ts
51191
+ import path17 from "path";
51192
+ import { homedir as homedir5 } from "os";
51193
+ async function findLatestGeminiSession(options = {}) {
51194
+ const baseDir = path17.join(homedir5(), ".gemini", "tmp");
51195
+ const files = await collectFilesIterative(baseDir, (name) => name.endsWith(".json") || name.endsWith(".jsonl"));
51196
+ if (!files.length)
51197
+ return null;
51198
+ let pool = files;
51199
+ const sinceVal = options.since;
51200
+ if (sinceVal !== undefined) {
51201
+ pool = pool.filter((f) => f.mtime >= sinceVal);
51202
+ }
51203
+ const untilVal = options.until;
51204
+ if (untilVal !== undefined) {
51205
+ pool = pool.filter((f) => f.mtime <= untilVal);
51206
+ }
51207
+ if (!pool.length)
51208
+ return null;
51209
+ const ref = options.preferClosestTo;
51210
+ const window = options.windowMs ?? 30 * 60 * 1000;
51211
+ pool = pool.slice().sort((a2, b) => {
51212
+ if (typeof ref === "number") {
51213
+ const da = Math.abs(a2.mtime - ref);
51214
+ const db = Math.abs(b.mtime - ref);
51215
+ if (da === db)
51216
+ return b.mtime - a2.mtime;
51217
+ if (da <= window || db <= window)
51218
+ return da - db;
51219
+ }
51220
+ return b.mtime - a2.mtime;
51221
+ });
51222
+ const branchFilter = typeof options.branch === "string" && options.branch.trim().length > 0 ? options.branch.trim() : null;
51223
+ const shouldCheckBranch = Boolean(branchFilter);
51224
+ const shouldCheckCwd = Boolean(options.cwd) && !shouldCheckBranch;
51225
+ let worktrees = [];
51226
+ if (shouldCheckBranch) {
51227
+ if (Array.isArray(options.worktrees) && options.worktrees.length > 0) {
51228
+ worktrees = options.worktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51229
+ } else {
51230
+ try {
51231
+ const allWorktrees = await listAllWorktrees();
51232
+ worktrees = allWorktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51233
+ } catch {
51234
+ worktrees = [];
51235
+ }
51236
+ }
51237
+ if (!worktrees.length)
51238
+ return null;
51239
+ }
51240
+ for (const file of pool) {
51241
+ const info = await readSessionInfoFromFile(file.fullPath);
51242
+ if (!info.id)
51243
+ continue;
51244
+ const sessionCwd = info.cwd ?? null;
51245
+ if (shouldCheckBranch) {
51246
+ const resolvedBranch = resolveBranchFromCwd(sessionCwd, worktrees);
51247
+ if (resolvedBranch !== branchFilter) {
51248
+ continue;
51249
+ }
51250
+ }
51251
+ if (shouldCheckCwd && options.cwd) {
51252
+ if (!matchesCwd(sessionCwd, options.cwd)) {
51253
+ continue;
51254
+ }
51255
+ }
51256
+ return { id: info.id, mtime: file.mtime };
51257
+ }
51258
+ return null;
51259
+ }
51260
+ // src/utils/session/parsers/opencode.ts
51261
+ import path18 from "path";
51262
+ import { homedir as homedir6 } from "os";
51263
+ function getOpenCodeSessionDir() {
51264
+ return path18.join(homedir6(), ".local", "share", "opencode", "storage", "session");
51265
+ }
51266
+ async function findLatestOpenCodeSession(options = {}) {
51267
+ const baseDir = getOpenCodeSessionDir();
51268
+ const files = await collectFilesIterative(baseDir, (name) => name.endsWith(".json") || name.endsWith(".jsonl"));
51269
+ if (!files.length)
51270
+ return null;
51271
+ let pool = files;
51272
+ const sinceVal = options.since;
51273
+ if (sinceVal !== undefined) {
51274
+ pool = pool.filter((f) => f.mtime >= sinceVal);
51275
+ }
51276
+ const untilVal = options.until;
51277
+ if (untilVal !== undefined) {
51278
+ pool = pool.filter((f) => f.mtime <= untilVal);
51279
+ }
51280
+ if (!pool.length)
51281
+ return null;
51282
+ const ref = options.preferClosestTo;
51283
+ const window = options.windowMs ?? 30 * 60 * 1000;
51284
+ pool = pool.slice().sort((a2, b) => {
51285
+ if (typeof ref === "number") {
51286
+ const da = Math.abs(a2.mtime - ref);
51287
+ const db = Math.abs(b.mtime - ref);
51288
+ if (da === db)
51289
+ return b.mtime - a2.mtime;
51290
+ if (da <= window || db <= window)
51291
+ return da - db;
51292
+ }
51293
+ return b.mtime - a2.mtime;
51294
+ });
51295
+ const branchFilter = typeof options.branch === "string" && options.branch.trim().length > 0 ? options.branch.trim() : null;
51296
+ const shouldCheckBranch = Boolean(branchFilter);
51297
+ const shouldCheckCwd = Boolean(options.cwd) && !shouldCheckBranch;
51298
+ let worktrees = [];
51299
+ if (shouldCheckBranch) {
51300
+ if (Array.isArray(options.worktrees) && options.worktrees.length > 0) {
51301
+ worktrees = options.worktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51302
+ } else {
51303
+ try {
51304
+ const allWorktrees = await listAllWorktrees();
51305
+ worktrees = allWorktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51306
+ } catch {
51307
+ worktrees = [];
51308
+ }
51309
+ }
51310
+ if (!worktrees.length)
51311
+ return null;
51312
+ }
51313
+ for (const file of pool) {
51314
+ const info = await readSessionInfoFromFile(file.fullPath);
51315
+ if (!info.id)
51316
+ continue;
51317
+ const sessionCwd = info.cwd ?? null;
51318
+ if (shouldCheckBranch) {
51319
+ const resolvedBranch = resolveBranchFromCwd(sessionCwd, worktrees);
51320
+ if (resolvedBranch !== branchFilter) {
51321
+ continue;
51322
+ }
51323
+ }
51324
+ if (shouldCheckCwd && options.cwd) {
51325
+ if (!matchesCwd(sessionCwd, options.cwd)) {
51326
+ continue;
51327
+ }
51328
+ }
51329
+ return { id: info.id, mtime: file.mtime };
51330
+ }
51331
+ return null;
51332
+ }
51333
+ // src/cli/ui/utils/continueSession.ts
51334
+ function findLatestBranchSessionsByTool(history, branch, worktreePath) {
51335
+ const byBranch = history.filter((entry) => entry && entry.branch === branch);
51336
+ if (!byBranch.length)
51337
+ return [];
51338
+ const source = worktreePath ? byBranch.filter((entry) => entry.worktreePath === worktreePath) : byBranch;
51339
+ if (!source.length)
51340
+ return [];
51341
+ const latestByTool = new Map;
51342
+ for (const entry of source) {
51343
+ if (!entry.toolId)
51344
+ continue;
51345
+ const current = latestByTool.get(entry.toolId);
51346
+ const currentTs = current?.timestamp ?? 0;
51347
+ const entryTs = entry.timestamp ?? 0;
51348
+ if (!current || entryTs >= currentTs) {
51349
+ latestByTool.set(entry.toolId, entry);
51350
+ }
51351
+ }
51352
+ return Array.from(latestByTool.values()).sort((a2, b) => (b.timestamp ?? 0) - (a2.timestamp ?? 0));
51353
+ }
51354
+ async function refreshQuickStartEntries(entries, context, lookups = {}) {
51355
+ if (!entries.length)
51356
+ return entries;
51357
+ const worktreePath = context.worktreePath ?? null;
51358
+ if (!worktreePath)
51359
+ return entries;
51360
+ const lookupWorktrees = lookups.listAllWorktrees ?? listAllWorktrees;
51361
+ let resolvedWorktrees = null;
51362
+ try {
51363
+ const allWorktrees = await lookupWorktrees();
51364
+ resolvedWorktrees = allWorktrees.filter((entry) => entry?.path && entry?.branch).map((entry) => ({ path: entry.path, branch: entry.branch }));
51365
+ } catch {
51366
+ resolvedWorktrees = null;
51367
+ }
51368
+ const searchOptions = {
51369
+ branch: context.branch,
51370
+ ...resolvedWorktrees && resolvedWorktrees.length > 0 ? { worktrees: resolvedWorktrees } : {}
51371
+ };
51372
+ const lookupCodex = lookups.findLatestCodexSession ?? findLatestCodexSession;
51373
+ const lookupClaude = lookups.findLatestClaudeSession ?? findLatestClaudeSession;
51374
+ const lookupGemini = lookups.findLatestGeminiSession ?? findLatestGeminiSession;
51375
+ const lookupOpenCode = lookups.findLatestOpenCodeSession ?? findLatestOpenCodeSession;
51376
+ const updated = await Promise.all(entries.map(async (entry) => {
51377
+ let latest = null;
51378
+ switch (entry.toolId) {
51379
+ case "codex-cli":
51380
+ latest = await lookupCodex(searchOptions);
51381
+ break;
51382
+ case "claude-code":
51383
+ latest = await lookupClaude(worktreePath, searchOptions);
51384
+ break;
51385
+ case "gemini-cli":
51386
+ latest = await lookupGemini(searchOptions);
51387
+ break;
51388
+ case "opencode":
51389
+ latest = await lookupOpenCode(searchOptions);
51390
+ break;
51391
+ default:
51392
+ return entry;
51393
+ }
51394
+ if (!latest?.id)
51395
+ return entry;
51396
+ const updatedTimestamp = Math.max(entry.timestamp ?? 0, latest.mtime);
51397
+ return {
51398
+ ...entry,
51399
+ sessionId: latest.id,
51400
+ timestamp: updatedTimestamp
51401
+ };
51402
+ }));
51403
+ return updated.sort((a2, b) => (b.timestamp ?? 0) - (a2.timestamp ?? 0));
51404
+ }
51405
+
51406
+ // src/config/tools.ts
51407
+ init_builtin_coding_agents();
51408
+ import { homedir as homedir8 } from "os";
51409
+ import path20 from "path";
51410
+ import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, rename as rename2 } from "fs/promises";
50108
51411
 
50109
51412
  // src/config/profiles.ts
50110
51413
  import {
50111
51414
  mkdir as mkdir2,
50112
51415
  open,
50113
- readFile as readFile2,
51416
+ readFile as readFile3,
50114
51417
  rename,
50115
- stat,
51418
+ stat as stat2,
50116
51419
  unlink,
50117
51420
  writeFile as writeFile2
50118
51421
  } from "fs/promises";
50119
- import path14 from "path";
51422
+ import path19 from "path";
50120
51423
 
50121
51424
  // node_modules/yaml/dist/index.js
50122
51425
  var composer = require_composer();
@@ -50165,7 +51468,7 @@ var $visit = visit.visit;
50165
51468
  var $visitAsync = visit.visitAsync;
50166
51469
 
50167
51470
  // src/config/profiles.ts
50168
- import { homedir as homedir3 } from "os";
51471
+ import { homedir as homedir7 } from "os";
50169
51472
 
50170
51473
  // src/types/profiles.ts
50171
51474
  var DEFAULT_PROFILES_CONFIG = Object.freeze({
@@ -50180,11 +51483,11 @@ function isValidProfileName(name) {
50180
51483
 
50181
51484
  // src/config/profiles.ts
50182
51485
  function getConfigDir() {
50183
- const worktreeHome = process.env.GWT_HOME && process.env.GWT_HOME.trim().length > 0 ? process.env.GWT_HOME : homedir3();
50184
- return path14.join(worktreeHome, ".gwt");
51486
+ const worktreeHome = process.env.GWT_HOME && process.env.GWT_HOME.trim().length > 0 ? process.env.GWT_HOME : homedir7();
51487
+ return path19.join(worktreeHome, ".gwt");
50185
51488
  }
50186
51489
  function getProfilesConfigPath() {
50187
- return path14.join(getConfigDir(), "profiles.yaml");
51490
+ return path19.join(getConfigDir(), "profiles.yaml");
50188
51491
  }
50189
51492
  var LOCK_RETRY_INTERVAL_MS = 50;
50190
51493
  var LOCK_TIMEOUT_MS = 3000;
@@ -50194,7 +51497,7 @@ function getProfilesLockPath() {
50194
51497
  }
50195
51498
  async function withProfilesLock(fn) {
50196
51499
  const lockPath = getProfilesLockPath();
50197
- await mkdir2(path14.dirname(lockPath), { recursive: true });
51500
+ await mkdir2(path19.dirname(lockPath), { recursive: true });
50198
51501
  const startedAt = Date.now();
50199
51502
  while (true) {
50200
51503
  try {
@@ -50213,7 +51516,7 @@ async function withProfilesLock(fn) {
50213
51516
  throw error;
50214
51517
  }
50215
51518
  try {
50216
- const lockStat = await stat(lockPath);
51519
+ const lockStat = await stat2(lockPath);
50217
51520
  const isStale = Date.now() - lockStat.mtimeMs > LOCK_STALE_MS;
50218
51521
  if (isStale) {
50219
51522
  await unlink(lockPath).catch(() => {});
@@ -50243,7 +51546,7 @@ var PROFILES_CONFIG_PATH = getProfilesConfigPath();
50243
51546
  async function loadProfiles() {
50244
51547
  try {
50245
51548
  const configPath = getProfilesConfigPath();
50246
- const content = await readFile2(configPath, "utf-8");
51549
+ const content = await readFile3(configPath, "utf-8");
50247
51550
  const config = $parse(content);
50248
51551
  if (!config.version || typeof config.version !== "string") {
50249
51552
  throw new Error("version field is required and must be a string");
@@ -50328,9 +51631,9 @@ async function deleteProfile(name) {
50328
51631
 
50329
51632
  // src/config/tools.ts
50330
51633
  var logger3 = createLogger({ category: "config" });
50331
- var WORKTREE_HOME = process.env.GWT_HOME && process.env.GWT_HOME.trim().length > 0 ? process.env.GWT_HOME : homedir4();
50332
- var CONFIG_DIR = path15.join(WORKTREE_HOME, ".gwt");
50333
- var TOOLS_CONFIG_PATH = path15.join(CONFIG_DIR, "tools.json");
51634
+ var WORKTREE_HOME = process.env.GWT_HOME && process.env.GWT_HOME.trim().length > 0 ? process.env.GWT_HOME : homedir8();
51635
+ var CONFIG_DIR = path20.join(WORKTREE_HOME, ".gwt");
51636
+ var TOOLS_CONFIG_PATH = path20.join(CONFIG_DIR, "tools.json");
50334
51637
  var TEMP_CONFIG_PATH = `${TOOLS_CONFIG_PATH}.tmp`;
50335
51638
  var DEFAULT_CONFIG2 = {
50336
51639
  version: "1.0.0",
@@ -50339,7 +51642,7 @@ var DEFAULT_CONFIG2 = {
50339
51642
  };
50340
51643
  async function loadCodingAgentsConfig() {
50341
51644
  try {
50342
- const content = await readFile3(TOOLS_CONFIG_PATH, "utf-8");
51645
+ const content = await readFile4(TOOLS_CONFIG_PATH, "utf-8");
50343
51646
  const config = JSON.parse(content);
50344
51647
  const legacyConfig = config;
50345
51648
  if (!config.customCodingAgents && legacyConfig.customTools) {
@@ -50423,7 +51726,7 @@ function validateCodingAgent(agent) {
50423
51726
  throw new Error(`Invalid type: "${a2.type}"
50424
51727
  ` + `Type must be one of: ${validTypes.join(", ")}`);
50425
51728
  }
50426
- if (a2.type === "path" && !path15.isAbsolute(a2.command)) {
51729
+ if (a2.type === "path" && !path20.isAbsolute(a2.command)) {
50427
51730
  throw new Error(`For type="path", command must be an absolute path: "${a2.command}"`);
50428
51731
  }
50429
51732
  if (!a2.modeArgs.normal && !a2.modeArgs.continue && !a2.modeArgs.resume) {
@@ -50450,14 +51753,45 @@ async function getAllCodingAgents() {
50450
51753
 
50451
51754
  // src/logging/reader.ts
50452
51755
  import fs4 from "fs/promises";
50453
- import path16 from "path";
51756
+ import path21 from "path";
50454
51757
  import os4 from "os";
51758
+ var LOG_FILENAME_PATTERN = /^\d{4}-\d{2}-\d{2}\.jsonl$/;
50455
51759
  function resolveLogDir(cwd = process.cwd()) {
50456
- const cwdBase = path16.basename(cwd) || "workspace";
50457
- return path16.join(os4.homedir(), ".gwt", "logs", cwdBase);
51760
+ const cwdBase = path21.basename(cwd) || "workspace";
51761
+ return path21.join(os4.homedir(), ".gwt", "logs", cwdBase);
50458
51762
  }
50459
- function buildLogFilePath(logDir, date) {
50460
- return path16.join(logDir, `${date}.jsonl`);
51763
+ function resolveLogTarget(branch, workingDirectory = process.cwd()) {
51764
+ if (!branch) {
51765
+ return {
51766
+ logDir: resolveLogDir(workingDirectory),
51767
+ sourcePath: workingDirectory,
51768
+ reason: "working-directory"
51769
+ };
51770
+ }
51771
+ const worktreePath = branch.worktree?.path;
51772
+ if (worktreePath) {
51773
+ const accessible = branch.worktree?.isAccessible !== false;
51774
+ if (accessible) {
51775
+ return {
51776
+ logDir: resolveLogDir(worktreePath),
51777
+ sourcePath: worktreePath,
51778
+ reason: "worktree"
51779
+ };
51780
+ }
51781
+ return {
51782
+ logDir: null,
51783
+ sourcePath: worktreePath,
51784
+ reason: "worktree-inaccessible"
51785
+ };
51786
+ }
51787
+ if (branch.isCurrent) {
51788
+ return {
51789
+ logDir: resolveLogDir(workingDirectory),
51790
+ sourcePath: workingDirectory,
51791
+ reason: "current-working-directory"
51792
+ };
51793
+ }
51794
+ return { logDir: null, sourcePath: null, reason: "no-worktree" };
50461
51795
  }
50462
51796
  function getTodayLogDate() {
50463
51797
  return formatDate(new Date);
@@ -50475,6 +51809,76 @@ async function readLogFileLines(filePath) {
50475
51809
  throw error;
50476
51810
  }
50477
51811
  }
51812
+ async function listLogFiles(logDir) {
51813
+ try {
51814
+ const entries = await fs4.readdir(logDir, { withFileTypes: true });
51815
+ const files = [];
51816
+ for (const entry of entries) {
51817
+ if (!entry.isFile())
51818
+ continue;
51819
+ if (!LOG_FILENAME_PATTERN.test(entry.name))
51820
+ continue;
51821
+ const date = entry.name.replace(/\.jsonl$/, "");
51822
+ const fullPath = path21.join(logDir, entry.name);
51823
+ try {
51824
+ const stat3 = await fs4.stat(fullPath);
51825
+ files.push({ date, path: fullPath, mtimeMs: stat3.mtimeMs });
51826
+ } catch {}
51827
+ }
51828
+ return files.sort((a2, b) => b.date.localeCompare(a2.date));
51829
+ } catch (error) {
51830
+ const err = error;
51831
+ if (err.code === "ENOENT") {
51832
+ return [];
51833
+ }
51834
+ throw error;
51835
+ }
51836
+ }
51837
+ async function clearLogFiles(logDir) {
51838
+ const files = await listLogFiles(logDir);
51839
+ let cleared = 0;
51840
+ for (const file of files) {
51841
+ try {
51842
+ await fs4.truncate(file.path, 0);
51843
+ cleared += 1;
51844
+ } catch (error) {
51845
+ const err = error;
51846
+ if (err.code !== "ENOENT") {
51847
+ throw error;
51848
+ }
51849
+ }
51850
+ }
51851
+ return cleared;
51852
+ }
51853
+ async function readLogLinesForDate(logDir, preferredDate) {
51854
+ const files = await listLogFiles(logDir);
51855
+ if (files.length === 0) {
51856
+ return null;
51857
+ }
51858
+ const ordered = [];
51859
+ const preferred = files.find((file) => file.date === preferredDate);
51860
+ if (preferred) {
51861
+ ordered.push(preferred);
51862
+ }
51863
+ for (const file of files) {
51864
+ if (preferred && file.date === preferred.date) {
51865
+ continue;
51866
+ }
51867
+ ordered.push(file);
51868
+ }
51869
+ for (const file of ordered) {
51870
+ const lines = await readLogFileLines(file.path);
51871
+ if (lines.length > 0) {
51872
+ return { date: file.date, lines };
51873
+ }
51874
+ }
51875
+ const fallback = files[0];
51876
+ if (!fallback) {
51877
+ return { date: preferredDate, lines: [] };
51878
+ }
51879
+ const fallbackDate = preferred?.date ?? fallback.date;
51880
+ return { date: fallbackDate, lines: [] };
51881
+ }
50478
51882
 
50479
51883
  // src/logging/formatter.ts
50480
51884
  var LEVEL_LABELS = {
@@ -50485,15 +51889,29 @@ var LEVEL_LABELS = {
50485
51889
  50: "ERROR",
50486
51890
  60: "FATAL"
50487
51891
  };
51892
+ var LOCAL_TIME_FORMATTER = new Intl.DateTimeFormat(undefined, {
51893
+ hour: "2-digit",
51894
+ minute: "2-digit",
51895
+ second: "2-digit",
51896
+ hour12: false
51897
+ });
51898
+ var formatLocalTimeParts = (date) => {
51899
+ const parts = LOCAL_TIME_FORMATTER.formatToParts(date);
51900
+ const get = (type) => parts.find((part) => part.type === type)?.value;
51901
+ const hour = get("hour");
51902
+ const minute = get("minute");
51903
+ const second = get("second");
51904
+ if (!hour || !minute || !second) {
51905
+ return LOCAL_TIME_FORMATTER.format(date);
51906
+ }
51907
+ return `${hour}:${minute}:${second}`;
51908
+ };
50488
51909
  var formatTimeLabel = (value) => {
50489
51910
  if (typeof value === "string" || typeof value === "number") {
50490
51911
  const date = new Date(value);
50491
51912
  if (!Number.isNaN(date.getTime())) {
50492
- const hours = String(date.getHours()).padStart(2, "0");
50493
- const minutes = String(date.getMinutes()).padStart(2, "0");
50494
- const seconds = String(date.getSeconds()).padStart(2, "0");
50495
51913
  return {
50496
- label: `${hours}:${minutes}:${seconds}`,
51914
+ label: formatLocalTimeParts(date),
50497
51915
  timestamp: date.getTime()
50498
51916
  };
50499
51917
  }
@@ -50577,17 +51995,17 @@ async function copyToClipboard(text, options = {}) {
50577
51995
  }
50578
51996
 
50579
51997
  // src/utils.ts
50580
- import path17 from "path";
51998
+ import path22 from "path";
50581
51999
  import { fileURLToPath as fileURLToPath4 } from "url";
50582
- import { readFile as readFile4 } from "fs/promises";
52000
+ import { readFile as readFile5 } from "fs/promises";
50583
52001
  function getCurrentDirname() {
50584
- return path17.dirname(fileURLToPath4(import.meta.url));
52002
+ return path22.dirname(fileURLToPath4(import.meta.url));
50585
52003
  }
50586
52004
  async function getPackageVersion() {
50587
52005
  try {
50588
52006
  const currentDir = getCurrentDirname();
50589
- const packageJsonPath = path17.resolve(currentDir, "..", "package.json");
50590
- const packageJsonContent = await readFile4(packageJsonPath, "utf-8");
52007
+ const packageJsonPath = path22.resolve(currentDir, "..", "package.json");
52008
+ const packageJsonContent = await readFile5(packageJsonPath, "utf-8");
50591
52009
  const packageJson = JSON.parse(packageJsonContent);
50592
52010
  return packageJson.version || null;
50593
52011
  } catch {
@@ -50597,9 +52015,11 @@ async function getPackageVersion() {
50597
52015
 
50598
52016
  // src/cli/ui/App.solid.tsx
50599
52017
  init_constants();
52018
+ init_versionFetcher();
50600
52019
  var logger4 = createLogger({
50601
52020
  category: "app"
50602
52021
  });
52022
+ var UNSAFE_SELECTION_MESSAGE = "Unsafe branch selected. Select anyway?";
50603
52023
  var DEFAULT_SCREEN = "branch-list";
50604
52024
  var buildStats = (branches) => calculateStatistics(branches);
50605
52025
  var applyCleanupStatus = (items, statusByBranch) => items.map((branch) => {
@@ -50630,6 +52050,23 @@ var applyCleanupStatus = (items, statusByBranch) => items.map((branch) => {
50630
52050
  worktree
50631
52051
  } : base2;
50632
52052
  });
52053
+ var buildCleanupSafetyPending = (items) => {
52054
+ const pending = new Set;
52055
+ for (const branch of items) {
52056
+ if (branch.type === "remote") {
52057
+ continue;
52058
+ }
52059
+ if (branch.worktree) {
52060
+ pending.add(branch.name);
52061
+ continue;
52062
+ }
52063
+ if (isProtectedBranchName(branch.name)) {
52064
+ continue;
52065
+ }
52066
+ pending.add(branch.name);
52067
+ }
52068
+ return pending;
52069
+ };
50633
52070
  var toLocalBranchName = (name) => {
50634
52071
  const segments = name.split("/");
50635
52072
  if (segments.length <= 1) {
@@ -50645,6 +52082,7 @@ var toSelectedBranchState = (branch) => {
50645
52082
  displayName: branch.name,
50646
52083
  branchType: branch.type,
50647
52084
  branchCategory: branch.branchType,
52085
+ worktreePath: branch.worktree?.path ?? null,
50648
52086
  ...isRemote ? {
50649
52087
  remoteBranch: branch.name
50650
52088
  } : {}
@@ -50652,6 +52090,7 @@ var toSelectedBranchState = (branch) => {
50652
52090
  };
50653
52091
  function AppSolid(props) {
50654
52092
  const renderer = useRenderer();
52093
+ const terminal = useTerminalSize();
50655
52094
  let hasExited = false;
50656
52095
  const exitApp = (result) => {
50657
52096
  if (hasExited)
@@ -50668,6 +52107,7 @@ function AppSolid(props) {
50668
52107
  const [stats, setStats] = createSignal(props.stats ?? buildStats(props.branches ?? []));
50669
52108
  const [loading, setLoading] = createSignal(!props.branches);
50670
52109
  const [error, setError] = createSignal(null);
52110
+ const [branchCursorPosition, setBranchCursorPosition] = createSignal(0);
50671
52111
  const [toolItems, setToolItems] = createSignal([]);
50672
52112
  const [toolError, setToolError] = createSignal(null);
50673
52113
  const [version, setVersion] = createSignal(props.version ?? null);
@@ -50676,11 +52116,15 @@ function AppSolid(props) {
50676
52116
  const [selectedTool, setSelectedTool] = createSignal(null);
50677
52117
  const [selectedMode, setSelectedMode] = createSignal("normal");
50678
52118
  const [selectedBranches, setSelectedBranches] = createSignal([]);
52119
+ const [unsafeSelectionConfirmVisible, setUnsafeSelectionConfirmVisible] = createSignal(false);
52120
+ const [unsafeConfirmInputLocked, setUnsafeConfirmInputLocked] = createSignal(false);
52121
+ const [unsafeSelectionTarget, setUnsafeSelectionTarget] = createSignal(null);
50679
52122
  const [branchFooterMessage, setBranchFooterMessage] = createSignal(null);
50680
52123
  const [branchInputLocked, setBranchInputLocked] = createSignal(false);
50681
52124
  const [cleanupIndicators, setCleanupIndicators] = createSignal({});
50682
52125
  const [cleanupStatusByBranch, setCleanupStatusByBranch] = createSignal(new Map);
50683
52126
  const [cleanupSafetyLoading, setCleanupSafetyLoading] = createSignal(false);
52127
+ const [cleanupSafetyPending, setCleanupSafetyPending] = createSignal(new Set);
50684
52128
  const [isNewBranch, setIsNewBranch] = createSignal(false);
50685
52129
  const [newBranchBaseRef, setNewBranchBaseRef] = createSignal(null);
50686
52130
  const [creationSource, setCreationSource] = createSignal(null);
@@ -50689,20 +52133,59 @@ function AppSolid(props) {
50689
52133
  });
50690
52134
  const [suppressCreateKey, setSuppressCreateKey] = createSignal(null);
50691
52135
  const [defaultBaseBranch, setDefaultBaseBranch] = createSignal("main");
52136
+ const suppressBranchInputOnce = () => {
52137
+ setUnsafeConfirmInputLocked(true);
52138
+ queueMicrotask(() => {
52139
+ setUnsafeConfirmInputLocked(false);
52140
+ });
52141
+ };
52142
+ const unsafeConfirmBoxWidth = createMemo(() => {
52143
+ const columns = terminal().columns || 80;
52144
+ return Math.max(1, Math.floor(columns * 0.6));
52145
+ });
52146
+ const unsafeConfirmContentWidth = createMemo(() => Math.max(0, unsafeConfirmBoxWidth() - 4));
50692
52147
  const [sessionHistory, setSessionHistory] = createSignal([]);
52148
+ const [quickStartHistory, setQuickStartHistory] = createSignal([]);
50693
52149
  const historyForBranch = createMemo(() => {
50694
52150
  const history = sessionHistory();
50695
52151
  const branch = selectedBranch();
50696
52152
  if (!branch)
50697
52153
  return [];
50698
- return history.filter((entry) => entry.branch === branch.name).sort((a2, b) => (b.timestamp ?? 0) - (a2.timestamp ?? 0));
52154
+ return findLatestBranchSessionsByTool(history, branch.name, branch.worktreePath ?? null);
52155
+ });
52156
+ createEffect(() => {
52157
+ setQuickStartHistory(historyForBranch());
52158
+ });
52159
+ createEffect(() => {
52160
+ const branch = selectedBranch();
52161
+ const baseHistory = historyForBranch();
52162
+ if (!wizardVisible() || !branch || baseHistory.length === 0) {
52163
+ return;
52164
+ }
52165
+ const worktreePath = branch.worktreePath ?? null;
52166
+ if (!worktreePath) {
52167
+ return;
52168
+ }
52169
+ const branchName = branch.name;
52170
+ (async () => {
52171
+ const refreshed = await refreshQuickStartEntries(baseHistory, {
52172
+ branch: branchName,
52173
+ worktreePath
52174
+ });
52175
+ if (selectedBranch()?.name !== branchName) {
52176
+ return;
52177
+ }
52178
+ setQuickStartHistory(refreshed);
52179
+ })();
50699
52180
  });
50700
52181
  const [logEntries, setLogEntries] = createSignal([]);
50701
52182
  const [logLoading, setLogLoading] = createSignal(false);
50702
52183
  const [logError2, setLogError] = createSignal(null);
50703
52184
  const [logSelectedEntry, setLogSelectedEntry] = createSignal(null);
50704
- const [logSelectedDate, _setLogSelectedDate] = createSignal(getTodayLogDate());
52185
+ const [logSelectedDate, setLogSelectedDate] = createSignal(getTodayLogDate());
50705
52186
  const [logNotification, setLogNotification] = createSignal(null);
52187
+ const [logTailEnabled, setLogTailEnabled] = createSignal(false);
52188
+ const [logTargetBranch, setLogTargetBranch] = createSignal(null);
50706
52189
  const [profileItems, setProfileItems] = createSignal([]);
50707
52190
  const [activeProfile, setActiveProfileName] = createSignal(null);
50708
52191
  const [profileError, setProfileError] = createSignal(null);
@@ -50717,7 +52200,21 @@ function AppSolid(props) {
50717
52200
  const [profileInputSuppressKey, setProfileInputSuppressKey] = createSignal(null);
50718
52201
  const [profileEnvKey, setProfileEnvKey] = createSignal(null);
50719
52202
  const [profileConfirmMode, setProfileConfirmMode] = createSignal("delete-profile");
50720
- const logDir = createMemo(() => resolveLogDir(workingDirectory()));
52203
+ const logTarget = createMemo(() => resolveLogTarget(logTargetBranch(), workingDirectory()));
52204
+ const logBranchLabel = createMemo(() => logTargetBranch()?.label ?? null);
52205
+ const logSourceLabel = createMemo(() => {
52206
+ const target = logTarget();
52207
+ if (!target.sourcePath) {
52208
+ return "(none)";
52209
+ }
52210
+ if (target.reason === "current-working-directory" || target.reason === "working-directory") {
52211
+ return `${target.sourcePath} (cwd)`;
52212
+ }
52213
+ if (target.reason === "worktree-inaccessible") {
52214
+ return `${target.sourcePath} (inaccessible)`;
52215
+ }
52216
+ return target.sourcePath;
52217
+ });
50721
52218
  const selectedProfileConfig = createMemo(() => {
50722
52219
  const name = selectedProfileName();
50723
52220
  const config = profilesConfig();
@@ -50746,15 +52243,42 @@ function AppSolid(props) {
50746
52243
  let cleanupSafetyRequestId = 0;
50747
52244
  const refreshCleanupSafety = async () => {
50748
52245
  const requestId = ++cleanupSafetyRequestId;
50749
- setCleanupSafetyLoading(true);
52246
+ const pendingBranches = buildCleanupSafetyPending(branchItems());
52247
+ setCleanupSafetyPending(pendingBranches);
52248
+ setCleanupSafetyLoading(pendingBranches.size > 0);
52249
+ const statusByBranch = new Map;
52250
+ const applyProgress = (status) => {
52251
+ if (requestId !== cleanupSafetyRequestId) {
52252
+ return;
52253
+ }
52254
+ statusByBranch.set(status.branch, status);
52255
+ batch(() => {
52256
+ setCleanupStatusByBranch(new Map(statusByBranch));
52257
+ setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
52258
+ setCleanupSafetyPending((prev) => {
52259
+ if (!prev.has(status.branch)) {
52260
+ return prev;
52261
+ }
52262
+ const next = new Set(prev);
52263
+ next.delete(status.branch);
52264
+ return next;
52265
+ });
52266
+ });
52267
+ };
50750
52268
  try {
50751
- const cleanupStatuses = await getCleanupStatus();
52269
+ const cleanupStatuses = await getCleanupStatus({
52270
+ onProgress: applyProgress
52271
+ });
50752
52272
  if (requestId !== cleanupSafetyRequestId) {
50753
52273
  return;
50754
52274
  }
50755
- const statusByBranch = new Map(cleanupStatuses.map((status) => [status.branch, status]));
50756
- setCleanupStatusByBranch(statusByBranch);
50757
- setBranchItems((items) => applyCleanupStatus(items, statusByBranch));
52275
+ if (cleanupStatuses.length > statusByBranch.size) {
52276
+ cleanupStatuses.forEach((status) => {
52277
+ if (!statusByBranch.has(status.branch)) {
52278
+ applyProgress(status);
52279
+ }
52280
+ });
52281
+ }
50758
52282
  } catch (err) {
50759
52283
  if (requestId !== cleanupSafetyRequestId) {
50760
52284
  return;
@@ -50763,15 +52287,19 @@ function AppSolid(props) {
50763
52287
  err
50764
52288
  }, "Failed to refresh cleanup safety indicators");
50765
52289
  const empty = new Map;
50766
- setCleanupStatusByBranch(empty);
50767
- setBranchItems((items) => applyCleanupStatus(items, empty));
52290
+ batch(() => {
52291
+ setCleanupStatusByBranch(empty);
52292
+ setBranchItems((items) => applyCleanupStatus(items, empty));
52293
+ });
50768
52294
  } finally {
50769
52295
  if (requestId === cleanupSafetyRequestId) {
50770
52296
  setCleanupSafetyLoading(false);
52297
+ setCleanupSafetyPending(new Set);
50771
52298
  }
50772
52299
  }
50773
52300
  };
50774
52301
  let logNotificationTimer = null;
52302
+ let logTailTimer = null;
50775
52303
  let branchFooterTimer = null;
50776
52304
  const BRANCH_LOAD_TIMEOUT_MS = 3000;
50777
52305
  const BRANCH_FULL_LOAD_TIMEOUT_MS = 8000;
@@ -50838,9 +52366,20 @@ function AppSolid(props) {
50838
52366
  setLogLoading(true);
50839
52367
  setLogError(null);
50840
52368
  try {
50841
- const filePath = buildLogFilePath(logDir(), targetDate);
50842
- const lines = await readLogFileLines(filePath);
50843
- const parsed = parseLogLines(lines, {
52369
+ const target = logTarget();
52370
+ if (!target.logDir) {
52371
+ setLogEntries([]);
52372
+ setLogSelectedDate(targetDate);
52373
+ return;
52374
+ }
52375
+ const result = await readLogLinesForDate(target.logDir, targetDate);
52376
+ if (!result) {
52377
+ setLogEntries([]);
52378
+ setLogSelectedDate(targetDate);
52379
+ return;
52380
+ }
52381
+ setLogSelectedDate(result.date);
52382
+ const parsed = parseLogLines(result.lines, {
50844
52383
  limit: 100
50845
52384
  });
50846
52385
  setLogEntries(parsed);
@@ -50851,10 +52390,39 @@ function AppSolid(props) {
50851
52390
  setLogLoading(false);
50852
52391
  }
50853
52392
  };
52393
+ const clearLogTailTimer = () => {
52394
+ if (logTailTimer) {
52395
+ clearInterval(logTailTimer);
52396
+ logTailTimer = null;
52397
+ }
52398
+ };
52399
+ const toggleLogTail = () => {
52400
+ setLogTailEnabled((prev) => !prev);
52401
+ };
52402
+ const resetLogFiles = async () => {
52403
+ const target = logTarget();
52404
+ if (!target.logDir) {
52405
+ showLogNotification("No logs available.", "error");
52406
+ return;
52407
+ }
52408
+ try {
52409
+ const cleared = await clearLogFiles(target.logDir);
52410
+ if (cleared === 0) {
52411
+ showLogNotification("No logs to reset.", "error");
52412
+ } else {
52413
+ showLogNotification("Logs cleared.", "success");
52414
+ }
52415
+ await loadLogEntries(logSelectedDate());
52416
+ } catch (err) {
52417
+ logger4.warn({
52418
+ err
52419
+ }, "Failed to clear log files");
52420
+ showLogNotification("Failed to reset logs.", "error");
52421
+ }
52422
+ };
50854
52423
  const refreshBranches = async () => {
50855
52424
  setLoading(true);
50856
52425
  setError(null);
50857
- refreshCleanupSafety();
50858
52426
  try {
50859
52427
  const repoRoot = await getRepositoryRoot();
50860
52428
  const worktreesPromise = listAdditionalWorktrees();
@@ -50873,6 +52441,7 @@ function AppSolid(props) {
50873
52441
  const initialItems = applyCleanupStatus(initial.items, cleanupStatusByBranch());
50874
52442
  setBranchItems(initialItems);
50875
52443
  setStats(buildStats(initialItems));
52444
+ refreshCleanupSafety();
50876
52445
  (async () => {
50877
52446
  const [branches, latestWorktrees] = await Promise.all([withTimeout(getAllBranches(repoRoot), BRANCH_FULL_LOAD_TIMEOUT_MS).catch(() => localBranches), withTimeout(listAdditionalWorktrees(), BRANCH_FULL_LOAD_TIMEOUT_MS).catch(() => worktrees)]);
50878
52447
  const full = buildBranchList(branches, latestWorktrees, lastToolUsageMap);
@@ -50954,15 +52523,31 @@ function AppSolid(props) {
50954
52523
  setToolError(err instanceof Error ? err : new Error(String(err)));
50955
52524
  });
50956
52525
  });
52526
+ onMount(() => {
52527
+ const bunxAgentIds = getBunxAgentIds();
52528
+ prefetchAgentVersions(bunxAgentIds).catch(() => {});
52529
+ });
50957
52530
  createEffect(() => {
50958
52531
  if (currentScreen() === "log-list") {
52532
+ logTarget();
50959
52533
  loadLogEntries(logSelectedDate());
50960
52534
  }
50961
52535
  });
52536
+ createEffect(() => {
52537
+ if (currentScreen() !== "log-list" || !logTailEnabled()) {
52538
+ clearLogTailTimer();
52539
+ return;
52540
+ }
52541
+ clearLogTailTimer();
52542
+ logTailTimer = setInterval(() => {
52543
+ loadLogEntries(logSelectedDate());
52544
+ }, 1500);
52545
+ });
50962
52546
  onCleanup(() => {
50963
52547
  if (logNotificationTimer) {
50964
52548
  clearTimeout(logNotificationTimer);
50965
52549
  }
52550
+ clearLogTailTimer();
50966
52551
  if (branchFooterTimer) {
50967
52552
  clearTimeout(branchFooterTimer);
50968
52553
  }
@@ -51058,6 +52643,10 @@ function AppSolid(props) {
51058
52643
  });
51059
52644
  };
51060
52645
  const handleWizardResume = (entry) => {
52646
+ if (!entry.sessionId) {
52647
+ handleWizardStartNew(entry);
52648
+ return;
52649
+ }
51061
52650
  setWizardVisible(false);
51062
52651
  const branch = selectedBranch();
51063
52652
  if (!branch) {
@@ -51166,24 +52755,10 @@ function AppSolid(props) {
51166
52755
  skipCounts.remote += 1;
51167
52756
  continue;
51168
52757
  }
51169
- if (isProtectedBranchName(branch.name)) {
51170
- skipCounts.protected += 1;
51171
- continue;
51172
- }
51173
52758
  if (branch.isCurrent) {
51174
52759
  skipCounts.current += 1;
51175
52760
  continue;
51176
52761
  }
51177
- const hasUncommitted = branch.worktree?.hasUncommittedChanges === true;
51178
- const hasUnpushed = branch.hasUnpushedCommits === true;
51179
- if (hasUncommitted || hasUnpushed) {
51180
- skipCounts.unsafe += 1;
51181
- continue;
51182
- }
51183
- if (branch.safeToCleanup !== true) {
51184
- skipCounts.unsafe += 1;
51185
- continue;
51186
- }
51187
52762
  const worktreePath = branch.worktree?.path ?? null;
51188
52763
  const cleanupType = worktreePath ? "worktree-and-branch" : "branch-only";
51189
52764
  const baseTask = {
@@ -51274,7 +52849,7 @@ function AppSolid(props) {
51274
52849
  return;
51275
52850
  }
51276
52851
  const selectionSet = new Set(selection);
51277
- const targets = branchItems().filter((branch) => selectionSet.has(branch.name) && branch.worktreeStatus === "inaccessible").map((branch) => branch.name);
52852
+ const targets = branchItems().filter((branch) => selectionSet.has(branch.name) && branch.type !== "remote" && branch.worktreeStatus !== undefined).map((branch) => branch.name);
51278
52853
  if (targets.length === 0) {
51279
52854
  showBranchFooterMessage("No worktrees to repair.", "yellow");
51280
52855
  return;
@@ -51485,15 +53060,41 @@ function AppSolid(props) {
51485
53060
  }
51486
53061
  };
51487
53062
  const toggleSelectedBranch = (branchName) => {
51488
- setSelectedBranches((prev) => {
51489
- const next = new Set(prev);
51490
- if (next.has(branchName)) {
51491
- next.delete(branchName);
51492
- } else {
51493
- next.add(branchName);
51494
- }
51495
- return Array.from(next);
51496
- });
53063
+ if (unsafeSelectionConfirmVisible()) {
53064
+ return;
53065
+ }
53066
+ const currentSelection = new Set(selectedBranches());
53067
+ if (currentSelection.has(branchName)) {
53068
+ currentSelection.delete(branchName);
53069
+ setSelectedBranches(Array.from(currentSelection));
53070
+ return;
53071
+ }
53072
+ const branch = branchItems().find((item) => item.name === branchName);
53073
+ const pending = cleanupSafetyPending();
53074
+ const hasSafetyPending = pending.has(branchName);
53075
+ const hasUncommitted = branch?.worktree?.hasUncommittedChanges === true;
53076
+ const hasUnpushed = branch?.hasUnpushedCommits === true;
53077
+ const isUnmerged = branch?.isUnmerged === true;
53078
+ const safeToCleanup = branch?.safeToCleanup === true;
53079
+ const isRemoteBranch = branch?.type === "remote";
53080
+ const isUnsafe = Boolean(branch) && !isRemoteBranch && !hasSafetyPending && (hasUncommitted || hasUnpushed || isUnmerged || !safeToCleanup);
53081
+ if (branch && isUnsafe) {
53082
+ setUnsafeSelectionTarget(branch.name);
53083
+ setUnsafeSelectionConfirmVisible(true);
53084
+ return;
53085
+ }
53086
+ currentSelection.add(branchName);
53087
+ setSelectedBranches(Array.from(currentSelection));
53088
+ };
53089
+ const confirmUnsafeSelection = (confirmed) => {
53090
+ suppressBranchInputOnce();
53091
+ const target = unsafeSelectionTarget();
53092
+ setUnsafeSelectionConfirmVisible(false);
53093
+ setUnsafeSelectionTarget(null);
53094
+ if (!confirmed || !target) {
53095
+ return;
53096
+ }
53097
+ setSelectedBranches((prev) => prev.includes(target) ? prev : [...prev, target]);
51497
53098
  };
51498
53099
  const handleToolSelect = (item) => {
51499
53100
  setSelectedTool(item.value);
@@ -51544,8 +53145,9 @@ function AppSolid(props) {
51544
53145
  const cleanupUI = {
51545
53146
  indicators: cleanupIndicators(),
51546
53147
  footerMessage: branchFooterMessage(),
51547
- inputLocked: branchInputLocked(),
51548
- safetyLoading: cleanupSafetyLoading()
53148
+ inputLocked: branchInputLocked() || unsafeConfirmInputLocked(),
53149
+ safetyLoading: cleanupSafetyLoading(),
53150
+ safetyPendingBranches: cleanupSafetyPending()
51549
53151
  };
51550
53152
  return createComponent2(BranchListScreen, {
51551
53153
  get branches() {
@@ -51580,7 +53182,13 @@ function AppSolid(props) {
51580
53182
  get activeProfile() {
51581
53183
  return activeProfile();
51582
53184
  },
51583
- onOpenLogs: () => navigateTo("log-list"),
53185
+ onOpenLogs: (branch) => {
53186
+ setLogTargetBranch(branch);
53187
+ setLogSelectedEntry(null);
53188
+ setLogSelectedDate(getTodayLogDate());
53189
+ setLogTailEnabled(false);
53190
+ navigateTo("log-list");
53191
+ },
51584
53192
  onOpenProfiles: () => navigateTo("profile"),
51585
53193
  get selectedBranches() {
51586
53194
  return selectedBranches();
@@ -51593,7 +53201,14 @@ function AppSolid(props) {
51593
53201
  },
51594
53202
  get wizardVisible() {
51595
53203
  return wizardVisible();
51596
- }
53204
+ },
53205
+ get confirmVisible() {
53206
+ return unsafeSelectionConfirmVisible();
53207
+ },
53208
+ get cursorPosition() {
53209
+ return branchCursorPosition();
53210
+ },
53211
+ onCursorPositionChange: setBranchCursorPosition
51597
53212
  });
51598
53213
  }
51599
53214
  if (screen === "tool-select") {
@@ -51679,6 +53294,9 @@ function AppSolid(props) {
51679
53294
  showLogNotification("Failed to copy to clipboard.", "error");
51680
53295
  }
51681
53296
  },
53297
+ onReload: () => void loadLogEntries(logSelectedDate()),
53298
+ onToggleTail: toggleLogTail,
53299
+ onReset: () => void resetLogFiles(),
51682
53300
  get notification() {
51683
53301
  return logNotification();
51684
53302
  },
@@ -51688,6 +53306,15 @@ function AppSolid(props) {
51688
53306
  get selectedDate() {
51689
53307
  return logSelectedDate();
51690
53308
  },
53309
+ get branchLabel() {
53310
+ return logBranchLabel();
53311
+ },
53312
+ get sourceLabel() {
53313
+ return logSourceLabel();
53314
+ },
53315
+ get tailing() {
53316
+ return logTailEnabled();
53317
+ },
51691
53318
  get helpVisible() {
51692
53319
  return helpVisible();
51693
53320
  }
@@ -51932,7 +53559,33 @@ function AppSolid(props) {
51932
53559
  }
51933
53560
  });
51934
53561
  };
51935
- return [memo2(renderCurrentScreen), createComponent2(HelpOverlay, {
53562
+ return [memo2(renderCurrentScreen), memo2(() => memo2(() => !!unsafeSelectionConfirmVisible())() && (() => {
53563
+ var _el$ = createElement("box");
53564
+ setProp(_el$, "position", "absolute");
53565
+ setProp(_el$, "top", "30%");
53566
+ setProp(_el$, "left", "20%");
53567
+ setProp(_el$, "zIndex", 110);
53568
+ setProp(_el$, "border", true);
53569
+ setProp(_el$, "borderStyle", "single");
53570
+ setProp(_el$, "borderColor", "yellow");
53571
+ setProp(_el$, "backgroundColor", "black");
53572
+ setProp(_el$, "padding", 1);
53573
+ insert(_el$, createComponent2(ConfirmScreen, {
53574
+ message: UNSAFE_SELECTION_MESSAGE,
53575
+ onConfirm: confirmUnsafeSelection,
53576
+ yesLabel: "OK",
53577
+ noLabel: "Cancel",
53578
+ defaultNo: true,
53579
+ get helpVisible() {
53580
+ return helpVisible();
53581
+ },
53582
+ get width() {
53583
+ return unsafeConfirmContentWidth();
53584
+ }
53585
+ }));
53586
+ effect((_$p) => setProp(_el$, "width", unsafeConfirmBoxWidth(), _$p));
53587
+ return _el$;
53588
+ })()), createComponent2(HelpOverlay, {
51936
53589
  get visible() {
51937
53590
  return helpVisible();
51938
53591
  },
@@ -51947,7 +53600,7 @@ function AppSolid(props) {
51947
53600
  return selectedBranch()?.name ?? "";
51948
53601
  },
51949
53602
  get history() {
51950
- return historyForBranch();
53603
+ return quickStartHistory();
51951
53604
  },
51952
53605
  onClose: handleWizardClose,
51953
53606
  onComplete: handleWizardComplete,