ralphctl 0.4.2 → 0.4.4

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 (31) hide show
  1. package/README.md +13 -11
  2. package/dist/{add-CIM72NE3.mjs → add-DVPVHENV.mjs} +7 -7
  3. package/dist/{add-GX7P7XTT.mjs → add-YVXM34RP.mjs} +6 -5
  4. package/dist/{chunk-GL7MKLLS.mjs → chunk-ACRMBVEE.mjs} +458 -181
  5. package/dist/{chunk-NUYQK5MN.mjs → chunk-BSB4EDGR.mjs} +2 -2
  6. package/dist/{chunk-YCDUVPRT.mjs → chunk-CBMFRQ4Y.mjs} +5 -73
  7. package/dist/{chunk-3QBEBKMZ.mjs → chunk-FNAAA32W.mjs} +7 -7
  8. package/dist/{chunk-JOQO4HMM.mjs → chunk-GQ2WFKBN.mjs} +11 -11
  9. package/dist/{chunk-TKPTT2UG.mjs → chunk-OFILN7QL.mjs} +798 -1023
  10. package/dist/{chunk-7JLZQICD.mjs → chunk-OGEXYSFS.mjs} +7 -7
  11. package/dist/{chunk-D2YGPLIV.mjs → chunk-PYZEQ2VK.mjs} +214 -9
  12. package/dist/{chunk-57UWLHRH.mjs → chunk-VAZ3LJBI.mjs} +12 -1
  13. package/dist/{chunk-CTP2A436.mjs → chunk-WDMLPXOD.mjs} +11 -4
  14. package/dist/{chunk-FKMKOWLA.mjs → chunk-XN2UIHBY.mjs} +84 -3
  15. package/dist/chunk-ZLWSPLWI.mjs +1117 -0
  16. package/dist/cli.mjs +72 -21
  17. package/dist/create-Z635FQKO.mjs +15 -0
  18. package/dist/{handle-BBAZJ44Y.mjs → handle-23EFF3BE.mjs} +1 -1
  19. package/dist/{mount-ISHZM36X.mjs → mount-VEV3TESX.mjs} +1702 -1202
  20. package/dist/{project-2IE7VWDB.mjs → project-DQHF4ISP.mjs} +3 -3
  21. package/dist/prompts/check-script-discover.md +69 -0
  22. package/dist/prompts/repo-onboard.md +111 -0
  23. package/dist/prompts/sprint-feedback.md +4 -0
  24. package/dist/prompts/task-evaluation.md +44 -2
  25. package/dist/prompts/task-execution.md +5 -0
  26. package/dist/{resolver-EOE5WUMV.mjs → resolver-OVPYVW6Q.mjs} +4 -4
  27. package/dist/{sprint-OGOFEJJH.mjs → sprint-4E26AB5F.mjs} +4 -4
  28. package/dist/start-2WH4BTDB.mjs +19 -0
  29. package/package.json +6 -6
  30. package/dist/create-7WFSCMP4.mjs +0 -15
  31. package/dist/start-76JKJQIH.mjs +0 -17
@@ -3,6 +3,7 @@ import {
3
3
  FIELD_LABEL_WIDTH,
4
4
  InMemorySignalBus,
5
5
  InkPromptAdapter,
6
+ InkSink,
6
7
  PromptHost,
7
8
  checkAiProvider,
8
9
  checkConfigSchemaValidation,
@@ -14,18 +15,18 @@ import {
14
15
  checkGlabInstalled,
15
16
  checkNodeVersion,
16
17
  checkProjectPaths,
18
+ checkRepoOnboarding,
17
19
  cliMetadata,
18
20
  configSetCommand,
19
21
  configShowCommand,
20
22
  createSharedDeps,
21
23
  doctorCommand,
22
24
  exportRequirementsToMarkdown,
23
- getAllSchemaEntries,
24
25
  getNextAction,
25
26
  glyphs,
26
27
  inkColors,
27
28
  loadDashboardData,
28
- parseConfigValue,
29
+ logEventBus,
29
30
  progressLogCommand,
30
31
  progressShowCommand,
31
32
  projectListCommand,
@@ -60,56 +61,53 @@ import {
60
61
  ticketRefineCommand,
61
62
  ticketRemoveCommand,
62
63
  ticketShowCommand,
63
- useCurrentPrompt,
64
- validateConfigValue
65
- } from "./chunk-GL7MKLLS.mjs";
64
+ useCurrentPrompt
65
+ } from "./chunk-ACRMBVEE.mjs";
66
66
  import {
67
67
  PromptCancelledError,
68
- projectAddCommand
69
- } from "./chunk-D2YGPLIV.mjs";
68
+ detectCheckScriptCandidates,
69
+ discoverCheckScriptWithAi,
70
+ getAllSchemaEntries,
71
+ getConfigDefaultValue,
72
+ parseConfigValue,
73
+ projectAddCommand,
74
+ suggestCheckScript,
75
+ validateConfigValue
76
+ } from "./chunk-PYZEQ2VK.mjs";
70
77
  import {
71
78
  sprintCreateCommand
72
- } from "./chunk-3QBEBKMZ.mjs";
79
+ } from "./chunk-FNAAA32W.mjs";
73
80
  import {
74
81
  ticketAddCommand
75
- } from "./chunk-7JLZQICD.mjs";
76
- import {
77
- addProjectRepo,
78
- createProject,
79
- getProject,
80
- getProjectById,
81
- getRepoById,
82
- listProjects,
83
- projectExists,
84
- removeProject,
85
- removeProjectRepo,
86
- resolveRepoPath,
87
- updateProject
88
- } from "./chunk-NUYQK5MN.mjs";
82
+ } from "./chunk-OGEXYSFS.mjs";
89
83
  import {
90
84
  addTask,
91
85
  areAllTasksDone,
92
86
  branchExists,
93
- createExecuteSprintPipeline,
94
87
  createIdeatePipeline,
88
+ createOnboardPipeline,
95
89
  createPlanPipeline,
96
90
  createRefinePipeline,
97
- enterAltScreen,
98
91
  executePipeline,
99
- exitAltScreen,
100
92
  getDefaultBranch,
101
93
  getNextTask,
102
94
  getTask,
103
95
  getTasks,
104
96
  isGhAvailable,
105
97
  listTasks,
106
- registerTuiInstance,
107
98
  removeTask,
108
99
  reorderTask,
109
100
  sprintStartCommand,
110
- updateTaskStatus,
101
+ updateTaskStatus
102
+ } from "./chunk-OFILN7QL.mjs";
103
+ import {
104
+ ProviderAiSessionAdapter,
105
+ SignalParser,
106
+ enterAltScreen,
107
+ exitAltScreen,
108
+ registerTuiInstance,
111
109
  withSuspendedTui
112
- } from "./chunk-TKPTT2UG.mjs";
110
+ } from "./chunk-ZLWSPLWI.mjs";
113
111
  import {
114
112
  addTicket,
115
113
  allRequirementsApproved,
@@ -121,38 +119,51 @@ import {
121
119
  removeTicket,
122
120
  truncate,
123
121
  updateTicket
124
- } from "./chunk-JOQO4HMM.mjs";
122
+ } from "./chunk-GQ2WFKBN.mjs";
125
123
  import "./chunk-CFUVE2BP.mjs";
126
124
  import {
127
125
  getPrompt,
128
126
  getSharedDeps,
129
127
  setSharedDeps
130
128
  } from "./chunk-747KW2RW.mjs";
129
+ import {
130
+ addProjectRepo,
131
+ createProject,
132
+ getProject,
133
+ getProjectById,
134
+ getRepoById,
135
+ listProjects,
136
+ projectExists,
137
+ removeProject,
138
+ removeProjectRepo,
139
+ resolveRepoPath,
140
+ updateProject
141
+ } from "./chunk-BSB4EDGR.mjs";
131
142
  import {
132
143
  closeSprint,
133
144
  createSprint,
134
145
  deleteSprint,
135
- getAiProvider,
136
- getConfig,
137
- getCurrentSprint,
138
146
  getCurrentSprintOrThrow,
139
- getEditor,
140
- getEvaluationIterations,
141
147
  getProgress,
142
148
  getSprint,
143
149
  listSprints,
144
150
  logProgress,
145
151
  resolveSprintId,
146
- saveSprint,
147
- setAiProvider,
148
- setCurrentSprint
149
- } from "./chunk-YCDUVPRT.mjs";
152
+ saveSprint
153
+ } from "./chunk-CBMFRQ4Y.mjs";
150
154
  import {
151
155
  banner,
152
156
  colors,
157
+ getAiProvider,
158
+ getConfig,
159
+ getCurrentSprint,
160
+ getEditor,
161
+ getEvaluationIterations,
153
162
  getRandomQuote,
154
- gradients
155
- } from "./chunk-FKMKOWLA.mjs";
163
+ gradients,
164
+ setAiProvider,
165
+ setCurrentSprint
166
+ } from "./chunk-XN2UIHBY.mjs";
156
167
  import {
157
168
  ensureError,
158
169
  wrapAsync
@@ -167,165 +178,22 @@ import {
167
178
  getTasksFilePath,
168
179
  readTextFile,
169
180
  readValidatedJson
170
- } from "./chunk-CTP2A436.mjs";
171
- import "./chunk-57UWLHRH.mjs";
181
+ } from "./chunk-WDMLPXOD.mjs";
182
+ import {
183
+ ExecutionAlreadyRunningError
184
+ } from "./chunk-VAZ3LJBI.mjs";
172
185
 
173
186
  // src/integration/ui/tui/runtime/mount.tsx
174
187
  import "react";
175
188
  import { render } from "ink";
176
189
 
177
- // src/integration/ui/tui/runtime/event-bus.ts
178
- var FRAME_MS = 16;
179
- var SingletonLogEventBus = class {
180
- listeners = /* @__PURE__ */ new Set();
181
- buffer = [];
182
- flushTimer = null;
183
- emit(event) {
184
- this.buffer.push(event);
185
- this.scheduleFlush();
186
- }
187
- subscribe(listener) {
188
- this.listeners.add(listener);
189
- return () => {
190
- this.listeners.delete(listener);
191
- };
192
- }
193
- dispose() {
194
- if (this.flushTimer) {
195
- clearTimeout(this.flushTimer);
196
- this.flushTimer = null;
197
- }
198
- this.listeners.clear();
199
- this.buffer.length = 0;
200
- }
201
- /** Force immediate drain. Primarily for tests. */
202
- flush() {
203
- if (this.flushTimer) {
204
- clearTimeout(this.flushTimer);
205
- this.flushTimer = null;
206
- }
207
- this.drain();
208
- }
209
- scheduleFlush() {
210
- if (this.flushTimer !== null) return;
211
- const timer = setTimeout(() => {
212
- this.flushTimer = null;
213
- this.drain();
214
- }, FRAME_MS);
215
- timer.unref();
216
- this.flushTimer = timer;
217
- }
218
- drain() {
219
- if (this.buffer.length === 0) return;
220
- const batch = this.buffer.splice(0, this.buffer.length);
221
- for (const listener of this.listeners) {
222
- try {
223
- listener(batch);
224
- } catch {
225
- }
226
- }
227
- }
228
- };
229
- var logEventBus = new SingletonLogEventBus();
230
-
231
- // src/integration/logging/ink-sink.ts
232
- var spinnerId = 0;
233
- var InkSink = class _InkSink {
234
- constructor(context = {}) {
235
- this.context = context;
236
- }
237
- context;
238
- emit(event) {
239
- logEventBus.emit(event);
240
- }
241
- // -- Structured log levels --------------------------------------------------
242
- debug(message, context) {
243
- this.emit({ kind: "log", level: "debug", message, context: this.merge(context), timestamp: /* @__PURE__ */ new Date() });
244
- }
245
- info(message, context) {
246
- this.emit({ kind: "log", level: "info", message, context: this.merge(context), timestamp: /* @__PURE__ */ new Date() });
247
- }
248
- warn(message, context) {
249
- this.emit({ kind: "log", level: "warn", message, context: this.merge(context), timestamp: /* @__PURE__ */ new Date() });
250
- }
251
- error(message, context) {
252
- this.emit({ kind: "log", level: "error", message, context: this.merge(context), timestamp: /* @__PURE__ */ new Date() });
253
- }
254
- // -- UI-level output --------------------------------------------------------
255
- success(message) {
256
- this.emit({ kind: "log", level: "success", message, context: this.context, timestamp: /* @__PURE__ */ new Date() });
257
- }
258
- warning(message) {
259
- this.emit({ kind: "log", level: "warning", message, context: this.context, timestamp: /* @__PURE__ */ new Date() });
260
- }
261
- tip(message) {
262
- this.emit({ kind: "log", level: "tip", message, context: this.context, timestamp: /* @__PURE__ */ new Date() });
263
- }
264
- // -- Layout -----------------------------------------------------------------
265
- header(title, icon) {
266
- this.emit({ kind: "header", title, icon, timestamp: /* @__PURE__ */ new Date() });
267
- }
268
- separator(width = 40) {
269
- this.emit({ kind: "separator", width, timestamp: /* @__PURE__ */ new Date() });
270
- }
271
- field(label, value, _width) {
272
- void _width;
273
- this.emit({ kind: "field", label, value, timestamp: /* @__PURE__ */ new Date() });
274
- }
275
- card(title, lines) {
276
- this.emit({ kind: "card", title, lines, timestamp: /* @__PURE__ */ new Date() });
277
- }
278
- newline() {
279
- this.emit({ kind: "newline", timestamp: /* @__PURE__ */ new Date() });
280
- }
281
- dim(message) {
282
- this.emit({ kind: "log", level: "dim", message, context: this.context, timestamp: /* @__PURE__ */ new Date() });
283
- }
284
- item(message) {
285
- this.emit({ kind: "log", level: "item", message, context: this.context, timestamp: /* @__PURE__ */ new Date() });
286
- }
287
- // -- Interactive ------------------------------------------------------------
288
- spinner(message) {
289
- const id = ++spinnerId;
290
- this.emit({ kind: "spinner-start", id, message, timestamp: /* @__PURE__ */ new Date() });
291
- return {
292
- succeed: (msg) => {
293
- this.emit({ kind: "spinner-succeed", id, message: msg, timestamp: /* @__PURE__ */ new Date() });
294
- },
295
- fail: (msg) => {
296
- this.emit({ kind: "spinner-fail", id, message: msg, timestamp: /* @__PURE__ */ new Date() });
297
- },
298
- stop: () => {
299
- this.emit({ kind: "spinner-stop", id, timestamp: /* @__PURE__ */ new Date() });
300
- }
301
- };
302
- }
303
- // -- Scoped child -----------------------------------------------------------
304
- child(context) {
305
- return new _InkSink({ ...this.context, ...context });
306
- }
307
- // -- Timing -----------------------------------------------------------------
308
- time(label) {
309
- const start = Date.now();
310
- return () => {
311
- const ms = Date.now() - start;
312
- this.debug(`${label}: ${String(ms)}ms`);
313
- };
314
- }
315
- // -- Internals --------------------------------------------------------------
316
- merge(extra) {
317
- if (!extra) return this.context;
318
- return { ...this.context, ...extra };
319
- }
320
- };
321
-
322
190
  // src/integration/ui/tui/views/app.tsx
323
- import { useEffect as useEffect28, useState as useState30 } from "react";
324
- import { Box as Box35, useStdout } from "ink";
191
+ import { useEffect as useEffect37, useState as useState38 } from "react";
192
+ import { Box as Box37, useStdout } from "ink";
325
193
 
326
194
  // src/integration/ui/tui/views/view-router.tsx
327
- import { useCallback as useCallback10, useMemo as useMemo35, useRef as useRef3, useState as useState29 } from "react";
328
- import { Box as Box34, useApp, useInput as useInput19 } from "ink";
195
+ import { useCallback as useCallback12, useMemo as useMemo34, useState as useState37 } from "react";
196
+ import { Box as Box36 } from "ink";
329
197
 
330
198
  // src/integration/ui/tui/components/banner.tsx
331
199
  import { useMemo } from "react";
@@ -445,10 +313,13 @@ function useRouter() {
445
313
  }
446
314
  return api;
447
315
  }
316
+ function useRouterOptional() {
317
+ return useContext2(RouterContext);
318
+ }
448
319
 
449
320
  // src/integration/ui/tui/views/home-view.tsx
450
321
  import { useCallback as useCallback2, useEffect as useEffect4, useState as useState4 } from "react";
451
- import { Box as Box9, Text as Text8, useInput as useInput3 } from "ink";
322
+ import { Box as Box9, Text as Text8, useInput as useInput4 } from "ink";
452
323
 
453
324
  // src/integration/ui/tui/views/menu-builder.ts
454
325
  var SEPARATOR_WIDTH = 48;
@@ -581,6 +452,11 @@ function buildProjectSubMenu() {
581
452
  description: "Add repository to project"
582
453
  });
583
454
  items.push({ name: "Remove Repository", value: "repo remove", description: "Remove repository" });
455
+ items.push({
456
+ name: "Onboard repo for agentic work",
457
+ value: "onboard",
458
+ description: "AI-assisted project context file + check script"
459
+ });
584
460
  items.push(line());
585
461
  items.push({ name: "Remove", value: "remove", description: "Remove a project" });
586
462
  items.push({ name: "Back", value: "back", description: "Return to main menu" });
@@ -1013,8 +889,68 @@ function SectionStamp({ title, color: color2 = inkColors.primary }) {
1013
889
  // src/integration/ui/tui/components/view-shell.tsx
1014
890
  import "react";
1015
891
  import { Box as Box8 } from "ink";
892
+
893
+ // src/integration/ui/tui/runtime/use-global-keys.ts
894
+ import { useApp, useInput as useInput3 } from "ink";
895
+
896
+ // src/integration/ui/tui/views/home-submenu-memory.ts
897
+ var memory = null;
898
+ function getHomeSubmenuMemory() {
899
+ return memory;
900
+ }
901
+ function setHomeSubmenuMemory(group) {
902
+ memory = group;
903
+ }
904
+ function clearHomeSubmenuMemory() {
905
+ memory = null;
906
+ }
907
+
908
+ // src/integration/ui/tui/runtime/use-global-keys.ts
909
+ function useGlobalKeys() {
910
+ const router = useRouterOptional();
911
+ const app = useApp();
912
+ const currentPrompt = useCurrentPrompt();
913
+ const routerHotkeysActive = router !== null && currentPrompt === null;
914
+ useInput3(
915
+ (input, key) => {
916
+ if (router === null) return;
917
+ if (key.escape) {
918
+ router.pop();
919
+ return;
920
+ }
921
+ if (input === "h") {
922
+ clearHomeSubmenuMemory();
923
+ router.reset({ id: "home" });
924
+ return;
925
+ }
926
+ if (input === "s" && router.current.id !== "settings") {
927
+ router.push({ id: "settings" });
928
+ return;
929
+ }
930
+ if (input === "d" && router.current.id !== "dashboard") {
931
+ router.push({ id: "dashboard" });
932
+ return;
933
+ }
934
+ if (input === "?" && router.current.id !== "doctor") {
935
+ router.push({ id: "doctor" });
936
+ return;
937
+ }
938
+ if (input === "x" && router.current.id !== "running-executions") {
939
+ router.push({ id: "running-executions" });
940
+ return;
941
+ }
942
+ if (input === "q" && router.stack.length === 1 && router.current.id === "home") {
943
+ app.exit();
944
+ }
945
+ },
946
+ { isActive: routerHotkeysActive }
947
+ );
948
+ }
949
+
950
+ // src/integration/ui/tui/components/view-shell.tsx
1016
951
  import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1017
952
  function ViewShell({ title, bare = false, children }) {
953
+ useGlobalKeys();
1018
954
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1019
955
  !bare && title !== void 0 ? /* @__PURE__ */ jsx9(SectionStamp, { title }) : null,
1020
956
  /* @__PURE__ */ jsx9(Box8, { marginTop: bare ? 0 : spacing.section, flexDirection: "column", children })
@@ -1169,7 +1105,8 @@ var WORKFLOW_VIEWS = {
1169
1105
  remove: "project-remove",
1170
1106
  "repo add": "project-repo-add",
1171
1107
  "repo remove": "project-repo-remove",
1172
- edit: "project-edit"
1108
+ edit: "project-edit",
1109
+ onboard: "project-onboard"
1173
1110
  },
1174
1111
  progress: {
1175
1112
  log: "progress-log",
@@ -1249,8 +1186,8 @@ async function loadHomeState() {
1249
1186
  ctx.nextAction = getNextAction(dashboardData);
1250
1187
  return { ctx, dashboardData, snapshot: computePipelineSnapshot(ctx) };
1251
1188
  }
1252
- var lastSubGroup = null;
1253
1189
  function HomeView() {
1190
+ useGlobalKeys();
1254
1191
  const router = useRouter();
1255
1192
  const [state, setState] = useState4(null);
1256
1193
  const [mode, setModeInternal] = useState4("main");
@@ -1260,7 +1197,7 @@ function HomeView() {
1260
1197
  const setMode = useCallback2((next) => {
1261
1198
  setModeInternal((prev) => {
1262
1199
  const resolved = typeof next === "function" ? next(prev) : next;
1263
- lastSubGroup = typeof resolved === "object" ? resolved.group : null;
1200
+ setHomeSubmenuMemory(typeof resolved === "object" ? resolved.group : null);
1264
1201
  return resolved;
1265
1202
  });
1266
1203
  }, []);
@@ -1274,9 +1211,10 @@ function HomeView() {
1274
1211
  const next = await loadHomeState();
1275
1212
  if (cancel.current) return;
1276
1213
  setState(next);
1277
- if (lastSubGroup !== null) {
1278
- const menu = buildSubMenu(lastSubGroup, next.ctx);
1279
- if (menu) setModeInternal({ kind: "sub", menu, group: lastSubGroup });
1214
+ const stored = getHomeSubmenuMemory();
1215
+ if (stored !== null) {
1216
+ const menu = buildSubMenu(stored, next.ctx);
1217
+ if (menu) setModeInternal({ kind: "sub", menu, group: stored });
1280
1218
  }
1281
1219
  } catch (err) {
1282
1220
  if (!cancel.current) setError(err instanceof Error ? err.message : String(err));
@@ -1336,12 +1274,16 @@ function HomeView() {
1336
1274
  },
1337
1275
  [refresh, router, state]
1338
1276
  );
1339
- useInput3((input, key) => {
1277
+ useInput4((input, key) => {
1340
1278
  if (mode === "busy") return;
1341
1279
  if (key.escape && typeof mode === "object") {
1342
1280
  setMode("main");
1343
1281
  return;
1344
1282
  }
1283
+ if (input === "h" && typeof mode === "object") {
1284
+ setMode("main");
1285
+ return;
1286
+ }
1345
1287
  if (mode === "main" && input === "b" && state !== null) {
1346
1288
  const menu = buildSubMenu("browse", state.ctx);
1347
1289
  if (menu) setMode({ kind: "sub", menu, group: "browse" });
@@ -1450,7 +1392,7 @@ import "react";
1450
1392
 
1451
1393
  // src/integration/ui/tui/views/settings-panel.tsx
1452
1394
  import { useCallback as useCallback3, useEffect as useEffect5, useState as useState5 } from "react";
1453
- import { Box as Box10, Text as Text9, useInput as useInput4 } from "ink";
1395
+ import { Box as Box10, Text as Text9, useInput as useInput5 } from "ink";
1454
1396
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1455
1397
  var SETTINGS_HINTS = [
1456
1398
  { key: "\u2191/\u2193", action: "navigate" },
@@ -1572,7 +1514,7 @@ function SettingsPanel({ onClose }) {
1572
1514
  setEditing(false);
1573
1515
  }
1574
1516
  }, [cursor, entries, config, saveValue]);
1575
- useInput4((input, key) => {
1517
+ useInput5((input, key) => {
1576
1518
  if (editing) return;
1577
1519
  if (key.escape) {
1578
1520
  onClose();
@@ -1663,15 +1605,17 @@ function SettingsView() {
1663
1605
  }
1664
1606
 
1665
1607
  // src/integration/ui/tui/views/execute-view.tsx
1666
- import { useEffect as useEffect8, useRef as useRef2, useState as useState8 } from "react";
1667
- import { Box as Box17, Text as Text16, useInput as useInput5 } from "ink";
1608
+ import { useEffect as useEffect8, useMemo as useMemo6, useRef as useRef2, useState as useState8 } from "react";
1609
+ import { Box as Box19, Text as Text18, useInput as useInput6 } from "ink";
1668
1610
 
1669
1611
  // src/integration/ui/tui/runtime/hooks.ts
1670
1612
  import { useCallback as useCallback4, useEffect as useEffect6, useRef, useState as useState6 } from "react";
1671
- function useLoggerEvents(limit = 200) {
1613
+ function useLoggerEvents(limit = 200, bus) {
1672
1614
  const [buffer, setBuffer] = useState6([]);
1615
+ const source = bus ?? logEventBus;
1673
1616
  useEffect6(() => {
1674
- const unsubscribe = logEventBus.subscribe((batch) => {
1617
+ setBuffer([]);
1618
+ const unsubscribe = source.subscribe((batch) => {
1675
1619
  setBuffer((prev) => {
1676
1620
  const next = prev.concat(batch);
1677
1621
  if (next.length > limit) next.splice(0, next.length - limit);
@@ -1679,12 +1623,29 @@ function useLoggerEvents(limit = 200) {
1679
1623
  });
1680
1624
  });
1681
1625
  return unsubscribe;
1682
- }, [limit]);
1626
+ }, [limit, source]);
1683
1627
  return buffer;
1684
1628
  }
1629
+ function useRegistryEvents(registry) {
1630
+ const [executions, setExecutions] = useState6(() => registry ? registry.list() : []);
1631
+ useEffect6(() => {
1632
+ if (registry === null) {
1633
+ setExecutions([]);
1634
+ return;
1635
+ }
1636
+ setExecutions(registry.list());
1637
+ const unsubscribe = registry.subscribe(() => {
1638
+ setExecutions(registry.list());
1639
+ });
1640
+ return unsubscribe;
1641
+ }, [registry]);
1642
+ return executions;
1643
+ }
1685
1644
  function useSignalEvents(bus, limit = 200) {
1686
1645
  const [buffer, setBuffer] = useState6([]);
1687
1646
  useEffect6(() => {
1647
+ setBuffer([]);
1648
+ if (bus === null) return;
1688
1649
  const unsubscribe = bus.subscribe((batch) => {
1689
1650
  setBuffer((prev) => {
1690
1651
  const next = prev.concat(batch);
@@ -1987,10 +1948,59 @@ function RateLimitBanner({ pausedSince, delayMs }) {
1987
1948
  ] });
1988
1949
  }
1989
1950
 
1990
- // src/integration/ui/tui/views/execute-view.tsx
1951
+ // src/integration/ui/tui/components/result-card.tsx
1952
+ import "react";
1953
+ import { Box as Box18, Text as Text17 } from "ink";
1954
+
1955
+ // src/integration/ui/tui/components/field-list.tsx
1956
+ import "react";
1957
+ import { Box as Box17, Text as Text16 } from "ink";
1991
1958
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1992
- var EXECUTE_HINTS_RUNNING = [];
1993
- var EXECUTE_HINTS_DONE = [{ key: "Enter", action: "home" }];
1959
+ function FieldList({ fields, labelWidth: labelWidth2 = FIELD_LABEL_WIDTH }) {
1960
+ return /* @__PURE__ */ jsx18(Box17, { flexDirection: "column", children: fields.map(([label, value]) => /* @__PURE__ */ jsxs16(Box17, { children: [
1961
+ /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: (label + ":").padEnd(labelWidth2) }),
1962
+ /* @__PURE__ */ jsx18(Text16, { children: ` ${value}` })
1963
+ ] }, label)) });
1964
+ }
1965
+
1966
+ // src/integration/ui/tui/components/result-card.tsx
1967
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
1968
+ var GLYPH = {
1969
+ success: glyphs.check,
1970
+ error: glyphs.cross,
1971
+ warning: glyphs.warningGlyph,
1972
+ info: glyphs.infoGlyph
1973
+ };
1974
+ var COLOR = {
1975
+ success: inkColors.success,
1976
+ error: inkColors.error,
1977
+ warning: inkColors.warning,
1978
+ info: inkColors.info
1979
+ };
1980
+ function ResultCard({ kind, title, fields, nextSteps, lines }) {
1981
+ const color2 = COLOR[kind];
1982
+ return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", children: [
1983
+ /* @__PURE__ */ jsxs17(Box18, { children: [
1984
+ /* @__PURE__ */ jsx19(Text17, { color: color2, bold: true, children: GLYPH[kind] }),
1985
+ /* @__PURE__ */ jsx19(Text17, { color: color2, bold: true, children: ` ${title}` })
1986
+ ] }),
1987
+ fields && fields.length > 0 ? /* @__PURE__ */ jsx19(Box18, { marginTop: spacing.section, children: /* @__PURE__ */ jsx19(FieldList, { fields }) }) : null,
1988
+ lines && lines.length > 0 ? /* @__PURE__ */ jsx19(Box18, { marginTop: spacing.section, flexDirection: "column", children: lines.map((line2, i) => /* @__PURE__ */ jsx19(Box18, { children: /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: line2 }) }, i)) }) : null,
1989
+ nextSteps && nextSteps.length > 0 ? /* @__PURE__ */ jsxs17(Box18, { marginTop: spacing.section, flexDirection: "column", children: [
1990
+ /* @__PURE__ */ jsx19(Text17, { dimColor: true, bold: true, children: "Next" }),
1991
+ nextSteps.map((step, i) => /* @__PURE__ */ jsxs17(Box18, { paddingLeft: spacing.indent, children: [
1992
+ /* @__PURE__ */ jsx19(Text17, { color: inkColors.highlight, children: `${glyphs.arrowRight} ` }),
1993
+ /* @__PURE__ */ jsx19(Text17, { children: step.action }),
1994
+ step.description ? /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: ` ${glyphs.emDash} ${step.description}` }) : null
1995
+ ] }, i))
1996
+ ] }) : null
1997
+ ] });
1998
+ }
1999
+
2000
+ // src/integration/ui/tui/views/execute-view.tsx
2001
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2002
+ var EXECUTE_HINTS_RUNNING = [{ key: "c", action: "cancel" }];
2003
+ var EXECUTE_HINTS_TERMINAL = [{ key: "Enter", action: "back" }];
1994
2004
  function initialState() {
1995
2005
  return {
1996
2006
  sprint: null,
@@ -1999,62 +2009,104 @@ function initialState() {
1999
2009
  blocked: /* @__PURE__ */ new Set(),
2000
2010
  activity: /* @__PURE__ */ new Map(),
2001
2011
  currentStep: /* @__PURE__ */ new Map(),
2002
- summary: null,
2003
- error: null,
2004
- rateLimit: null
2012
+ rateLimit: null,
2013
+ taskRefreshError: null
2005
2014
  };
2006
2015
  }
2007
- function ExecuteView({ sprintId, executionOptions }) {
2016
+ var initialAttach = {
2017
+ kind: "attaching",
2018
+ execution: null,
2019
+ collisionId: null,
2020
+ errorMessage: null
2021
+ };
2022
+ function ExecuteView({ sprintId, executionId, executionOptions }) {
2008
2023
  const router = useRouter();
2009
2024
  const shared = getSharedDeps();
2010
- const signalEvents = useSignalEvents(shared.signalBus);
2011
- const logEvents = useLoggerEvents(200);
2025
+ const registry = shared.executionRegistry;
2026
+ const registryEvents = useRegistryEvents(registry);
2027
+ const [attach, setAttach] = useState8(initialAttach);
2012
2028
  const [state, setState] = useState8(initialState);
2013
- const [done, setDone] = useState8(false);
2014
2029
  const processedCountRef = useRef2(0);
2030
+ const startedRef = useRef2(false);
2031
+ const attachedId = attach.execution?.id ?? null;
2032
+ const liveExecution = useMemo6(() => {
2033
+ if (attachedId === null) return null;
2034
+ return registry.get(attachedId);
2035
+ }, [attachedId, registry, registryEvents]);
2015
2036
  useEffect8(() => {
2016
- const cancel = { current: false };
2017
- const load = async () => {
2037
+ if (startedRef.current) return;
2038
+ startedRef.current = true;
2039
+ if (executionId !== void 0) {
2040
+ const existing = registry.get(executionId);
2041
+ if (existing === null) {
2042
+ setAttach({
2043
+ kind: "error",
2044
+ execution: null,
2045
+ collisionId: null,
2046
+ errorMessage: `Execution ${executionId} is no longer tracked.`
2047
+ });
2048
+ return;
2049
+ }
2050
+ setAttach({ kind: "attached", execution: existing, collisionId: null, errorMessage: null });
2051
+ return;
2052
+ }
2053
+ void (async () => {
2018
2054
  try {
2019
- const [sprint, tasks] = await Promise.all([
2020
- shared.persistence.getSprint(sprintId),
2021
- shared.persistence.getTasks(sprintId)
2022
- ]);
2023
- if (!cancel.current) setState((s) => ({ ...s, sprint, tasks }));
2055
+ const execution = await registry.start({ sprintId, options: executionOptions });
2056
+ setAttach({ kind: "attached", execution, collisionId: null, errorMessage: null });
2024
2057
  } catch (err) {
2025
- if (!cancel.current) setState((s) => ({ ...s, error: err instanceof Error ? err.message : String(err) }));
2058
+ if (err instanceof ExecutionAlreadyRunningError) {
2059
+ setAttach({
2060
+ kind: "collision",
2061
+ execution: null,
2062
+ collisionId: err.existingExecutionId,
2063
+ errorMessage: err.message
2064
+ });
2065
+ return;
2066
+ }
2067
+ setAttach({
2068
+ kind: "error",
2069
+ execution: null,
2070
+ collisionId: null,
2071
+ errorMessage: err instanceof Error ? err.message : String(err)
2072
+ });
2026
2073
  }
2027
- };
2028
- void load();
2029
- return () => {
2030
- cancel.current = true;
2031
- };
2032
- }, [shared, sprintId]);
2074
+ })();
2075
+ }, [executionId, executionOptions, registry, sprintId]);
2076
+ const scopedSignalBus = useMemo6(() => {
2077
+ if (attachedId === null) return null;
2078
+ return registry.getSignalBus(attachedId);
2079
+ }, [attachedId, registry]);
2080
+ const scopedLogEventBus = useMemo6(() => {
2081
+ if (attachedId === null) return null;
2082
+ return registry.getLogEventBus(attachedId);
2083
+ }, [attachedId, registry]);
2084
+ const signalEvents = useSignalEvents(scopedSignalBus);
2085
+ const logEvents = useLoggerEvents(200, scopedLogEventBus);
2033
2086
  useEffect8(() => {
2034
- if (state.sprint === null || done) return;
2035
2087
  const cancel = { current: false };
2036
- const run = async () => {
2088
+ void (async () => {
2037
2089
  try {
2038
- const pipeline = createExecuteSprintPipeline(shared, executionOptions);
2039
- const result = await executePipeline(pipeline, { sprintId });
2040
- if (cancel.current) return;
2041
- if (result.ok) {
2042
- const summary = result.value.context.executionSummary ?? null;
2043
- setState((s) => ({ ...s, summary }));
2044
- } else {
2045
- setState((s) => ({ ...s, error: result.error.message }));
2090
+ if (liveExecution) {
2091
+ if (!cancel.current) {
2092
+ setState((s) => ({ ...s, sprint: liveExecution.sprint }));
2093
+ }
2046
2094
  }
2095
+ const tasks = await shared.persistence.getTasks(sprintId);
2096
+ if (!cancel.current) setState((s) => ({ ...s, tasks }));
2047
2097
  } catch (err) {
2048
- if (!cancel.current) setState((s) => ({ ...s, error: err instanceof Error ? err.message : String(err) }));
2049
- } finally {
2050
- if (!cancel.current) setDone(true);
2098
+ if (!cancel.current) {
2099
+ setState((s) => ({ ...s, taskRefreshError: err instanceof Error ? err.message : String(err) }));
2100
+ }
2051
2101
  }
2052
- };
2053
- void run();
2102
+ })();
2054
2103
  return () => {
2055
2104
  cancel.current = true;
2056
2105
  };
2057
- }, [state.sprint, shared, sprintId, executionOptions, done]);
2106
+ }, [liveExecution, shared, sprintId]);
2107
+ useEffect8(() => {
2108
+ processedCountRef.current = 0;
2109
+ }, [scopedSignalBus]);
2058
2110
  useEffect8(() => {
2059
2111
  if (signalEvents.length <= processedCountRef.current) return;
2060
2112
  const fresh = signalEvents.slice(processedCountRef.current);
@@ -2070,18 +2122,20 @@ function ExecuteView({ sprintId, executionOptions }) {
2070
2122
  })();
2071
2123
  }
2072
2124
  }, [signalEvents, shared, sprintId]);
2125
+ const status = liveExecution?.status ?? "running";
2126
+ const terminal = status === "completed" || status === "failed" || status === "cancelled";
2073
2127
  const [closePromptRun, setClosePromptRun] = useState8(false);
2074
2128
  useEffect8(() => {
2075
- if (!done) return;
2129
+ if (!terminal) return;
2130
+ if (closePromptRun) return;
2131
+ setClosePromptRun(true);
2076
2132
  void (async () => {
2077
2133
  try {
2078
2134
  const tasks = await shared.persistence.getTasks(sprintId);
2079
2135
  setState((s) => ({ ...s, tasks }));
2080
2136
  } catch {
2081
2137
  }
2082
- if (closePromptRun) return;
2083
- setClosePromptRun(true);
2084
- const summary = state.summary;
2138
+ const summary = liveExecution ? liveExecution.summary : void 0;
2085
2139
  if (summary?.stopReason === "all_completed" && summary.remaining === 0 && summary.completed > 0 && await areAllTasksDone(sprintId)) {
2086
2140
  try {
2087
2141
  const shouldClose = await getPrompt().confirm({
@@ -2093,28 +2147,62 @@ function ExecuteView({ sprintId, executionOptions }) {
2093
2147
  }
2094
2148
  }
2095
2149
  })();
2096
- }, [done, shared, sprintId, state.summary, closePromptRun]);
2097
- useInput5((input, key) => {
2098
- if (!done) return;
2099
- if (key.escape || input === "h" || input === "s" || input === "q") return;
2100
- if (key.return || input.length > 0) {
2101
- router.replace({ id: "home" });
2150
+ }, [terminal, closePromptRun, shared, sprintId, liveExecution]);
2151
+ useInput6((input, key) => {
2152
+ if (attach.kind === "attached" && liveExecution?.status === "running" && input === "c") {
2153
+ registry.cancel(liveExecution.id);
2154
+ return;
2155
+ }
2156
+ if (terminal && key.return) {
2157
+ router.pop();
2102
2158
  }
2103
2159
  });
2104
- useViewHints(done ? EXECUTE_HINTS_DONE : EXECUTE_HINTS_RUNNING);
2105
- return /* @__PURE__ */ jsxs16(ViewShell, { title: "Execute", children: [
2106
- /* @__PURE__ */ jsxs16(Box17, { children: [
2107
- /* @__PURE__ */ jsx18(Text16, { bold: true, color: inkColors.primary, children: state.sprint?.name ?? "Sprint" }),
2108
- /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
2160
+ useViewHints(terminal ? EXECUTE_HINTS_TERMINAL : EXECUTE_HINTS_RUNNING);
2161
+ if (attach.kind === "collision") {
2162
+ return /* @__PURE__ */ jsxs18(ViewShell, { title: "Execute", children: [
2163
+ /* @__PURE__ */ jsx20(
2164
+ ResultCard,
2165
+ {
2166
+ kind: "warning",
2167
+ title: "Execution already running",
2168
+ lines: [attach.errorMessage ?? "A run is already in progress for this project."],
2169
+ nextSteps: [
2170
+ {
2171
+ action: "Press Enter to view the running execution",
2172
+ description: "The harness keeps one execution per project."
2173
+ }
2174
+ ]
2175
+ }
2176
+ ),
2177
+ /* @__PURE__ */ jsx20(CollisionRedirect, { registry, collisionId: attach.collisionId, fallbackSprintId: sprintId })
2178
+ ] });
2179
+ }
2180
+ if (attach.kind === "error") {
2181
+ return /* @__PURE__ */ jsx20(ViewShell, { title: "Execute", children: /* @__PURE__ */ jsx20(
2182
+ ResultCard,
2183
+ {
2184
+ kind: "error",
2185
+ title: "Could not attach to execution",
2186
+ lines: [attach.errorMessage ?? "Unknown error."]
2187
+ }
2188
+ ) });
2189
+ }
2190
+ if (attach.kind === "attaching") {
2191
+ return /* @__PURE__ */ jsx20(ViewShell, { title: "Execute", children: /* @__PURE__ */ jsx20(Spinner, { label: "Attaching to execution\u2026" }) });
2192
+ }
2193
+ return /* @__PURE__ */ jsxs18(ViewShell, { title: "Execute", children: [
2194
+ /* @__PURE__ */ jsxs18(Box19, { children: [
2195
+ /* @__PURE__ */ jsx20(Text18, { bold: true, color: inkColors.primary, children: state.sprint?.name ?? "Sprint" }),
2196
+ /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
2109
2197
  " ",
2110
2198
  state.sprint?.branch ? `[${state.sprint.branch}]` : "",
2111
2199
  " ",
2112
2200
  state.sprint?.status ? `(${state.sprint.status})` : ""
2113
2201
  ] })
2114
2202
  ] }),
2115
- /* @__PURE__ */ jsx18(Box17, { marginTop: spacing.section, children: /* @__PURE__ */ jsx18(SprintSummary, { tasks: state.tasks }) }),
2116
- state.rateLimit ? /* @__PURE__ */ jsx18(Box17, { marginTop: spacing.section, children: /* @__PURE__ */ jsx18(RateLimitBanner, { pausedSince: state.rateLimit.pausedSince, delayMs: state.rateLimit.delayMs }) }) : null,
2117
- /* @__PURE__ */ jsx18(Box17, { marginTop: spacing.section, children: /* @__PURE__ */ jsx18(
2203
+ /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, children: /* @__PURE__ */ jsx20(SprintSummary, { tasks: state.tasks }) }),
2204
+ state.rateLimit ? /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, children: /* @__PURE__ */ jsx20(RateLimitBanner, { pausedSince: state.rateLimit.pausedSince, delayMs: state.rateLimit.delayMs }) }) : null,
2205
+ /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, children: /* @__PURE__ */ jsx20(
2118
2206
  TaskGrid,
2119
2207
  {
2120
2208
  tasks: state.tasks,
@@ -2123,42 +2211,61 @@ function ExecuteView({ sprintId, executionOptions }) {
2123
2211
  activityByTask: state.activity
2124
2212
  }
2125
2213
  ) }),
2126
- !done && state.currentStep.size > 0 ? /* @__PURE__ */ jsx18(Box17, { marginTop: spacing.section, flexDirection: "column", children: Array.from(state.currentStep.entries()).map(([taskId, label]) => {
2214
+ !terminal && state.currentStep.size > 0 ? /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, flexDirection: "column", children: Array.from(state.currentStep.entries()).map(([taskId, label]) => {
2127
2215
  const task = state.tasks.find((t) => t.id === taskId);
2128
2216
  const taskName = task?.name ?? taskId.slice(0, 8);
2129
- return /* @__PURE__ */ jsx18(Spinner, { label: `${taskName} ${glyphs.emDash} ${label}` }, taskId);
2217
+ return /* @__PURE__ */ jsx20(Spinner, { label: `${taskName} ${glyphs.emDash} ${label}` }, taskId);
2130
2218
  }) }) : null,
2131
- /* @__PURE__ */ jsx18(Box17, { marginTop: spacing.section, children: /* @__PURE__ */ jsx18(LogTail, { events: logEvents }) }),
2132
- state.error ? /* @__PURE__ */ jsx18(Box17, { marginTop: spacing.section, children: /* @__PURE__ */ jsxs16(Text16, { color: inkColors.error, children: [
2133
- glyphs.cross,
2134
- " ",
2135
- state.error
2136
- ] }) }) : null,
2137
- state.summary && done ? /* @__PURE__ */ jsxs16(Box17, { marginTop: spacing.section, flexDirection: "column", children: [
2138
- /* @__PURE__ */ jsxs16(Text16, { color: inkColors.success, bold: true, children: [
2139
- glyphs.check,
2140
- " Execution finished"
2219
+ /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, children: /* @__PURE__ */ jsx20(LogTail, { events: logEvents }) }),
2220
+ terminal && liveExecution ? /* @__PURE__ */ jsxs18(Box19, { marginTop: spacing.section, flexDirection: "column", children: [
2221
+ /* @__PURE__ */ jsxs18(Text18, { color: terminalColor(liveExecution.status), bold: true, children: [
2222
+ terminalGlyph(liveExecution.status),
2223
+ " Execution ",
2224
+ liveExecution.status
2141
2225
  ] }),
2142
- /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
2143
- state.summary.completed,
2226
+ liveExecution.summary ? /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
2227
+ liveExecution.summary.completed,
2144
2228
  " completed ",
2145
2229
  glyphs.inlineDot,
2146
2230
  " ",
2147
- state.summary.remaining,
2231
+ liveExecution.summary.remaining,
2148
2232
  " remaining",
2149
2233
  " ",
2150
2234
  glyphs.inlineDot,
2151
2235
  " ",
2152
- state.summary.blocked,
2236
+ liveExecution.summary.blocked,
2153
2237
  " blocked",
2154
2238
  " (",
2155
- state.summary.stopReason,
2239
+ liveExecution.summary.stopReason,
2156
2240
  ")"
2157
- ] }),
2158
- /* @__PURE__ */ jsx18(Box17, { marginTop: spacing.section, children: /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Press any key to return home." }) })
2241
+ ] }) : null
2159
2242
  ] }) : null
2160
2243
  ] });
2161
2244
  }
2245
+ function terminalColor(status) {
2246
+ if (status === "completed") return inkColors.success;
2247
+ if (status === "failed") return inkColors.error;
2248
+ return inkColors.muted;
2249
+ }
2250
+ function terminalGlyph(status) {
2251
+ if (status === "completed") return glyphs.check;
2252
+ if (status === "failed") return glyphs.cross;
2253
+ return glyphs.warningGlyph;
2254
+ }
2255
+ function CollisionRedirect({ registry, collisionId, fallbackSprintId }) {
2256
+ const router = useRouter();
2257
+ useInput6((_input, key) => {
2258
+ if (!collisionId) return;
2259
+ if (key.return) {
2260
+ const existingSprintId = registry.get(collisionId)?.sprintId ?? fallbackSprintId;
2261
+ router.replace({
2262
+ id: "execute",
2263
+ props: { sprintId: existingSprintId, executionId: collisionId }
2264
+ });
2265
+ }
2266
+ });
2267
+ return null;
2268
+ }
2162
2269
  var STEP_LABELS = {
2163
2270
  "branch-preflight": "Verifying branch\u2026",
2164
2271
  "contract-negotiate": "Writing contract\u2026",
@@ -2229,57 +2336,6 @@ function reduceEvents(state, events) {
2229
2336
  // src/integration/ui/tui/views/dashboard-view.tsx
2230
2337
  import { useEffect as useEffect9, useState as useState9 } from "react";
2231
2338
  import { Box as Box20, Text as Text19 } from "ink";
2232
-
2233
- // src/integration/ui/tui/components/result-card.tsx
2234
- import "react";
2235
- import { Box as Box19, Text as Text18 } from "ink";
2236
-
2237
- // src/integration/ui/tui/components/field-list.tsx
2238
- import "react";
2239
- import { Box as Box18, Text as Text17 } from "ink";
2240
- import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2241
- function FieldList({ fields, labelWidth = FIELD_LABEL_WIDTH }) {
2242
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: fields.map(([label, value]) => /* @__PURE__ */ jsxs17(Box18, { children: [
2243
- /* @__PURE__ */ jsx19(Text17, { dimColor: true, children: (label + ":").padEnd(labelWidth) }),
2244
- /* @__PURE__ */ jsx19(Text17, { children: ` ${value}` })
2245
- ] }, label)) });
2246
- }
2247
-
2248
- // src/integration/ui/tui/components/result-card.tsx
2249
- import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2250
- var GLYPH = {
2251
- success: glyphs.check,
2252
- error: glyphs.cross,
2253
- warning: glyphs.warningGlyph,
2254
- info: glyphs.infoGlyph
2255
- };
2256
- var COLOR = {
2257
- success: inkColors.success,
2258
- error: inkColors.error,
2259
- warning: inkColors.warning,
2260
- info: inkColors.info
2261
- };
2262
- function ResultCard({ kind, title, fields, nextSteps, lines }) {
2263
- const color2 = COLOR[kind];
2264
- return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", children: [
2265
- /* @__PURE__ */ jsxs18(Box19, { children: [
2266
- /* @__PURE__ */ jsx20(Text18, { color: color2, bold: true, children: GLYPH[kind] }),
2267
- /* @__PURE__ */ jsx20(Text18, { color: color2, bold: true, children: ` ${title}` })
2268
- ] }),
2269
- fields && fields.length > 0 ? /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, children: /* @__PURE__ */ jsx20(FieldList, { fields }) }) : null,
2270
- lines && lines.length > 0 ? /* @__PURE__ */ jsx20(Box19, { marginTop: spacing.section, flexDirection: "column", children: lines.map((line2, i) => /* @__PURE__ */ jsx20(Box19, { children: /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: line2 }) }, i)) }) : null,
2271
- nextSteps && nextSteps.length > 0 ? /* @__PURE__ */ jsxs18(Box19, { marginTop: spacing.section, flexDirection: "column", children: [
2272
- /* @__PURE__ */ jsx20(Text18, { dimColor: true, bold: true, children: "Next" }),
2273
- nextSteps.map((step, i) => /* @__PURE__ */ jsxs18(Box19, { paddingLeft: spacing.indent, children: [
2274
- /* @__PURE__ */ jsx20(Text18, { color: inkColors.highlight, children: `${glyphs.arrowRight} ` }),
2275
- /* @__PURE__ */ jsx20(Text18, { children: step.action }),
2276
- step.description ? /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: ` ${glyphs.emDash} ${step.description}` }) : null
2277
- ] }, i))
2278
- ] }) : null
2279
- ] });
2280
- }
2281
-
2282
- // src/integration/ui/tui/views/dashboard-view.tsx
2283
2339
  import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
2284
2340
  var PROGRESS_TAIL_LIMIT = 8;
2285
2341
  async function loadRecentProgress(sprintId, limit) {
@@ -2311,12 +2367,12 @@ function Hero({ data }) {
2311
2367
  const ticketCount = sprint.tickets.length;
2312
2368
  const taskCount = tasks.length;
2313
2369
  const providerLabel = aiProvider === "claude" ? "Claude" : aiProvider === "copilot" ? "Copilot" : null;
2314
- const statusColor6 = sprint.status === "active" ? inkColors.success : sprint.status === "closed" ? inkColors.info : inkColors.muted;
2370
+ const statusColor7 = sprint.status === "active" ? inkColors.success : sprint.status === "closed" ? inkColors.info : inkColors.muted;
2315
2371
  return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", children: [
2316
2372
  /* @__PURE__ */ jsxs19(Box20, { children: [
2317
2373
  /* @__PURE__ */ jsx21(Text19, { bold: true, color: inkColors.primary, children: sprint.name }),
2318
2374
  /* @__PURE__ */ jsx21(Text19, { children: " " }),
2319
- /* @__PURE__ */ jsxs19(Text19, { color: statusColor6, children: [
2375
+ /* @__PURE__ */ jsxs19(Text19, { color: statusColor7, children: [
2320
2376
  "[",
2321
2377
  sprint.status,
2322
2378
  "]"
@@ -2434,7 +2490,7 @@ var EMPTY_MAP = /* @__PURE__ */ new Map();
2434
2490
 
2435
2491
  // src/integration/ui/tui/views/phases/refine-phase-view.tsx
2436
2492
  import { useCallback as useCallback5, useEffect as useEffect10, useState as useState10 } from "react";
2437
- import { Box as Box22, Text as Text21, useInput as useInput6 } from "ink";
2493
+ import { Box as Box22, Text as Text21, useInput as useInput7 } from "ink";
2438
2494
 
2439
2495
  // src/integration/ui/tui/views/phases/phase-run-trace.tsx
2440
2496
  import "react";
@@ -2460,13 +2516,13 @@ function formatDuration(ms) {
2460
2516
  function PhaseRunTrace({ records, title = "Last run" }) {
2461
2517
  return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", children: [
2462
2518
  /* @__PURE__ */ jsx22(Box21, { children: /* @__PURE__ */ jsx22(Text20, { dimColor: true, bold: true, children: title }) }),
2463
- records.length === 0 ? /* @__PURE__ */ jsx22(Box21, { paddingLeft: 2, children: /* @__PURE__ */ jsx22(Text20, { dimColor: true, children: "(no runs yet)" }) }) : records.map((r, i) => /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", paddingLeft: 2, children: [
2519
+ records.length === 0 ? /* @__PURE__ */ jsx22(Box21, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx22(Text20, { dimColor: true, children: "(no runs yet)" }) }) : records.map((r, i) => /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", paddingLeft: spacing.indent, children: [
2464
2520
  /* @__PURE__ */ jsxs20(Box21, { children: [
2465
2521
  /* @__PURE__ */ jsx22(Text20, { color: STATUS_COLOR2[r.status], bold: true, children: STATUS_GLYPH2[r.status] }),
2466
2522
  /* @__PURE__ */ jsx22(Text20, { children: ` ${r.stepName} ` }),
2467
2523
  /* @__PURE__ */ jsx22(Text20, { dimColor: true, children: formatDuration(r.durationMs) })
2468
2524
  ] }),
2469
- r.error ? /* @__PURE__ */ jsx22(Box21, { paddingLeft: 2, children: /* @__PURE__ */ jsxs20(Text20, { color: inkColors.error, children: [
2525
+ r.error ? /* @__PURE__ */ jsx22(Box21, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsxs20(Text20, { color: inkColors.error, children: [
2470
2526
  "\u21B3 ",
2471
2527
  r.error.message
2472
2528
  ] }) }) : null
@@ -2528,7 +2584,7 @@ function RefinePhaseView({ sprintId }) {
2528
2584
  await loadSprint();
2529
2585
  }
2530
2586
  }, [shared, sprintId, loadSprint]);
2531
- useInput6(
2587
+ useInput7(
2532
2588
  (_input, key) => {
2533
2589
  if (key.return && !state.running && canRefine(state.sprint)) {
2534
2590
  void runRefine();
@@ -2590,7 +2646,7 @@ function reasonUnavailable(sprint) {
2590
2646
 
2591
2647
  // src/integration/ui/tui/views/phases/plan-phase-view.tsx
2592
2648
  import { useCallback as useCallback6, useEffect as useEffect11, useState as useState11 } from "react";
2593
- import { Box as Box23, Text as Text22, useInput as useInput7 } from "ink";
2649
+ import { Box as Box23, Text as Text22, useInput as useInput8 } from "ink";
2594
2650
  import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
2595
2651
  var HINTS_RUNNABLE2 = [
2596
2652
  { key: "Enter", action: "plan" },
@@ -2634,7 +2690,7 @@ function PlanPhaseView({ sprintId }) {
2634
2690
  await loadSprintAndTasks();
2635
2691
  }
2636
2692
  }, [shared, sprintId, loadSprintAndTasks]);
2637
- useInput7(
2693
+ useInput8(
2638
2694
  (_input, key) => {
2639
2695
  if (key.return && !state.running && canPlan(state.sprint)) {
2640
2696
  void runPlan();
@@ -2711,18 +2767,20 @@ function groupTasksByPath(tasks) {
2711
2767
  function statusGlyph(status) {
2712
2768
  if (status === "done") return glyphs.check;
2713
2769
  if (status === "in_progress") return glyphs.actionCursor;
2770
+ if (status === "cancelled") return glyphs.cross;
2714
2771
  return glyphs.inlineDot;
2715
2772
  }
2716
2773
  function statusColor2(status) {
2717
2774
  if (status === "done") return inkColors.success;
2718
2775
  if (status === "in_progress") return inkColors.warning;
2776
+ if (status === "cancelled") return inkColors.muted;
2719
2777
  return inkColors.muted;
2720
2778
  }
2721
2779
 
2722
2780
  // src/integration/ui/tui/views/phases/close-phase-view.tsx
2723
2781
  import { spawnSync } from "child_process";
2724
- import { useCallback as useCallback7, useEffect as useEffect12, useMemo as useMemo6, useState as useState12 } from "react";
2725
- import { Box as Box24, Text as Text23, useInput as useInput8 } from "ink";
2782
+ import { useCallback as useCallback7, useEffect as useEffect12, useMemo as useMemo7, useState as useState12 } from "react";
2783
+ import { Box as Box24, Text as Text23, useInput as useInput9 } from "ink";
2726
2784
  import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
2727
2785
  var HINTS_READY = [
2728
2786
  { key: "\u2191/\u2193", action: "select" },
@@ -2755,7 +2813,7 @@ function ClosePhaseView({ sprintId }) {
2755
2813
  useEffect12(() => {
2756
2814
  void load();
2757
2815
  }, [load]);
2758
- const actions = useMemo6(() => {
2816
+ const actions = useMemo7(() => {
2759
2817
  const sprint2 = state.sprint;
2760
2818
  if (sprint2?.status !== "active") return [];
2761
2819
  const base = ["close"];
@@ -2801,7 +2859,7 @@ function ClosePhaseView({ sprintId }) {
2801
2859
  },
2802
2860
  [sprintId, state.sprint]
2803
2861
  );
2804
- useInput8(
2862
+ useInput9(
2805
2863
  (_input, key) => {
2806
2864
  const { phase: phase2 } = state;
2807
2865
  if (phase2.kind === "done" || phase2.kind === "error") {
@@ -3000,11 +3058,11 @@ ID: ${sprintId}`
3000
3058
  }
3001
3059
 
3002
3060
  // src/integration/ui/tui/views/workflows/create-sprint-view.tsx
3003
- import { useMemo as useMemo7 } from "react";
3061
+ import { useMemo as useMemo8 } from "react";
3004
3062
 
3005
3063
  // src/integration/ui/tui/views/workflows/use-workflow.ts
3006
3064
  import { useCallback as useCallback8, useEffect as useEffect13, useState as useState13 } from "react";
3007
- import { useInput as useInput9 } from "ink";
3065
+ import { useInput as useInput10 } from "ink";
3008
3066
  var DEFAULT_NON_INTERACTIVE = /* @__PURE__ */ new Set(["running", "loading"]);
3009
3067
  function defaultIsTerminal(phase) {
3010
3068
  return !DEFAULT_NON_INTERACTIVE.has(phase.kind);
@@ -3016,7 +3074,7 @@ function useWorkflow(options) {
3016
3074
  const router = useRouter();
3017
3075
  const [phase, setPhase] = useState13(options.initial);
3018
3076
  const [started, setStarted] = useState13(false);
3019
- const isTerminal = options.isTerminal ?? defaultIsTerminal;
3077
+ const isTerminal2 = options.isTerminal ?? defaultIsTerminal;
3020
3078
  const isInteractive = options.isInteractive ?? defaultIsInteractive;
3021
3079
  const kick = useCallback8(async () => {
3022
3080
  try {
@@ -3034,9 +3092,9 @@ function useWorkflow(options) {
3034
3092
  setStarted(true);
3035
3093
  void kick();
3036
3094
  }, [started, kick]);
3037
- useInput9(
3095
+ useInput10(
3038
3096
  (_input, key) => {
3039
- if (key.return && isTerminal(phase)) router.pop();
3097
+ if (key.return && isTerminal2(phase)) router.pop();
3040
3098
  },
3041
3099
  { isActive: isInteractive(phase) }
3042
3100
  );
@@ -3094,7 +3152,7 @@ function CreateSprintView() {
3094
3152
  setPhase({ kind: "done", sprint, project, setAsCurrent });
3095
3153
  }
3096
3154
  });
3097
- const hints = useMemo7(() => phase.kind === "running" ? HINTS_RUNNING : HINTS_DONE, [phase.kind]);
3155
+ const hints = useMemo8(() => phase.kind === "running" ? HINTS_RUNNING : HINTS_DONE, [phase.kind]);
3098
3156
  useViewHints(hints);
3099
3157
  return /* @__PURE__ */ jsx26(ViewShell, { title: TITLE, children: renderBody(phase) });
3100
3158
  }
@@ -3136,84 +3194,154 @@ function renderBody(phase) {
3136
3194
  }
3137
3195
 
3138
3196
  // src/integration/ui/tui/views/workflows/delete-sprint-view.tsx
3139
- import { useMemo as useMemo8 } from "react";
3197
+ import { useEffect as useEffect15, useState as useState15 } from "react";
3198
+
3199
+ // src/integration/ui/tui/components/removal-workflow.tsx
3200
+ import { useEffect as useEffect14, useMemo as useMemo9, useState as useState14 } from "react";
3201
+ import { useInput as useInput11 } from "ink";
3140
3202
  import { jsx as jsx27 } from "react/jsx-runtime";
3141
- var TITLE2 = "Delete Sprint";
3142
3203
  var HINTS_RUNNING2 = [{ key: "Esc", action: "cancel" }];
3143
3204
  var HINTS_DONE2 = [
3144
- { key: "Enter", action: "home" },
3205
+ { key: "Enter", action: "back" },
3145
3206
  { key: "Esc", action: "back" }
3146
3207
  ];
3147
- function DeleteSprintView({ sprintId: initial2 }) {
3148
- const { phase } = useWorkflow({
3149
- initial: { kind: "loading" },
3150
- onError: (message) => ({ kind: "error", message }),
3151
- run: async ({ setPhase }) => {
3152
- const prompt = getPrompt();
3153
- let targetId = initial2 ?? null;
3154
- if (!targetId) {
3155
- const sprints = await listSprints();
3156
- if (sprints.length === 0) {
3157
- setPhase({ kind: "no-sprints" });
3208
+ function RemovalWorkflow({
3209
+ entityLabel,
3210
+ confirmMessage,
3211
+ onConfirm,
3212
+ successMessage,
3213
+ onDone
3214
+ }) {
3215
+ const [phase, setPhase] = useState14({ kind: "idle" });
3216
+ const [started, setStarted] = useState14(false);
3217
+ useEffect14(() => {
3218
+ if (started) return;
3219
+ setStarted(true);
3220
+ void (async () => {
3221
+ try {
3222
+ setPhase({ kind: "confirming" });
3223
+ const confirmed = await getPrompt().confirm({ message: confirmMessage, default: false });
3224
+ if (!confirmed) {
3225
+ setPhase({ kind: "done", outcome: "cancelled" });
3158
3226
  return;
3159
3227
  }
3160
- setPhase({ kind: "running", step: "select" });
3161
- targetId = await prompt.select({
3162
- message: "Select sprint to delete:",
3163
- choices: sprints.map((s) => ({
3164
- label: `${s.id} \u2014 ${s.name} (${s.status})`,
3165
- value: s.id
3166
- }))
3167
- });
3168
- }
3169
- const sprint = await getSprint(targetId);
3170
- if (sprint.status === "active") {
3171
- setPhase({ kind: "active-blocked", name: sprint.name });
3172
- return;
3228
+ setPhase({ kind: "running" });
3229
+ await onConfirm();
3230
+ setPhase({ kind: "done", outcome: "success" });
3231
+ } catch (err) {
3232
+ if (err instanceof PromptCancelledError) {
3233
+ onDone();
3234
+ return;
3235
+ }
3236
+ setPhase({ kind: "error", message: err instanceof Error ? err.message : String(err) });
3173
3237
  }
3174
- const tasks = await listTasks(targetId).catch(() => []);
3175
- setPhase({ kind: "running", step: "confirm" });
3176
- const confirmed = await prompt.confirm({
3177
- message: `Delete "${sprint.name}" (${String(sprint.tickets.length)} tickets, ${String(tasks.length)} tasks)? This cannot be undone.`,
3178
- default: false
3179
- });
3180
- if (!confirmed) {
3181
- setPhase({ kind: "cancelled" });
3182
- return;
3238
+ })();
3239
+ }, [started, confirmMessage, onConfirm, onDone]);
3240
+ const terminal = phase.kind === "done" || phase.kind === "error";
3241
+ const hints = useMemo9(() => terminal ? HINTS_DONE2 : HINTS_RUNNING2, [terminal]);
3242
+ useViewHints(hints);
3243
+ useInput11(
3244
+ (_input, key) => {
3245
+ if (key.return && terminal) onDone();
3246
+ },
3247
+ { isActive: terminal }
3248
+ );
3249
+ return /* @__PURE__ */ jsx27(ViewShell, { title: entityLabel, children: renderBody2(phase, successMessage, entityLabel) });
3250
+ }
3251
+ function renderBody2(phase, successMessage, entityLabel) {
3252
+ switch (phase.kind) {
3253
+ case "idle":
3254
+ case "confirming":
3255
+ return /* @__PURE__ */ jsx27(Spinner, { label: "Awaiting confirmation\u2026" });
3256
+ case "running":
3257
+ return /* @__PURE__ */ jsx27(Spinner, { label: `${entityLabel}\u2026` });
3258
+ case "done":
3259
+ return phase.outcome === "success" ? /* @__PURE__ */ jsx27(ResultCard, { kind: "success", title: successMessage }) : /* @__PURE__ */ jsx27(ResultCard, { kind: "info", title: "Removal cancelled" });
3260
+ case "error":
3261
+ return /* @__PURE__ */ jsx27(ResultCard, { kind: "error", title: `Could not complete ${entityLabel}`, lines: [phase.message] });
3262
+ }
3263
+ }
3264
+
3265
+ // src/integration/ui/tui/views/workflows/delete-sprint-view.tsx
3266
+ import { jsx as jsx28 } from "react/jsx-runtime";
3267
+ var TITLE2 = "Delete Sprint";
3268
+ function DeleteSprintView({ sprintId: initial2 }) {
3269
+ const router = useRouter();
3270
+ const [phase, setPhase] = useState15({ kind: "loading" });
3271
+ const [started, setStarted] = useState15(false);
3272
+ useEffect15(() => {
3273
+ if (started) return;
3274
+ setStarted(true);
3275
+ void (async () => {
3276
+ try {
3277
+ const prompt = getPrompt();
3278
+ let targetId = initial2 ?? null;
3279
+ if (!targetId) {
3280
+ const sprints = await listSprints();
3281
+ if (sprints.length === 0) {
3282
+ setPhase({ kind: "no-sprints" });
3283
+ return;
3284
+ }
3285
+ setPhase({ kind: "selecting" });
3286
+ targetId = await prompt.select({
3287
+ message: "Select sprint to delete:",
3288
+ choices: sprints.map((s) => ({
3289
+ label: `${s.id} \u2014 ${s.name} (${s.status})`,
3290
+ value: s.id
3291
+ }))
3292
+ });
3293
+ }
3294
+ const sprint = await getSprint(targetId);
3295
+ if (sprint.status === "active") {
3296
+ setPhase({ kind: "active-blocked", name: sprint.name });
3297
+ return;
3298
+ }
3299
+ const tasks = await listTasks(targetId).catch(() => []);
3300
+ setPhase({ kind: "ready", sprint, taskCount: tasks.length });
3301
+ } catch (err) {
3302
+ if (err instanceof PromptCancelledError) {
3303
+ router.pop();
3304
+ return;
3305
+ }
3306
+ setPhase({ kind: "error", message: err instanceof Error ? err.message : String(err) });
3183
3307
  }
3184
- const reconfirmed = await prompt.confirm({
3185
- message: `Really delete "${sprint.name}"? All sprint data (tickets, tasks, progress, evaluations) will be removed.`,
3186
- default: false
3187
- });
3188
- if (!reconfirmed) {
3189
- setPhase({ kind: "cancelled" });
3190
- return;
3308
+ })();
3309
+ }, [started, initial2, router]);
3310
+ if (phase.kind === "ready") {
3311
+ const { sprint, taskCount } = phase;
3312
+ const ticketCount = sprint.tickets.length;
3313
+ const confirmMessage = `Delete "${sprint.name}" (${String(ticketCount)} ${pluralize2("ticket", ticketCount)}, ${String(
3314
+ taskCount
3315
+ )} ${pluralize2("task", taskCount)})? All sprint data \u2014 tickets, tasks, progress, evaluations \u2014 will be removed. This cannot be undone.`;
3316
+ return /* @__PURE__ */ jsx28(
3317
+ RemovalWorkflow,
3318
+ {
3319
+ entityLabel: TITLE2,
3320
+ confirmMessage,
3321
+ onConfirm: async () => {
3322
+ const currentId = await getCurrentSprint();
3323
+ await deleteSprint(sprint.id);
3324
+ if (currentId === sprint.id) await setCurrentSprint(null);
3325
+ },
3326
+ successMessage: `Sprint "${sprint.name}" deleted`,
3327
+ onDone: () => {
3328
+ router.pop();
3329
+ }
3191
3330
  }
3192
- setPhase({ kind: "running", step: "deleting" });
3193
- const currentId = await getCurrentSprint();
3194
- await deleteSprint(targetId);
3195
- const clearedCurrent = currentId === targetId;
3196
- if (clearedCurrent) await setCurrentSprint(null);
3197
- setPhase({ kind: "done", name: sprint.name, id: sprint.id, clearedCurrent });
3198
- }
3199
- });
3200
- const running = phase.kind === "running" || phase.kind === "loading";
3201
- const hints = useMemo8(() => running ? HINTS_RUNNING2 : HINTS_DONE2, [running]);
3202
- useViewHints(hints);
3203
- return /* @__PURE__ */ jsx27(ViewShell, { title: TITLE2, children: renderBody2(phase) });
3331
+ );
3332
+ }
3333
+ return /* @__PURE__ */ jsx28(ViewShell, { title: TITLE2, children: renderPre(phase) });
3204
3334
  }
3205
- function renderBody2(phase) {
3335
+ function renderPre(phase) {
3206
3336
  switch (phase.kind) {
3207
3337
  case "loading":
3208
- return /* @__PURE__ */ jsx27(Spinner, { label: "Loading sprints\u2026" });
3338
+ return /* @__PURE__ */ jsx28(Spinner, { label: "Loading sprints\u2026" });
3339
+ case "selecting":
3340
+ return /* @__PURE__ */ jsx28(Spinner, { label: "Awaiting selection\u2026" });
3209
3341
  case "no-sprints":
3210
- return /* @__PURE__ */ jsx27(ResultCard, { kind: "info", title: "No sprints to delete" });
3211
- case "running":
3212
- return /* @__PURE__ */ jsx27(Spinner, { label: runningLabel(phase.step) });
3213
- case "cancelled":
3214
- return /* @__PURE__ */ jsx27(ResultCard, { kind: "info", title: "Deletion cancelled" });
3342
+ return /* @__PURE__ */ jsx28(ResultCard, { kind: "info", title: "No sprints to delete" });
3215
3343
  case "active-blocked":
3216
- return /* @__PURE__ */ jsx27(
3344
+ return /* @__PURE__ */ jsx28(
3217
3345
  ResultCard,
3218
3346
  {
3219
3347
  kind: "warning",
@@ -3222,31 +3350,16 @@ function renderBody2(phase) {
3222
3350
  }
3223
3351
  );
3224
3352
  case "error":
3225
- return /* @__PURE__ */ jsx27(ResultCard, { kind: "error", title: "Could not delete sprint", lines: [phase.message] });
3226
- case "done":
3227
- return /* @__PURE__ */ jsx27(
3228
- ResultCard,
3229
- {
3230
- kind: "success",
3231
- title: "Sprint deleted",
3232
- fields: [
3233
- ["Name", phase.name],
3234
- ["ID", phase.id]
3235
- ],
3236
- lines: phase.clearedCurrent ? ["Current sprint pointer was cleared."] : void 0
3237
- }
3238
- );
3353
+ return /* @__PURE__ */ jsx28(ResultCard, { kind: "error", title: "Could not delete sprint", lines: [phase.message] });
3239
3354
  }
3240
3355
  }
3241
- function runningLabel(step) {
3242
- if (step === "select") return "Awaiting selection\u2026";
3243
- if (step === "confirm") return "Awaiting confirmation\u2026";
3244
- return "Deleting sprint\u2026";
3356
+ function pluralize2(noun, count) {
3357
+ return count === 1 ? noun : `${noun}s`;
3245
3358
  }
3246
3359
 
3247
3360
  // src/integration/ui/tui/views/workflows/set-current-sprint-view.tsx
3248
- import { useMemo as useMemo9 } from "react";
3249
- import { jsx as jsx28 } from "react/jsx-runtime";
3361
+ import { useMemo as useMemo10 } from "react";
3362
+ import { jsx as jsx29 } from "react/jsx-runtime";
3250
3363
  var TITLE3 = "Set Current Sprint";
3251
3364
  var HINTS_RUNNING3 = [{ key: "Esc", action: "cancel" }];
3252
3365
  var HINTS_DONE3 = [
@@ -3278,18 +3391,18 @@ function SetCurrentSprintView() {
3278
3391
  }
3279
3392
  });
3280
3393
  const running = phase.kind === "loading" || phase.kind === "running";
3281
- const hints = useMemo9(() => running ? HINTS_RUNNING3 : HINTS_DONE3, [running]);
3394
+ const hints = useMemo10(() => running ? HINTS_RUNNING3 : HINTS_DONE3, [running]);
3282
3395
  useViewHints(hints);
3283
- return /* @__PURE__ */ jsx28(ViewShell, { title: TITLE3, children: renderBody3(phase) });
3396
+ return /* @__PURE__ */ jsx29(ViewShell, { title: TITLE3, children: renderBody3(phase) });
3284
3397
  }
3285
3398
  function renderBody3(phase) {
3286
3399
  switch (phase.kind) {
3287
3400
  case "loading":
3288
- return /* @__PURE__ */ jsx28(Spinner, { label: "Loading sprints\u2026" });
3401
+ return /* @__PURE__ */ jsx29(Spinner, { label: "Loading sprints\u2026" });
3289
3402
  case "running":
3290
- return /* @__PURE__ */ jsx28(Spinner, { label: "Awaiting sprint selection\u2026" });
3403
+ return /* @__PURE__ */ jsx29(Spinner, { label: "Awaiting sprint selection\u2026" });
3291
3404
  case "no-candidates":
3292
- return /* @__PURE__ */ jsx28(
3405
+ return /* @__PURE__ */ jsx29(
3293
3406
  ResultCard,
3294
3407
  {
3295
3408
  kind: "info",
@@ -3298,9 +3411,9 @@ function renderBody3(phase) {
3298
3411
  }
3299
3412
  );
3300
3413
  case "error":
3301
- return /* @__PURE__ */ jsx28(ResultCard, { kind: "error", title: "Could not set current sprint", lines: [phase.message] });
3414
+ return /* @__PURE__ */ jsx29(ResultCard, { kind: "error", title: "Could not set current sprint", lines: [phase.message] });
3302
3415
  case "done":
3303
- return /* @__PURE__ */ jsx28(
3416
+ return /* @__PURE__ */ jsx29(
3304
3417
  ResultCard,
3305
3418
  {
3306
3419
  kind: "success",
@@ -3316,8 +3429,8 @@ function renderBody3(phase) {
3316
3429
 
3317
3430
  // src/integration/ui/tui/views/workflows/requirements-export-view.tsx
3318
3431
  import { join } from "path";
3319
- import { useMemo as useMemo10 } from "react";
3320
- import { jsx as jsx29 } from "react/jsx-runtime";
3432
+ import { useMemo as useMemo11 } from "react";
3433
+ import { jsx as jsx30 } from "react/jsx-runtime";
3321
3434
  var TITLE4 = "Export Requirements";
3322
3435
  var HINTS_RUNNING4 = [{ key: "Esc", action: "cancel" }];
3323
3436
  var HINTS_DONE4 = [
@@ -3351,16 +3464,16 @@ function RequirementsExportView({ sprintId }) {
3351
3464
  });
3352
3465
  }
3353
3466
  });
3354
- const hints = useMemo10(() => phase.kind === "running" ? HINTS_RUNNING4 : HINTS_DONE4, [phase.kind]);
3467
+ const hints = useMemo11(() => phase.kind === "running" ? HINTS_RUNNING4 : HINTS_DONE4, [phase.kind]);
3355
3468
  useViewHints(hints);
3356
- return /* @__PURE__ */ jsx29(ViewShell, { title: TITLE4, children: renderBody4(phase) });
3469
+ return /* @__PURE__ */ jsx30(ViewShell, { title: TITLE4, children: renderBody4(phase) });
3357
3470
  }
3358
3471
  function renderBody4(phase) {
3359
3472
  switch (phase.kind) {
3360
3473
  case "running":
3361
- return /* @__PURE__ */ jsx29(Spinner, { label: "Writing requirements.md\u2026" });
3474
+ return /* @__PURE__ */ jsx30(Spinner, { label: "Writing requirements.md\u2026" });
3362
3475
  case "empty":
3363
- return /* @__PURE__ */ jsx29(
3476
+ return /* @__PURE__ */ jsx30(
3364
3477
  ResultCard,
3365
3478
  {
3366
3479
  kind: "warning",
@@ -3369,7 +3482,7 @@ function renderBody4(phase) {
3369
3482
  }
3370
3483
  );
3371
3484
  case "no-approved":
3372
- return /* @__PURE__ */ jsx29(
3485
+ return /* @__PURE__ */ jsx30(
3373
3486
  ResultCard,
3374
3487
  {
3375
3488
  kind: "warning",
@@ -3378,9 +3491,9 @@ function renderBody4(phase) {
3378
3491
  }
3379
3492
  );
3380
3493
  case "error":
3381
- return /* @__PURE__ */ jsx29(ResultCard, { kind: "error", title: "Could not export", lines: [phase.message] });
3494
+ return /* @__PURE__ */ jsx30(ResultCard, { kind: "error", title: "Could not export", lines: [phase.message] });
3382
3495
  case "done":
3383
- return /* @__PURE__ */ jsx29(
3496
+ return /* @__PURE__ */ jsx30(
3384
3497
  ResultCard,
3385
3498
  {
3386
3499
  kind: "success",
@@ -3398,8 +3511,8 @@ function renderBody4(phase) {
3398
3511
  // src/integration/ui/tui/views/workflows/context-export-view.tsx
3399
3512
  import { writeFile } from "fs/promises";
3400
3513
  import { join as join2 } from "path";
3401
- import { useMemo as useMemo11 } from "react";
3402
- import { jsx as jsx30 } from "react/jsx-runtime";
3514
+ import { useMemo as useMemo12 } from "react";
3515
+ import { jsx as jsx31 } from "react/jsx-runtime";
3403
3516
  var TITLE5 = "Export Context";
3404
3517
  var HINTS_RUNNING5 = [{ key: "Esc", action: "cancel" }];
3405
3518
  var HINTS_DONE5 = [
@@ -3428,18 +3541,18 @@ function ContextExportView({ sprintId }) {
3428
3541
  });
3429
3542
  }
3430
3543
  });
3431
- const hints = useMemo11(() => phase.kind === "running" ? HINTS_RUNNING5 : HINTS_DONE5, [phase.kind]);
3544
+ const hints = useMemo12(() => phase.kind === "running" ? HINTS_RUNNING5 : HINTS_DONE5, [phase.kind]);
3432
3545
  useViewHints(hints);
3433
- return /* @__PURE__ */ jsx30(ViewShell, { title: TITLE5, children: renderBody5(phase) });
3546
+ return /* @__PURE__ */ jsx31(ViewShell, { title: TITLE5, children: renderBody5(phase) });
3434
3547
  }
3435
3548
  function renderBody5(phase) {
3436
3549
  switch (phase.kind) {
3437
3550
  case "running":
3438
- return /* @__PURE__ */ jsx30(Spinner, { label: "Writing context.md\u2026" });
3551
+ return /* @__PURE__ */ jsx31(Spinner, { label: "Writing context.md\u2026" });
3439
3552
  case "error":
3440
- return /* @__PURE__ */ jsx30(ResultCard, { kind: "error", title: "Could not export context", lines: [phase.message] });
3553
+ return /* @__PURE__ */ jsx31(ResultCard, { kind: "error", title: "Could not export context", lines: [phase.message] });
3441
3554
  case "done":
3442
- return /* @__PURE__ */ jsx30(
3555
+ return /* @__PURE__ */ jsx31(
3443
3556
  ResultCard,
3444
3557
  {
3445
3558
  kind: "success",
@@ -3528,8 +3641,8 @@ async function renderContextMarkdown(sprint, tasks) {
3528
3641
  }
3529
3642
 
3530
3643
  // src/integration/ui/tui/views/workflows/ticket-add-view.tsx
3531
- import { useMemo as useMemo12 } from "react";
3532
- import { jsx as jsx31 } from "react/jsx-runtime";
3644
+ import { useMemo as useMemo13 } from "react";
3645
+ import { jsx as jsx32 } from "react/jsx-runtime";
3533
3646
  var TITLE6 = "Add Ticket";
3534
3647
  var HINTS_RUNNING6 = [{ key: "Esc", action: "cancel" }];
3535
3648
  var HINTS_DONE6 = [
@@ -3625,16 +3738,16 @@ function TicketAddView() {
3625
3738
  }
3626
3739
  }
3627
3740
  });
3628
- const hints = useMemo12(() => phase.kind === "running" ? HINTS_RUNNING6 : HINTS_DONE6, [phase.kind]);
3741
+ const hints = useMemo13(() => phase.kind === "running" ? HINTS_RUNNING6 : HINTS_DONE6, [phase.kind]);
3629
3742
  useViewHints(hints);
3630
- return /* @__PURE__ */ jsx31(ViewShell, { title: TITLE6, children: renderBody6(phase) });
3743
+ return /* @__PURE__ */ jsx32(ViewShell, { title: TITLE6, children: renderBody6(phase) });
3631
3744
  }
3632
3745
  function renderBody6(phase) {
3633
3746
  switch (phase.kind) {
3634
3747
  case "running":
3635
- return /* @__PURE__ */ jsx31(Spinner, { label: STEP_LABEL[phase.step] });
3748
+ return /* @__PURE__ */ jsx32(Spinner, { label: STEP_LABEL[phase.step] });
3636
3749
  case "no-draft-sprint":
3637
- return /* @__PURE__ */ jsx31(
3750
+ return /* @__PURE__ */ jsx32(
3638
3751
  ResultCard,
3639
3752
  {
3640
3753
  kind: "warning",
@@ -3644,7 +3757,7 @@ function renderBody6(phase) {
3644
3757
  }
3645
3758
  );
3646
3759
  case "no-project":
3647
- return /* @__PURE__ */ jsx31(
3760
+ return /* @__PURE__ */ jsx32(
3648
3761
  ResultCard,
3649
3762
  {
3650
3763
  kind: "warning",
@@ -3653,10 +3766,10 @@ function renderBody6(phase) {
3653
3766
  }
3654
3767
  );
3655
3768
  case "error":
3656
- return /* @__PURE__ */ jsx31(ResultCard, { kind: "error", title: "Could not add ticket", lines: [phase.message] });
3769
+ return /* @__PURE__ */ jsx32(ResultCard, { kind: "error", title: "Could not add ticket", lines: [phase.message] });
3657
3770
  case "done": {
3658
3771
  const title = phase.count > 1 ? `${String(phase.count)} tickets added` : phase.prefilled ? "Ticket added (prefilled from issue)" : "Ticket added";
3659
- return /* @__PURE__ */ jsx31(
3772
+ return /* @__PURE__ */ jsx32(
3660
3773
  ResultCard,
3661
3774
  {
3662
3775
  kind: "success",
@@ -3675,8 +3788,8 @@ function renderBody6(phase) {
3675
3788
  }
3676
3789
 
3677
3790
  // src/integration/ui/tui/views/workflows/ticket-edit-view.tsx
3678
- import { useMemo as useMemo13 } from "react";
3679
- import { jsx as jsx32 } from "react/jsx-runtime";
3791
+ import { useMemo as useMemo14 } from "react";
3792
+ import { jsx as jsx33 } from "react/jsx-runtime";
3680
3793
  var TITLE7 = "Edit Ticket";
3681
3794
  var HINTS_RUNNING7 = [{ key: "Esc", action: "cancel" }];
3682
3795
  var HINTS_DONE7 = [
@@ -3750,18 +3863,18 @@ function TicketEditView({ ticketId } = {}) {
3750
3863
  setPhase({ kind: "done", ticket, field });
3751
3864
  }
3752
3865
  });
3753
- const hints = useMemo13(() => phase.kind === "running" ? HINTS_RUNNING7 : HINTS_DONE7, [phase.kind]);
3866
+ const hints = useMemo14(() => phase.kind === "running" ? HINTS_RUNNING7 : HINTS_DONE7, [phase.kind]);
3754
3867
  useViewHints(hints);
3755
- return /* @__PURE__ */ jsx32(ViewShell, { title: TITLE7, children: renderBody7(phase) });
3868
+ return /* @__PURE__ */ jsx33(ViewShell, { title: TITLE7, children: renderBody7(phase) });
3756
3869
  }
3757
3870
  function renderBody7(phase) {
3758
3871
  switch (phase.kind) {
3759
3872
  case "running":
3760
- return /* @__PURE__ */ jsx32(Spinner, { label: stepLabel(phase.step) });
3873
+ return /* @__PURE__ */ jsx33(Spinner, { label: stepLabel(phase.step) });
3761
3874
  case "no-tickets":
3762
- return /* @__PURE__ */ jsx32(ResultCard, { kind: "info", title: "No tickets to edit" });
3875
+ return /* @__PURE__ */ jsx33(ResultCard, { kind: "info", title: "No tickets to edit" });
3763
3876
  case "no-draft-sprint":
3764
- return /* @__PURE__ */ jsx32(
3877
+ return /* @__PURE__ */ jsx33(
3765
3878
  ResultCard,
3766
3879
  {
3767
3880
  kind: "warning",
@@ -3770,9 +3883,9 @@ function renderBody7(phase) {
3770
3883
  }
3771
3884
  );
3772
3885
  case "error":
3773
- return /* @__PURE__ */ jsx32(ResultCard, { kind: "error", title: "Could not edit ticket", lines: [phase.message] });
3886
+ return /* @__PURE__ */ jsx33(ResultCard, { kind: "error", title: "Could not edit ticket", lines: [phase.message] });
3774
3887
  case "done":
3775
- return /* @__PURE__ */ jsx32(
3888
+ return /* @__PURE__ */ jsx33(
3776
3889
  ResultCard,
3777
3890
  {
3778
3891
  kind: "success",
@@ -3799,97 +3912,86 @@ function stepLabel(step) {
3799
3912
  }
3800
3913
 
3801
3914
  // src/integration/ui/tui/views/workflows/ticket-remove-view.tsx
3802
- import { useMemo as useMemo14 } from "react";
3803
- import { jsx as jsx33 } from "react/jsx-runtime";
3915
+ import { useEffect as useEffect16, useState as useState16 } from "react";
3916
+ import { jsx as jsx34 } from "react/jsx-runtime";
3804
3917
  var TITLE8 = "Remove Ticket";
3805
- var HINTS_RUNNING8 = [{ key: "Esc", action: "cancel" }];
3806
- var HINTS_DONE8 = [
3807
- { key: "Enter", action: "home" },
3808
- { key: "Esc", action: "back" }
3809
- ];
3810
3918
  function TicketRemoveView() {
3811
- const { phase } = useWorkflow({
3812
- initial: { kind: "running", step: "select" },
3813
- onError: (message) => ({ kind: "error", message }),
3814
- run: async ({ setPhase }) => {
3815
- const prompt = getPrompt();
3816
- const sprint = await getCurrentSprintOrThrow();
3817
- if (sprint.status !== "draft") {
3818
- setPhase({ kind: "no-draft-sprint" });
3819
- return;
3820
- }
3821
- const tickets = await listTickets();
3822
- if (tickets.length === 0) {
3823
- setPhase({ kind: "no-tickets" });
3824
- return;
3919
+ const router = useRouter();
3920
+ const [phase, setPhase] = useState16({ kind: "loading" });
3921
+ const [started, setStarted] = useState16(false);
3922
+ useEffect16(() => {
3923
+ if (started) return;
3924
+ setStarted(true);
3925
+ void (async () => {
3926
+ try {
3927
+ const sprint = await getCurrentSprintOrThrow();
3928
+ if (sprint.status !== "draft") {
3929
+ setPhase({ kind: "no-draft-sprint" });
3930
+ return;
3931
+ }
3932
+ const tickets = await listTickets();
3933
+ if (tickets.length === 0) {
3934
+ setPhase({ kind: "no-tickets" });
3935
+ return;
3936
+ }
3937
+ setPhase({ kind: "selecting" });
3938
+ const ticketId = await getPrompt().select({
3939
+ message: "Select ticket to remove:",
3940
+ choices: tickets.map((t) => ({
3941
+ label: `${t.id} \u2014 ${t.title}`,
3942
+ value: t.id,
3943
+ description: t.requirementStatus
3944
+ }))
3945
+ });
3946
+ const target = tickets.find((t) => t.id === ticketId);
3947
+ if (!target) throw new Error(`Ticket ${ticketId} disappeared`);
3948
+ setPhase({ kind: "ready", ticketId: target.id, ticketTitle: target.title });
3949
+ } catch (err) {
3950
+ if (err instanceof PromptCancelledError) {
3951
+ router.pop();
3952
+ return;
3953
+ }
3954
+ setPhase({ kind: "error", message: err instanceof Error ? err.message : String(err) });
3825
3955
  }
3826
- setPhase({ kind: "running", step: "select" });
3827
- const ticketId = await prompt.select({
3828
- message: "Select ticket to remove:",
3829
- choices: tickets.map((t) => ({
3830
- label: `${t.id} \u2014 ${t.title}`,
3831
- value: t.id,
3832
- description: t.requirementStatus
3833
- }))
3834
- });
3835
- const target = tickets.find((t) => t.id === ticketId);
3836
- if (!target) throw new Error(`Ticket ${ticketId} disappeared`);
3837
- setPhase({ kind: "running", step: "confirm" });
3838
- const ok = await prompt.confirm({
3839
- message: `Remove ticket "${target.title}"? This cannot be undone.`,
3840
- default: false
3841
- });
3842
- if (!ok) {
3843
- setPhase({ kind: "cancelled" });
3844
- return;
3956
+ })();
3957
+ }, [started, router]);
3958
+ if (phase.kind === "ready") {
3959
+ return /* @__PURE__ */ jsx34(
3960
+ RemovalWorkflow,
3961
+ {
3962
+ entityLabel: TITLE8,
3963
+ confirmMessage: `Remove ticket "${phase.ticketTitle}"? This cannot be undone.`,
3964
+ onConfirm: () => removeTicket(phase.ticketId),
3965
+ successMessage: `Ticket "${phase.ticketTitle}" removed`,
3966
+ onDone: () => {
3967
+ router.pop();
3968
+ }
3845
3969
  }
3846
- setPhase({ kind: "running", step: "removing" });
3847
- await removeTicket(ticketId);
3848
- setPhase({ kind: "done", id: target.id, title: target.title });
3849
- }
3850
- });
3851
- const hints = useMemo14(() => phase.kind === "running" ? HINTS_RUNNING8 : HINTS_DONE8, [phase.kind]);
3852
- useViewHints(hints);
3853
- return /* @__PURE__ */ jsx33(ViewShell, { title: TITLE8, children: renderBody8(phase) });
3970
+ );
3971
+ }
3972
+ return /* @__PURE__ */ jsx34(ViewShell, { title: TITLE8, children: renderPre2(phase) });
3854
3973
  }
3855
- function renderBody8(phase) {
3974
+ function renderPre2(phase) {
3856
3975
  switch (phase.kind) {
3857
- case "running":
3858
- return /* @__PURE__ */ jsx33(Spinner, { label: stepLabel2(phase.step) });
3976
+ case "loading":
3977
+ return /* @__PURE__ */ jsx34(Spinner, { label: "Loading tickets\u2026" });
3978
+ case "selecting":
3979
+ return /* @__PURE__ */ jsx34(Spinner, { label: "Awaiting ticket selection\u2026" });
3859
3980
  case "no-tickets":
3860
- return /* @__PURE__ */ jsx33(ResultCard, { kind: "info", title: "No tickets to remove" });
3981
+ return /* @__PURE__ */ jsx34(ResultCard, { kind: "info", title: "No tickets to remove" });
3861
3982
  case "no-draft-sprint":
3862
- return /* @__PURE__ */ jsx33(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
3863
- case "cancelled":
3864
- return /* @__PURE__ */ jsx33(ResultCard, { kind: "info", title: "Removal cancelled" });
3983
+ return /* @__PURE__ */ jsx34(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
3865
3984
  case "error":
3866
- return /* @__PURE__ */ jsx33(ResultCard, { kind: "error", title: "Could not remove ticket", lines: [phase.message] });
3867
- case "done":
3868
- return /* @__PURE__ */ jsx33(
3869
- ResultCard,
3870
- {
3871
- kind: "success",
3872
- title: "Ticket removed",
3873
- fields: [
3874
- ["ID", phase.id],
3875
- ["Title", phase.title]
3876
- ]
3877
- }
3878
- );
3985
+ return /* @__PURE__ */ jsx34(ResultCard, { kind: "error", title: "Could not remove ticket", lines: [phase.message] });
3879
3986
  }
3880
3987
  }
3881
- function stepLabel2(step) {
3882
- if (step === "select") return "Awaiting ticket selection\u2026";
3883
- if (step === "confirm") return "Awaiting confirmation\u2026";
3884
- return "Removing ticket\u2026";
3885
- }
3886
3988
 
3887
3989
  // src/integration/ui/tui/views/workflows/ticket-refine-view.tsx
3888
3990
  import { useMemo as useMemo15 } from "react";
3889
- import { jsx as jsx34 } from "react/jsx-runtime";
3991
+ import { jsx as jsx35 } from "react/jsx-runtime";
3890
3992
  var TITLE9 = "Re-Refine Ticket";
3891
- var HINTS_RUNNING9 = [{ key: "Esc", action: "cancel" }];
3892
- var HINTS_DONE9 = [
3993
+ var HINTS_RUNNING8 = [{ key: "Esc", action: "cancel" }];
3994
+ var HINTS_DONE8 = [
3893
3995
  { key: "Enter", action: "home" },
3894
3996
  { key: "Esc", action: "back" }
3895
3997
  ];
@@ -3921,18 +4023,18 @@ function TicketRefineView() {
3921
4023
  setPhase({ kind: "done", ticketTitle: target.title });
3922
4024
  }
3923
4025
  });
3924
- const hints = useMemo15(() => phase.kind === "running" ? HINTS_RUNNING9 : HINTS_DONE9, [phase.kind]);
4026
+ const hints = useMemo15(() => phase.kind === "running" ? HINTS_RUNNING8 : HINTS_DONE8, [phase.kind]);
3925
4027
  useViewHints(hints);
3926
- return /* @__PURE__ */ jsx34(ViewShell, { title: TITLE9, children: renderBody9(phase) });
4028
+ return /* @__PURE__ */ jsx35(ViewShell, { title: TITLE9, children: renderBody8(phase) });
3927
4029
  }
3928
- function renderBody9(phase) {
4030
+ function renderBody8(phase) {
3929
4031
  switch (phase.kind) {
3930
4032
  case "running":
3931
- return /* @__PURE__ */ jsx34(Spinner, { label: phase.step === "select" ? "Awaiting ticket selection\u2026" : "Running AI session\u2026" });
4033
+ return /* @__PURE__ */ jsx35(Spinner, { label: phase.step === "select" ? "Awaiting ticket selection\u2026" : "Running AI session\u2026" });
3932
4034
  case "no-draft-sprint":
3933
- return /* @__PURE__ */ jsx34(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
4035
+ return /* @__PURE__ */ jsx35(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
3934
4036
  case "no-approved":
3935
- return /* @__PURE__ */ jsx34(
4037
+ return /* @__PURE__ */ jsx35(
3936
4038
  ResultCard,
3937
4039
  {
3938
4040
  kind: "warning",
@@ -3941,9 +4043,9 @@ function renderBody9(phase) {
3941
4043
  }
3942
4044
  );
3943
4045
  case "error":
3944
- return /* @__PURE__ */ jsx34(ResultCard, { kind: "error", title: "Re-refinement failed", lines: [phase.message] });
4046
+ return /* @__PURE__ */ jsx35(ResultCard, { kind: "error", title: "Re-refinement failed", lines: [phase.message] });
3945
4047
  case "done":
3946
- return /* @__PURE__ */ jsx34(
4048
+ return /* @__PURE__ */ jsx35(
3947
4049
  ResultCard,
3948
4050
  {
3949
4051
  kind: "success",
@@ -3957,10 +4059,10 @@ function renderBody9(phase) {
3957
4059
 
3958
4060
  // src/integration/ui/tui/views/workflows/task-add-view.tsx
3959
4061
  import { useMemo as useMemo16 } from "react";
3960
- import { jsx as jsx35 } from "react/jsx-runtime";
4062
+ import { jsx as jsx36 } from "react/jsx-runtime";
3961
4063
  var TITLE10 = "Add Task";
3962
- var HINTS_RUNNING10 = [{ key: "Esc", action: "cancel" }];
3963
- var HINTS_DONE10 = [
4064
+ var HINTS_RUNNING9 = [{ key: "Esc", action: "cancel" }];
4065
+ var HINTS_DONE9 = [
3964
4066
  { key: "Enter", action: "home" },
3965
4067
  { key: "Esc", action: "back" }
3966
4068
  ];
@@ -4023,22 +4125,22 @@ function TaskAddView() {
4023
4125
  setPhase({ kind: "done", task, repo });
4024
4126
  }
4025
4127
  });
4026
- const hints = useMemo16(() => phase.kind === "running" ? HINTS_RUNNING10 : HINTS_DONE10, [phase.kind]);
4128
+ const hints = useMemo16(() => phase.kind === "running" ? HINTS_RUNNING9 : HINTS_DONE9, [phase.kind]);
4027
4129
  useViewHints(hints);
4028
- return /* @__PURE__ */ jsx35(ViewShell, { title: TITLE10, children: renderBody10(phase) });
4130
+ return /* @__PURE__ */ jsx36(ViewShell, { title: TITLE10, children: renderBody9(phase) });
4029
4131
  }
4030
- function renderBody10(phase) {
4132
+ function renderBody9(phase) {
4031
4133
  switch (phase.kind) {
4032
4134
  case "running":
4033
- return /* @__PURE__ */ jsx35(Spinner, { label: stepLabel3(phase.step) });
4135
+ return /* @__PURE__ */ jsx36(Spinner, { label: stepLabel2(phase.step) });
4034
4136
  case "no-project":
4035
- return /* @__PURE__ */ jsx35(ResultCard, { kind: "warning", title: "Sprint's project is missing or has no repos" });
4137
+ return /* @__PURE__ */ jsx36(ResultCard, { kind: "warning", title: "Sprint's project is missing or has no repos" });
4036
4138
  case "no-draft-sprint":
4037
- return /* @__PURE__ */ jsx35(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
4139
+ return /* @__PURE__ */ jsx36(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
4038
4140
  case "error":
4039
- return /* @__PURE__ */ jsx35(ResultCard, { kind: "error", title: "Could not add task", lines: [phase.message] });
4141
+ return /* @__PURE__ */ jsx36(ResultCard, { kind: "error", title: "Could not add task", lines: [phase.message] });
4040
4142
  case "done":
4041
- return /* @__PURE__ */ jsx35(
4143
+ return /* @__PURE__ */ jsx36(
4042
4144
  ResultCard,
4043
4145
  {
4044
4146
  kind: "success",
@@ -4053,7 +4155,7 @@ function renderBody10(phase) {
4053
4155
  );
4054
4156
  }
4055
4157
  }
4056
- function stepLabel3(step) {
4158
+ function stepLabel2(step) {
4057
4159
  if (step === "repo") return "Awaiting repo selection\u2026";
4058
4160
  if (step === "name") return "Awaiting task name\u2026";
4059
4161
  if (step === "description") return "Awaiting description\u2026";
@@ -4062,10 +4164,10 @@ function stepLabel3(step) {
4062
4164
 
4063
4165
  // src/integration/ui/tui/views/workflows/task-import-view.tsx
4064
4166
  import { useMemo as useMemo17 } from "react";
4065
- import { jsx as jsx36 } from "react/jsx-runtime";
4167
+ import { jsx as jsx37 } from "react/jsx-runtime";
4066
4168
  var TITLE11 = "Import Tasks";
4067
- var HINTS_RUNNING11 = [{ key: "Esc", action: "cancel" }];
4068
- var HINTS_DONE11 = [
4169
+ var HINTS_RUNNING10 = [{ key: "Esc", action: "cancel" }];
4170
+ var HINTS_DONE10 = [
4069
4171
  { key: "Enter", action: "home" },
4070
4172
  { key: "Esc", action: "back" }
4071
4173
  ];
@@ -4085,29 +4187,29 @@ function TaskImportView() {
4085
4187
  setPhase({ kind: "done" });
4086
4188
  }
4087
4189
  });
4088
- const hints = useMemo17(() => phase.kind === "running" ? HINTS_RUNNING11 : HINTS_DONE11, [phase.kind]);
4190
+ const hints = useMemo17(() => phase.kind === "running" ? HINTS_RUNNING10 : HINTS_DONE10, [phase.kind]);
4089
4191
  useViewHints(hints);
4090
- return /* @__PURE__ */ jsx36(ViewShell, { title: TITLE11, children: renderBody11(phase) });
4192
+ return /* @__PURE__ */ jsx37(ViewShell, { title: TITLE11, children: renderBody10(phase) });
4091
4193
  }
4092
- function renderBody11(phase) {
4194
+ function renderBody10(phase) {
4093
4195
  switch (phase.kind) {
4094
4196
  case "running":
4095
- return /* @__PURE__ */ jsx36(Spinner, { label: phase.step === "path" ? "Awaiting JSON file path\u2026" : "Importing tasks\u2026" });
4197
+ return /* @__PURE__ */ jsx37(Spinner, { label: phase.step === "path" ? "Awaiting JSON file path\u2026" : "Importing tasks\u2026" });
4096
4198
  case "done":
4097
- return /* @__PURE__ */ jsx36(ResultCard, { kind: "success", title: "Import finished", lines: ["Check the task list to see the result."] });
4199
+ return /* @__PURE__ */ jsx37(ResultCard, { kind: "success", title: "Import finished", lines: ["Check the task list to see the result."] });
4098
4200
  case "cancelled":
4099
- return /* @__PURE__ */ jsx36(ResultCard, { kind: "info", title: "Import cancelled" });
4201
+ return /* @__PURE__ */ jsx37(ResultCard, { kind: "info", title: "Import cancelled" });
4100
4202
  case "error":
4101
- return /* @__PURE__ */ jsx36(ResultCard, { kind: "error", title: "Import failed", lines: [phase.message] });
4203
+ return /* @__PURE__ */ jsx37(ResultCard, { kind: "error", title: "Import failed", lines: [phase.message] });
4102
4204
  }
4103
4205
  }
4104
4206
 
4105
4207
  // src/integration/ui/tui/views/workflows/task-status-view.tsx
4106
4208
  import { useMemo as useMemo18 } from "react";
4107
- import { jsx as jsx37 } from "react/jsx-runtime";
4209
+ import { jsx as jsx38 } from "react/jsx-runtime";
4108
4210
  var TITLE12 = "Task Status";
4109
- var HINTS_RUNNING12 = [{ key: "Esc", action: "cancel" }];
4110
- var HINTS_DONE12 = [
4211
+ var HINTS_RUNNING11 = [{ key: "Esc", action: "cancel" }];
4212
+ var HINTS_DONE11 = [
4111
4213
  { key: "Enter", action: "home" },
4112
4214
  { key: "Esc", action: "back" }
4113
4215
  ];
@@ -4150,18 +4252,18 @@ function TaskStatusView() {
4150
4252
  setPhase({ kind: "done", task });
4151
4253
  }
4152
4254
  });
4153
- const hints = useMemo18(() => phase.kind === "running" ? HINTS_RUNNING12 : HINTS_DONE12, [phase.kind]);
4255
+ const hints = useMemo18(() => phase.kind === "running" ? HINTS_RUNNING11 : HINTS_DONE11, [phase.kind]);
4154
4256
  useViewHints(hints);
4155
- return /* @__PURE__ */ jsx37(ViewShell, { title: TITLE12, children: renderBody12(phase) });
4257
+ return /* @__PURE__ */ jsx38(ViewShell, { title: TITLE12, children: renderBody11(phase) });
4156
4258
  }
4157
- function renderBody12(phase) {
4259
+ function renderBody11(phase) {
4158
4260
  switch (phase.kind) {
4159
4261
  case "running":
4160
- return /* @__PURE__ */ jsx37(Spinner, { label: stepLabel4(phase.step) });
4262
+ return /* @__PURE__ */ jsx38(Spinner, { label: stepLabel3(phase.step) });
4161
4263
  case "no-tasks":
4162
- return /* @__PURE__ */ jsx37(ResultCard, { kind: "info", title: "No tasks in this sprint" });
4264
+ return /* @__PURE__ */ jsx38(ResultCard, { kind: "info", title: "No tasks in this sprint" });
4163
4265
  case "not-active":
4164
- return /* @__PURE__ */ jsx37(
4266
+ return /* @__PURE__ */ jsx38(
4165
4267
  ResultCard,
4166
4268
  {
4167
4269
  kind: "warning",
@@ -4170,9 +4272,9 @@ function renderBody12(phase) {
4170
4272
  }
4171
4273
  );
4172
4274
  case "error":
4173
- return /* @__PURE__ */ jsx37(ResultCard, { kind: "error", title: "Could not update status", lines: [phase.message] });
4275
+ return /* @__PURE__ */ jsx38(ResultCard, { kind: "error", title: "Could not update status", lines: [phase.message] });
4174
4276
  case "done":
4175
- return /* @__PURE__ */ jsx37(
4277
+ return /* @__PURE__ */ jsx38(
4176
4278
  ResultCard,
4177
4279
  {
4178
4280
  kind: "success",
@@ -4185,7 +4287,7 @@ function renderBody12(phase) {
4185
4287
  );
4186
4288
  }
4187
4289
  }
4188
- function stepLabel4(step) {
4290
+ function stepLabel3(step) {
4189
4291
  if (step === "select-task") return "Awaiting task selection\u2026";
4190
4292
  if (step === "select-status") return "Awaiting status selection\u2026";
4191
4293
  return "Saving task\u2026";
@@ -4193,10 +4295,10 @@ function stepLabel4(step) {
4193
4295
 
4194
4296
  // src/integration/ui/tui/views/workflows/task-reorder-view.tsx
4195
4297
  import { useMemo as useMemo19 } from "react";
4196
- import { jsx as jsx38 } from "react/jsx-runtime";
4298
+ import { jsx as jsx39 } from "react/jsx-runtime";
4197
4299
  var TITLE13 = "Reorder Task";
4198
- var HINTS_RUNNING13 = [{ key: "Esc", action: "cancel" }];
4199
- var HINTS_DONE13 = [
4300
+ var HINTS_RUNNING12 = [{ key: "Esc", action: "cancel" }];
4301
+ var HINTS_DONE12 = [
4200
4302
  { key: "Enter", action: "home" },
4201
4303
  { key: "Esc", action: "back" }
4202
4304
  ];
@@ -4240,22 +4342,22 @@ function TaskReorderView() {
4240
4342
  setPhase({ kind: "done", task });
4241
4343
  }
4242
4344
  });
4243
- const hints = useMemo19(() => phase.kind === "running" ? HINTS_RUNNING13 : HINTS_DONE13, [phase.kind]);
4345
+ const hints = useMemo19(() => phase.kind === "running" ? HINTS_RUNNING12 : HINTS_DONE12, [phase.kind]);
4244
4346
  useViewHints(hints);
4245
- return /* @__PURE__ */ jsx38(ViewShell, { title: TITLE13, children: renderBody13(phase) });
4347
+ return /* @__PURE__ */ jsx39(ViewShell, { title: TITLE13, children: renderBody12(phase) });
4246
4348
  }
4247
- function renderBody13(phase) {
4349
+ function renderBody12(phase) {
4248
4350
  switch (phase.kind) {
4249
4351
  case "running":
4250
- return /* @__PURE__ */ jsx38(Spinner, { label: stepLabel5(phase.step) });
4352
+ return /* @__PURE__ */ jsx39(Spinner, { label: stepLabel4(phase.step) });
4251
4353
  case "no-tasks":
4252
- return /* @__PURE__ */ jsx38(ResultCard, { kind: "info", title: "No tasks in this sprint" });
4354
+ return /* @__PURE__ */ jsx39(ResultCard, { kind: "info", title: "No tasks in this sprint" });
4253
4355
  case "not-draft":
4254
- return /* @__PURE__ */ jsx38(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
4356
+ return /* @__PURE__ */ jsx39(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
4255
4357
  case "error":
4256
- return /* @__PURE__ */ jsx38(ResultCard, { kind: "error", title: "Could not reorder", lines: [phase.message] });
4358
+ return /* @__PURE__ */ jsx39(ResultCard, { kind: "error", title: "Could not reorder", lines: [phase.message] });
4257
4359
  case "done":
4258
- return /* @__PURE__ */ jsx38(
4360
+ return /* @__PURE__ */ jsx39(
4259
4361
  ResultCard,
4260
4362
  {
4261
4363
  kind: "success",
@@ -4268,100 +4370,89 @@ function renderBody13(phase) {
4268
4370
  );
4269
4371
  }
4270
4372
  }
4271
- function stepLabel5(step) {
4373
+ function stepLabel4(step) {
4272
4374
  if (step === "select") return "Awaiting task selection\u2026";
4273
4375
  if (step === "order") return "Awaiting new order\u2026";
4274
4376
  return "Saving task\u2026";
4275
4377
  }
4276
4378
 
4277
4379
  // src/integration/ui/tui/views/workflows/task-remove-view.tsx
4278
- import { useMemo as useMemo20 } from "react";
4279
- import { jsx as jsx39 } from "react/jsx-runtime";
4380
+ import { useEffect as useEffect17, useState as useState17 } from "react";
4381
+ import { jsx as jsx40 } from "react/jsx-runtime";
4280
4382
  var TITLE14 = "Remove Task";
4281
- var HINTS_RUNNING14 = [{ key: "Esc", action: "cancel" }];
4282
- var HINTS_DONE14 = [
4283
- { key: "Enter", action: "home" },
4284
- { key: "Esc", action: "back" }
4285
- ];
4286
4383
  function TaskRemoveView() {
4287
- const { phase } = useWorkflow({
4288
- initial: { kind: "running", step: "select" },
4289
- onError: (message) => ({ kind: "error", message }),
4290
- run: async ({ setPhase }) => {
4291
- const prompt = getPrompt();
4292
- const sprint = await getCurrentSprintOrThrow();
4293
- if (sprint.status !== "draft") {
4294
- setPhase({ kind: "not-draft" });
4295
- return;
4296
- }
4297
- const tasks = await listTasks();
4298
- if (tasks.length === 0) {
4299
- setPhase({ kind: "no-tasks" });
4300
- return;
4384
+ const router = useRouter();
4385
+ const [phase, setPhase] = useState17({ kind: "loading" });
4386
+ const [started, setStarted] = useState17(false);
4387
+ useEffect17(() => {
4388
+ if (started) return;
4389
+ setStarted(true);
4390
+ void (async () => {
4391
+ try {
4392
+ const sprint = await getCurrentSprintOrThrow();
4393
+ if (sprint.status !== "draft") {
4394
+ setPhase({ kind: "not-draft" });
4395
+ return;
4396
+ }
4397
+ const tasks = await listTasks();
4398
+ if (tasks.length === 0) {
4399
+ setPhase({ kind: "no-tasks" });
4400
+ return;
4401
+ }
4402
+ setPhase({ kind: "selecting" });
4403
+ const taskId = await getPrompt().select({
4404
+ message: "Select task to remove:",
4405
+ choices: tasks.map((t) => ({ label: `${t.id} \u2014 ${t.name}`, value: t.id, description: t.status }))
4406
+ });
4407
+ const target = tasks.find((t) => t.id === taskId);
4408
+ if (!target) throw new Error(`Task ${taskId} disappeared`);
4409
+ setPhase({ kind: "ready", taskId: target.id, taskName: target.name });
4410
+ } catch (err) {
4411
+ if (err instanceof PromptCancelledError) {
4412
+ router.pop();
4413
+ return;
4414
+ }
4415
+ setPhase({ kind: "error", message: err instanceof Error ? err.message : String(err) });
4301
4416
  }
4302
- setPhase({ kind: "running", step: "select" });
4303
- const taskId = await prompt.select({
4304
- message: "Select task to remove:",
4305
- choices: tasks.map((t) => ({ label: `${t.id} \u2014 ${t.name}`, value: t.id, description: t.status }))
4306
- });
4307
- const target = tasks.find((t) => t.id === taskId);
4308
- if (!target) throw new Error(`Task ${taskId} disappeared`);
4309
- setPhase({ kind: "running", step: "confirm" });
4310
- const ok = await prompt.confirm({
4311
- message: `Remove task "${target.name}"?`,
4312
- default: false
4313
- });
4314
- if (!ok) {
4315
- setPhase({ kind: "cancelled" });
4316
- return;
4417
+ })();
4418
+ }, [started, router]);
4419
+ if (phase.kind === "ready") {
4420
+ return /* @__PURE__ */ jsx40(
4421
+ RemovalWorkflow,
4422
+ {
4423
+ entityLabel: TITLE14,
4424
+ confirmMessage: `Remove task "${phase.taskName}"? This cannot be undone.`,
4425
+ onConfirm: () => removeTask(phase.taskId),
4426
+ successMessage: `Task "${phase.taskName}" removed`,
4427
+ onDone: () => {
4428
+ router.pop();
4429
+ }
4317
4430
  }
4318
- setPhase({ kind: "running", step: "removing" });
4319
- await removeTask(taskId);
4320
- setPhase({ kind: "done", id: target.id, name: target.name });
4321
- }
4322
- });
4323
- const hints = useMemo20(() => phase.kind === "running" ? HINTS_RUNNING14 : HINTS_DONE14, [phase.kind]);
4324
- useViewHints(hints);
4325
- return /* @__PURE__ */ jsx39(ViewShell, { title: TITLE14, children: renderBody14(phase) });
4431
+ );
4432
+ }
4433
+ return /* @__PURE__ */ jsx40(ViewShell, { title: TITLE14, children: renderPre3(phase) });
4326
4434
  }
4327
- function renderBody14(phase) {
4435
+ function renderPre3(phase) {
4328
4436
  switch (phase.kind) {
4329
- case "running":
4330
- return /* @__PURE__ */ jsx39(Spinner, { label: stepLabel6(phase.step) });
4437
+ case "loading":
4438
+ return /* @__PURE__ */ jsx40(Spinner, { label: "Loading tasks\u2026" });
4439
+ case "selecting":
4440
+ return /* @__PURE__ */ jsx40(Spinner, { label: "Awaiting task selection\u2026" });
4331
4441
  case "no-tasks":
4332
- return /* @__PURE__ */ jsx39(ResultCard, { kind: "info", title: "No tasks to remove" });
4442
+ return /* @__PURE__ */ jsx40(ResultCard, { kind: "info", title: "No tasks to remove" });
4333
4443
  case "not-draft":
4334
- return /* @__PURE__ */ jsx39(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
4335
- case "cancelled":
4336
- return /* @__PURE__ */ jsx39(ResultCard, { kind: "info", title: "Removal cancelled" });
4444
+ return /* @__PURE__ */ jsx40(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
4337
4445
  case "error":
4338
- return /* @__PURE__ */ jsx39(ResultCard, { kind: "error", title: "Could not remove task", lines: [phase.message] });
4339
- case "done":
4340
- return /* @__PURE__ */ jsx39(
4341
- ResultCard,
4342
- {
4343
- kind: "success",
4344
- title: "Task removed",
4345
- fields: [
4346
- ["ID", phase.id],
4347
- ["Name", phase.name]
4348
- ]
4349
- }
4350
- );
4446
+ return /* @__PURE__ */ jsx40(ResultCard, { kind: "error", title: "Could not remove task", lines: [phase.message] });
4351
4447
  }
4352
4448
  }
4353
- function stepLabel6(step) {
4354
- if (step === "select") return "Awaiting task selection\u2026";
4355
- if (step === "confirm") return "Awaiting confirmation\u2026";
4356
- return "Removing task\u2026";
4357
- }
4358
4449
 
4359
4450
  // src/integration/ui/tui/views/workflows/task-next-view.tsx
4360
- import { useMemo as useMemo21 } from "react";
4361
- import { jsx as jsx40 } from "react/jsx-runtime";
4451
+ import { useMemo as useMemo20 } from "react";
4452
+ import { jsx as jsx41 } from "react/jsx-runtime";
4362
4453
  var TITLE15 = "Next Task";
4363
- var HINTS_RUNNING15 = [{ key: "Esc", action: "cancel" }];
4364
- var HINTS_DONE15 = [
4454
+ var HINTS_RUNNING13 = [{ key: "Esc", action: "cancel" }];
4455
+ var HINTS_DONE13 = [
4365
4456
  { key: "Enter", action: "home" },
4366
4457
  { key: "Esc", action: "back" }
4367
4458
  ];
@@ -4378,20 +4469,20 @@ function TaskNextView() {
4378
4469
  setPhase({ kind: "ready", task });
4379
4470
  }
4380
4471
  });
4381
- const hints = useMemo21(() => phase.kind === "running" ? HINTS_RUNNING15 : HINTS_DONE15, [phase.kind]);
4472
+ const hints = useMemo20(() => phase.kind === "running" ? HINTS_RUNNING13 : HINTS_DONE13, [phase.kind]);
4382
4473
  useViewHints(hints);
4383
- return /* @__PURE__ */ jsx40(ViewShell, { title: TITLE15, children: renderBody15(phase) });
4474
+ return /* @__PURE__ */ jsx41(ViewShell, { title: TITLE15, children: renderBody13(phase) });
4384
4475
  }
4385
- function renderBody15(phase) {
4476
+ function renderBody13(phase) {
4386
4477
  switch (phase.kind) {
4387
4478
  case "running":
4388
- return /* @__PURE__ */ jsx40(Spinner, { label: "Resolving next task\u2026" });
4479
+ return /* @__PURE__ */ jsx41(Spinner, { label: "Resolving next task\u2026" });
4389
4480
  case "none":
4390
- return /* @__PURE__ */ jsx40(ResultCard, { kind: "info", title: "No task available", lines: ["All tasks are done or blocked."] });
4481
+ return /* @__PURE__ */ jsx41(ResultCard, { kind: "info", title: "No task available", lines: ["All tasks are done or blocked."] });
4391
4482
  case "error":
4392
- return /* @__PURE__ */ jsx40(ResultCard, { kind: "error", title: "Could not resolve next task", lines: [phase.message] });
4483
+ return /* @__PURE__ */ jsx41(ResultCard, { kind: "error", title: "Could not resolve next task", lines: [phase.message] });
4393
4484
  case "ready":
4394
- return /* @__PURE__ */ jsx40(
4485
+ return /* @__PURE__ */ jsx41(
4395
4486
  ResultCard,
4396
4487
  {
4397
4488
  kind: "info",
@@ -4410,11 +4501,55 @@ function renderBody15(phase) {
4410
4501
 
4411
4502
  // src/integration/ui/tui/views/workflows/project-add-view.tsx
4412
4503
  import { resolve } from "path";
4413
- import { useMemo as useMemo22 } from "react";
4414
- import { jsx as jsx41 } from "react/jsx-runtime";
4504
+ import { useMemo as useMemo21 } from "react";
4505
+
4506
+ // src/integration/ui/tui/views/workflows/collect-check-script.ts
4507
+ async function collectCheckScript(repoPath, setStep) {
4508
+ const prompt = getPrompt();
4509
+ setStep("check-script");
4510
+ let detection;
4511
+ try {
4512
+ detection = detectCheckScriptCandidates(repoPath);
4513
+ } catch {
4514
+ detection = null;
4515
+ }
4516
+ let suggested = detection ? suggestCheckScript(repoPath) : null;
4517
+ if (suggested === null) {
4518
+ const config = await getConfig();
4519
+ const enabled = config.aiCheckScriptDiscovery ?? getConfigDefaultValue("aiCheckScriptDiscovery");
4520
+ if (enabled && config.aiProvider) {
4521
+ const wantAi = await prompt.confirm({
4522
+ message: "No ecosystem detected. Ask AI to inspect the repo and suggest a check script?",
4523
+ default: true
4524
+ });
4525
+ if (wantAi) {
4526
+ setStep("discovering");
4527
+ const aiSession = new ProviderAiSessionAdapter();
4528
+ const signalParser = new SignalParser();
4529
+ const discoverR = await wrapAsync(async () => {
4530
+ await aiSession.ensureReady();
4531
+ return discoverCheckScriptWithAi(repoPath, aiSession, signalParser);
4532
+ }, ensureError);
4533
+ if (discoverR.ok && discoverR.value) {
4534
+ suggested = discoverR.value;
4535
+ }
4536
+ setStep("check-script");
4537
+ }
4538
+ }
4539
+ }
4540
+ const value = await prompt.input({
4541
+ message: "Check script (optional):",
4542
+ default: suggested ?? void 0
4543
+ });
4544
+ const trimmed = value.trim();
4545
+ return trimmed.length > 0 ? trimmed : void 0;
4546
+ }
4547
+
4548
+ // src/integration/ui/tui/views/workflows/project-add-view.tsx
4549
+ import { jsx as jsx42 } from "react/jsx-runtime";
4415
4550
  var TITLE16 = "Add Project";
4416
- var HINTS_RUNNING16 = [{ key: "Esc", action: "cancel" }];
4417
- var HINTS_DONE16 = [
4551
+ var HINTS_RUNNING14 = [{ key: "Esc", action: "cancel" }];
4552
+ var HINTS_DONE14 = [
4418
4553
  { key: "Enter", action: "home" },
4419
4554
  { key: "Esc", action: "back" }
4420
4555
  ];
@@ -4448,25 +4583,28 @@ function ProjectAddView() {
4448
4583
  });
4449
4584
  const absolute = resolve(repoPath.trim().replace(/^~(\/|$)/, `${process.env["HOME"] ?? ""}$1`));
4450
4585
  const repoName = absolute.split(/[\\/]/).pop() ?? "repo";
4586
+ const checkScript = await collectCheckScript(absolute, (next) => {
4587
+ setPhase({ kind: "running", step: next });
4588
+ });
4451
4589
  setPhase({ kind: "running", step: "saving" });
4452
4590
  const project = await createProject({
4453
4591
  name: name.trim(),
4454
4592
  displayName: display.trim(),
4455
- repositories: [{ name: repoName, path: absolute }]
4593
+ repositories: [{ name: repoName, path: absolute, ...checkScript ? { checkScript } : {} }]
4456
4594
  });
4457
4595
  setPhase({ kind: "done", project });
4458
4596
  }
4459
4597
  });
4460
- const hints = useMemo22(() => phase.kind === "running" ? HINTS_RUNNING16 : HINTS_DONE16, [phase.kind]);
4598
+ const hints = useMemo21(() => phase.kind === "running" ? HINTS_RUNNING14 : HINTS_DONE14, [phase.kind]);
4461
4599
  useViewHints(hints);
4462
- return /* @__PURE__ */ jsx41(ViewShell, { title: TITLE16, children: renderBody16(phase) });
4600
+ return /* @__PURE__ */ jsx42(ViewShell, { title: TITLE16, children: renderBody14(phase) });
4463
4601
  }
4464
- function renderBody16(phase) {
4602
+ function renderBody14(phase) {
4465
4603
  switch (phase.kind) {
4466
4604
  case "running":
4467
- return /* @__PURE__ */ jsx41(Spinner, { label: stepLabel7(phase.step) });
4605
+ return /* @__PURE__ */ jsx42(Spinner, { label: stepLabel5(phase.step) });
4468
4606
  case "error":
4469
- return /* @__PURE__ */ jsx41(
4607
+ return /* @__PURE__ */ jsx42(
4470
4608
  ResultCard,
4471
4609
  {
4472
4610
  kind: "error",
@@ -4475,7 +4613,7 @@ function renderBody16(phase) {
4475
4613
  }
4476
4614
  );
4477
4615
  case "done":
4478
- return /* @__PURE__ */ jsx41(
4616
+ return /* @__PURE__ */ jsx42(
4479
4617
  ResultCard,
4480
4618
  {
4481
4619
  kind: "success",
@@ -4490,87 +4628,88 @@ function renderBody16(phase) {
4490
4628
  );
4491
4629
  }
4492
4630
  }
4493
- function stepLabel7(step) {
4631
+ function stepLabel5(step) {
4494
4632
  if (step === "name") return "Awaiting project slug\u2026";
4495
4633
  if (step === "display") return "Awaiting display name\u2026";
4496
4634
  if (step === "repo-path") return "Awaiting repository path\u2026";
4635
+ if (step === "check-script") return "Awaiting check-script confirmation\u2026";
4636
+ if (step === "discovering") return "Discovering check script with AI\u2026";
4497
4637
  return "Saving project\u2026";
4498
4638
  }
4499
4639
 
4500
4640
  // src/integration/ui/tui/views/workflows/project-remove-view.tsx
4501
- import { useMemo as useMemo23 } from "react";
4502
- import { jsx as jsx42 } from "react/jsx-runtime";
4641
+ import { useEffect as useEffect18, useState as useState18 } from "react";
4642
+ import { jsx as jsx43 } from "react/jsx-runtime";
4503
4643
  var TITLE17 = "Remove Project";
4504
- var HINTS_RUNNING17 = [{ key: "Esc", action: "cancel" }];
4505
- var HINTS_DONE17 = [
4506
- { key: "Enter", action: "home" },
4507
- { key: "Esc", action: "back" }
4508
- ];
4509
4644
  function ProjectRemoveView() {
4510
- const { phase } = useWorkflow({
4511
- initial: { kind: "running", step: "select" },
4512
- onError: (message) => ({ kind: "error", message }),
4513
- run: async ({ setPhase }) => {
4514
- const prompt = getPrompt();
4515
- const projects = await listProjects();
4516
- if (projects.length === 0) {
4517
- setPhase({ kind: "no-projects" });
4518
- return;
4645
+ const router = useRouter();
4646
+ const [phase, setPhase] = useState18({ kind: "loading" });
4647
+ const [started, setStarted] = useState18(false);
4648
+ useEffect18(() => {
4649
+ if (started) return;
4650
+ setStarted(true);
4651
+ void (async () => {
4652
+ try {
4653
+ const projects = await listProjects();
4654
+ if (projects.length === 0) {
4655
+ setPhase({ kind: "no-projects" });
4656
+ return;
4657
+ }
4658
+ setPhase({ kind: "selecting" });
4659
+ const name = await getPrompt().select({
4660
+ message: "Select project to remove:",
4661
+ choices: projects.map((p) => ({
4662
+ label: `${p.displayName} (${p.name})`,
4663
+ value: p.name,
4664
+ description: `${String(p.repositories.length)} repo${p.repositories.length === 1 ? "" : "s"}`
4665
+ }))
4666
+ });
4667
+ setPhase({ kind: "ready", name });
4668
+ } catch (err) {
4669
+ if (err instanceof PromptCancelledError) {
4670
+ router.pop();
4671
+ return;
4672
+ }
4673
+ setPhase({ kind: "error", message: err instanceof Error ? err.message : String(err) });
4519
4674
  }
4520
- setPhase({ kind: "running", step: "select" });
4521
- const name = await prompt.select({
4522
- message: "Select project to remove:",
4523
- choices: projects.map((p) => ({
4524
- label: `${p.displayName} (${p.name})`,
4525
- value: p.name,
4526
- description: `${String(p.repositories.length)} repo${p.repositories.length === 1 ? "" : "s"}`
4527
- }))
4528
- });
4529
- setPhase({ kind: "running", step: "confirm" });
4530
- const ok = await prompt.confirm({
4531
- message: `Remove project "${name}"? This cannot be undone.`,
4532
- default: false
4533
- });
4534
- if (!ok) {
4535
- setPhase({ kind: "cancelled" });
4536
- return;
4675
+ })();
4676
+ }, [started, router]);
4677
+ if (phase.kind === "ready") {
4678
+ return /* @__PURE__ */ jsx43(
4679
+ RemovalWorkflow,
4680
+ {
4681
+ entityLabel: TITLE17,
4682
+ confirmMessage: `Remove project "${phase.name}"? This cannot be undone.`,
4683
+ onConfirm: () => removeProject(phase.name),
4684
+ successMessage: `Project "${phase.name}" removed`,
4685
+ onDone: () => {
4686
+ router.pop();
4687
+ }
4537
4688
  }
4538
- setPhase({ kind: "running", step: "removing" });
4539
- await removeProject(name);
4540
- setPhase({ kind: "done", name });
4541
- }
4542
- });
4543
- const hints = useMemo23(() => phase.kind === "running" ? HINTS_RUNNING17 : HINTS_DONE17, [phase.kind]);
4544
- useViewHints(hints);
4545
- return /* @__PURE__ */ jsx42(ViewShell, { title: TITLE17, children: renderBody17(phase) });
4689
+ );
4690
+ }
4691
+ return /* @__PURE__ */ jsx43(ViewShell, { title: TITLE17, children: renderPre4(phase) });
4546
4692
  }
4547
- function renderBody17(phase) {
4693
+ function renderPre4(phase) {
4548
4694
  switch (phase.kind) {
4549
- case "running":
4550
- return /* @__PURE__ */ jsx42(Spinner, { label: stepLabel8(phase.step) });
4695
+ case "loading":
4696
+ return /* @__PURE__ */ jsx43(Spinner, { label: "Loading projects\u2026" });
4697
+ case "selecting":
4698
+ return /* @__PURE__ */ jsx43(Spinner, { label: "Awaiting project selection\u2026" });
4551
4699
  case "no-projects":
4552
- return /* @__PURE__ */ jsx42(ResultCard, { kind: "info", title: "No projects to remove" });
4553
- case "cancelled":
4554
- return /* @__PURE__ */ jsx42(ResultCard, { kind: "info", title: "Removal cancelled" });
4700
+ return /* @__PURE__ */ jsx43(ResultCard, { kind: "info", title: "No projects to remove" });
4555
4701
  case "error":
4556
- return /* @__PURE__ */ jsx42(ResultCard, { kind: "error", title: "Could not remove project", lines: [phase.message] });
4557
- case "done":
4558
- return /* @__PURE__ */ jsx42(ResultCard, { kind: "success", title: "Project removed", fields: [["Name", phase.name]] });
4702
+ return /* @__PURE__ */ jsx43(ResultCard, { kind: "error", title: "Could not remove project", lines: [phase.message] });
4559
4703
  }
4560
4704
  }
4561
- function stepLabel8(step) {
4562
- if (step === "select") return "Awaiting project selection\u2026";
4563
- if (step === "confirm") return "Awaiting confirmation\u2026";
4564
- return "Removing project\u2026";
4565
- }
4566
4705
 
4567
4706
  // src/integration/ui/tui/views/workflows/project-repo-add-view.tsx
4568
4707
  import { resolve as resolve2 } from "path";
4569
- import { useMemo as useMemo24 } from "react";
4570
- import { jsx as jsx43 } from "react/jsx-runtime";
4708
+ import { useMemo as useMemo22 } from "react";
4709
+ import { jsx as jsx44 } from "react/jsx-runtime";
4571
4710
  var TITLE18 = "Add Repository";
4572
- var HINTS_RUNNING18 = [{ key: "Esc", action: "cancel" }];
4573
- var HINTS_DONE18 = [
4711
+ var HINTS_RUNNING15 = [{ key: "Esc", action: "cancel" }];
4712
+ var HINTS_DONE15 = [
4574
4713
  { key: "Enter", action: "home" },
4575
4714
  { key: "Esc", action: "back" }
4576
4715
  ];
@@ -4597,25 +4736,32 @@ function ProjectRepoAddView() {
4597
4736
  });
4598
4737
  const absolute = resolve2(repoPath.trim().replace(/^~(\/|$)/, `${process.env["HOME"] ?? ""}$1`));
4599
4738
  const repoName = absolute.split(/[\\/]/).pop() ?? "repo";
4739
+ const checkScript = await collectCheckScript(absolute, (next) => {
4740
+ setPhase({ kind: "running", step: next });
4741
+ });
4600
4742
  setPhase({ kind: "running", step: "saving" });
4601
- const project = await addProjectRepo(projectName, { name: repoName, path: absolute });
4743
+ const project = await addProjectRepo(projectName, {
4744
+ name: repoName,
4745
+ path: absolute,
4746
+ ...checkScript ? { checkScript } : {}
4747
+ });
4602
4748
  setPhase({ kind: "done", project, repoName });
4603
4749
  }
4604
4750
  });
4605
- const hints = useMemo24(() => phase.kind === "running" ? HINTS_RUNNING18 : HINTS_DONE18, [phase.kind]);
4751
+ const hints = useMemo22(() => phase.kind === "running" ? HINTS_RUNNING15 : HINTS_DONE15, [phase.kind]);
4606
4752
  useViewHints(hints);
4607
- return /* @__PURE__ */ jsx43(ViewShell, { title: TITLE18, children: renderBody18(phase) });
4753
+ return /* @__PURE__ */ jsx44(ViewShell, { title: TITLE18, children: renderBody15(phase) });
4608
4754
  }
4609
- function renderBody18(phase) {
4755
+ function renderBody15(phase) {
4610
4756
  switch (phase.kind) {
4611
4757
  case "running":
4612
- return /* @__PURE__ */ jsx43(Spinner, { label: stepLabel9(phase.step) });
4758
+ return /* @__PURE__ */ jsx44(Spinner, { label: stepLabel6(phase.step) });
4613
4759
  case "no-projects":
4614
- return /* @__PURE__ */ jsx43(ResultCard, { kind: "warning", title: "No projects to add a repository to" });
4760
+ return /* @__PURE__ */ jsx44(ResultCard, { kind: "warning", title: "No projects to add a repository to" });
4615
4761
  case "error":
4616
- return /* @__PURE__ */ jsx43(ResultCard, { kind: "error", title: "Could not add repository", lines: [phase.message] });
4762
+ return /* @__PURE__ */ jsx44(ResultCard, { kind: "error", title: "Could not add repository", lines: [phase.message] });
4617
4763
  case "done":
4618
- return /* @__PURE__ */ jsx43(
4764
+ return /* @__PURE__ */ jsx44(
4619
4765
  ResultCard,
4620
4766
  {
4621
4767
  kind: "success",
@@ -4629,106 +4775,106 @@ function renderBody18(phase) {
4629
4775
  );
4630
4776
  }
4631
4777
  }
4632
- function stepLabel9(step) {
4778
+ function stepLabel6(step) {
4633
4779
  if (step === "project") return "Awaiting project selection\u2026";
4634
4780
  if (step === "path") return "Awaiting repository path\u2026";
4781
+ if (step === "check-script") return "Awaiting check-script confirmation\u2026";
4782
+ if (step === "discovering") return "Discovering check script with AI\u2026";
4635
4783
  return "Saving repository\u2026";
4636
4784
  }
4637
4785
 
4638
4786
  // src/integration/ui/tui/views/workflows/project-repo-remove-view.tsx
4639
- import { useMemo as useMemo25 } from "react";
4640
- import { jsx as jsx44 } from "react/jsx-runtime";
4787
+ import { useEffect as useEffect19, useState as useState19 } from "react";
4788
+ import { jsx as jsx45 } from "react/jsx-runtime";
4641
4789
  var TITLE19 = "Remove Repository";
4642
- var HINTS_RUNNING19 = [{ key: "Esc", action: "cancel" }];
4643
- var HINTS_DONE19 = [
4644
- { key: "Enter", action: "home" },
4645
- { key: "Esc", action: "back" }
4646
- ];
4647
4790
  function ProjectRepoRemoveView() {
4648
- const { phase } = useWorkflow({
4649
- initial: { kind: "running", step: "project" },
4650
- onError: (message) => ({ kind: "error", message }),
4651
- run: async ({ setPhase }) => {
4652
- const prompt = getPrompt();
4653
- const projects = await listProjects();
4654
- if (projects.length === 0) {
4655
- setPhase({ kind: "no-projects" });
4656
- return;
4657
- }
4658
- setPhase({ kind: "running", step: "project" });
4659
- const projectName = projects.length === 1 && projects[0] ? projects[0].name : await prompt.select({
4660
- message: "Which project?",
4661
- choices: projects.map((p) => ({ label: p.displayName, value: p.name }))
4662
- });
4663
- const project = projects.find((p) => p.name === projectName);
4664
- if (!project || project.repositories.length === 0) {
4665
- setPhase({ kind: "no-repos" });
4666
- return;
4791
+ const router = useRouter();
4792
+ const [phase, setPhase] = useState19({ kind: "loading" });
4793
+ const [started, setStarted] = useState19(false);
4794
+ useEffect19(() => {
4795
+ if (started) return;
4796
+ setStarted(true);
4797
+ void (async () => {
4798
+ try {
4799
+ const prompt = getPrompt();
4800
+ const projects = await listProjects();
4801
+ if (projects.length === 0) {
4802
+ setPhase({ kind: "no-projects" });
4803
+ return;
4804
+ }
4805
+ setPhase({ kind: "selecting-project" });
4806
+ const projectName = projects.length === 1 && projects[0] ? projects[0].name : await prompt.select({
4807
+ message: "Which project?",
4808
+ choices: projects.map((p) => ({ label: p.displayName, value: p.name }))
4809
+ });
4810
+ const project = projects.find((p) => p.name === projectName);
4811
+ if (!project || project.repositories.length === 0) {
4812
+ setPhase({ kind: "no-repos" });
4813
+ return;
4814
+ }
4815
+ setPhase({ kind: "selecting-repo" });
4816
+ const repoPath = await prompt.select({
4817
+ message: "Repository to remove:",
4818
+ choices: project.repositories.map((r) => ({ label: r.name, value: r.path, description: r.path }))
4819
+ });
4820
+ const repoName = project.repositories.find((r) => r.path === repoPath)?.name ?? repoPath;
4821
+ setPhase({
4822
+ kind: "ready",
4823
+ projectName,
4824
+ projectDisplay: project.displayName,
4825
+ repoPath,
4826
+ repoName
4827
+ });
4828
+ } catch (err) {
4829
+ if (err instanceof PromptCancelledError) {
4830
+ router.pop();
4831
+ return;
4832
+ }
4833
+ setPhase({ kind: "error", message: err instanceof Error ? err.message : String(err) });
4667
4834
  }
4668
- setPhase({ kind: "running", step: "repo" });
4669
- const repoPath = await prompt.select({
4670
- message: "Repository to remove:",
4671
- choices: project.repositories.map((r) => ({ label: r.name, value: r.path, description: r.path }))
4672
- });
4673
- const repoName = project.repositories.find((r) => r.path === repoPath)?.name ?? repoPath;
4674
- setPhase({ kind: "running", step: "confirm" });
4675
- const ok = await prompt.confirm({
4676
- message: `Remove repository "${repoName}" from ${project.displayName}?`,
4677
- default: false
4678
- });
4679
- if (!ok) {
4680
- setPhase({ kind: "cancelled" });
4681
- return;
4835
+ })();
4836
+ }, [started, router]);
4837
+ if (phase.kind === "ready") {
4838
+ return /* @__PURE__ */ jsx45(
4839
+ RemovalWorkflow,
4840
+ {
4841
+ entityLabel: TITLE19,
4842
+ confirmMessage: `Remove repository "${phase.repoName}" from ${phase.projectDisplay}? This cannot be undone.`,
4843
+ onConfirm: async () => {
4844
+ await removeProjectRepo(phase.projectName, phase.repoPath);
4845
+ },
4846
+ successMessage: `Repository "${phase.repoName}" removed from ${phase.projectDisplay}`,
4847
+ onDone: () => {
4848
+ router.pop();
4849
+ }
4682
4850
  }
4683
- setPhase({ kind: "running", step: "removing" });
4684
- const updated = await removeProjectRepo(projectName, repoPath);
4685
- setPhase({ kind: "done", project: updated, repoName });
4686
- }
4687
- });
4688
- const hints = useMemo25(() => phase.kind === "running" ? HINTS_RUNNING19 : HINTS_DONE19, [phase.kind]);
4689
- useViewHints(hints);
4690
- return /* @__PURE__ */ jsx44(ViewShell, { title: TITLE19, children: renderBody19(phase) });
4851
+ );
4852
+ }
4853
+ return /* @__PURE__ */ jsx45(ViewShell, { title: TITLE19, children: renderPre5(phase) });
4691
4854
  }
4692
- function renderBody19(phase) {
4855
+ function renderPre5(phase) {
4693
4856
  switch (phase.kind) {
4694
- case "running":
4695
- return /* @__PURE__ */ jsx44(Spinner, { label: stepLabel10(phase.step) });
4857
+ case "loading":
4858
+ return /* @__PURE__ */ jsx45(Spinner, { label: "Loading projects\u2026" });
4859
+ case "selecting-project":
4860
+ return /* @__PURE__ */ jsx45(Spinner, { label: "Awaiting project selection\u2026" });
4861
+ case "selecting-repo":
4862
+ return /* @__PURE__ */ jsx45(Spinner, { label: "Awaiting repository selection\u2026" });
4696
4863
  case "no-projects":
4697
- return /* @__PURE__ */ jsx44(ResultCard, { kind: "info", title: "No projects" });
4864
+ return /* @__PURE__ */ jsx45(ResultCard, { kind: "info", title: "No projects" });
4698
4865
  case "no-repos":
4699
- return /* @__PURE__ */ jsx44(ResultCard, { kind: "info", title: "Project has no repositories" });
4700
- case "cancelled":
4701
- return /* @__PURE__ */ jsx44(ResultCard, { kind: "info", title: "Removal cancelled" });
4866
+ return /* @__PURE__ */ jsx45(ResultCard, { kind: "info", title: "Project has no repositories" });
4702
4867
  case "error":
4703
- return /* @__PURE__ */ jsx44(ResultCard, { kind: "error", title: "Could not remove repository", lines: [phase.message] });
4704
- case "done":
4705
- return /* @__PURE__ */ jsx44(
4706
- ResultCard,
4707
- {
4708
- kind: "success",
4709
- title: "Repository removed",
4710
- fields: [
4711
- ["Project", phase.project.displayName],
4712
- ["Repo", phase.repoName],
4713
- ["Remaining", String(phase.project.repositories.length)]
4714
- ]
4715
- }
4716
- );
4717
- }
4718
- }
4719
- function stepLabel10(step) {
4720
- if (step === "project") return "Awaiting project selection\u2026";
4721
- if (step === "repo") return "Awaiting repository selection\u2026";
4722
- if (step === "confirm") return "Awaiting confirmation\u2026";
4723
- return "Removing repository\u2026";
4868
+ return /* @__PURE__ */ jsx45(ResultCard, { kind: "error", title: "Could not remove repository", lines: [phase.message] });
4869
+ }
4724
4870
  }
4725
4871
 
4726
4872
  // src/integration/ui/tui/views/workflows/project-edit-view.tsx
4727
- import { useMemo as useMemo26 } from "react";
4728
- import { jsx as jsx45 } from "react/jsx-runtime";
4873
+ import { useMemo as useMemo23 } from "react";
4874
+ import { jsx as jsx46 } from "react/jsx-runtime";
4729
4875
  var TITLE20 = "Edit Project";
4730
- var HINTS_RUNNING20 = [{ key: "Esc", action: "cancel" }];
4731
- var HINTS_DONE20 = [
4876
+ var HINTS_RUNNING16 = [{ key: "Esc", action: "cancel" }];
4877
+ var HINTS_DONE16 = [
4732
4878
  { key: "Enter", action: "home" },
4733
4879
  { key: "Esc", action: "back" }
4734
4880
  ];
@@ -4770,20 +4916,20 @@ function ProjectEditView() {
4770
4916
  setPhase({ kind: "done", project });
4771
4917
  }
4772
4918
  });
4773
- const hints = useMemo26(() => phase.kind === "running" ? HINTS_RUNNING20 : HINTS_DONE20, [phase.kind]);
4919
+ const hints = useMemo23(() => phase.kind === "running" ? HINTS_RUNNING16 : HINTS_DONE16, [phase.kind]);
4774
4920
  useViewHints(hints);
4775
- return /* @__PURE__ */ jsx45(ViewShell, { title: TITLE20, children: renderBody20(phase) });
4921
+ return /* @__PURE__ */ jsx46(ViewShell, { title: TITLE20, children: renderBody16(phase) });
4776
4922
  }
4777
- function renderBody20(phase) {
4923
+ function renderBody16(phase) {
4778
4924
  switch (phase.kind) {
4779
4925
  case "running":
4780
- return /* @__PURE__ */ jsx45(Spinner, { label: stepLabel11(phase.step) });
4926
+ return /* @__PURE__ */ jsx46(Spinner, { label: stepLabel7(phase.step) });
4781
4927
  case "no-projects":
4782
- return /* @__PURE__ */ jsx45(ResultCard, { kind: "info", title: "No projects registered" });
4928
+ return /* @__PURE__ */ jsx46(ResultCard, { kind: "info", title: "No projects registered" });
4783
4929
  case "error":
4784
- return /* @__PURE__ */ jsx45(ResultCard, { kind: "error", title: "Could not update project", lines: [phase.message] });
4930
+ return /* @__PURE__ */ jsx46(ResultCard, { kind: "error", title: "Could not update project", lines: [phase.message] });
4785
4931
  case "done":
4786
- return /* @__PURE__ */ jsx45(
4932
+ return /* @__PURE__ */ jsx46(
4787
4933
  ResultCard,
4788
4934
  {
4789
4935
  kind: "success",
@@ -4797,21 +4943,116 @@ function renderBody20(phase) {
4797
4943
  );
4798
4944
  }
4799
4945
  }
4800
- function stepLabel11(step) {
4946
+ function stepLabel7(step) {
4801
4947
  if (step === "select") return "Awaiting project selection\u2026";
4802
4948
  if (step === "display") return "Awaiting display name\u2026";
4803
4949
  if (step === "description") return "Awaiting description\u2026";
4804
4950
  return "Saving project\u2026";
4805
4951
  }
4806
4952
 
4953
+ // src/integration/ui/tui/views/workflows/project-onboard-view.tsx
4954
+ import { useMemo as useMemo24 } from "react";
4955
+ import { jsx as jsx47 } from "react/jsx-runtime";
4956
+ var TITLE21 = "Onboard Repository";
4957
+ var HINTS_RUNNING17 = [{ key: "Esc", action: "cancel" }];
4958
+ var HINTS_DONE17 = [
4959
+ { key: "Enter", action: "home" },
4960
+ { key: "Esc", action: "back" }
4961
+ ];
4962
+ function ProjectOnboardView({
4963
+ projectName: preselectedProject,
4964
+ repo: preselectedRepo
4965
+ } = {}) {
4966
+ const { phase } = useWorkflow({
4967
+ initial: preselectedProject ? { kind: "running", step: "onboarding" } : { kind: "running", step: "select-project" },
4968
+ onError: (message) => ({ kind: "error", message }),
4969
+ run: async ({ setPhase }) => {
4970
+ const shared = getSharedDeps();
4971
+ const prompt = shared.prompt;
4972
+ const projects = await listProjects();
4973
+ if (projects.length === 0) {
4974
+ setPhase({ kind: "no-projects" });
4975
+ return;
4976
+ }
4977
+ let projectName;
4978
+ if (preselectedProject) {
4979
+ projectName = preselectedProject;
4980
+ } else {
4981
+ setPhase({ kind: "running", step: "select-project" });
4982
+ projectName = await prompt.select({
4983
+ message: "Select a project to onboard:",
4984
+ choices: projects.map((p) => ({
4985
+ label: `${p.displayName} (${p.name})`,
4986
+ value: p.name,
4987
+ description: `${String(p.repositories.length)} repo${p.repositories.length === 1 ? "" : "s"}`
4988
+ }))
4989
+ });
4990
+ }
4991
+ setPhase({ kind: "running", step: "onboarding" });
4992
+ const options = preselectedRepo ? { repo: preselectedRepo } : {};
4993
+ const pipeline = createOnboardPipeline(shared, options);
4994
+ const initialContext = { sprintId: "", projectName };
4995
+ const result = await executePipeline(pipeline, initialContext);
4996
+ if (!result.ok) {
4997
+ setPhase({ kind: "error", message: result.error.message });
4998
+ return;
4999
+ }
5000
+ const ctx = result.value.context;
5001
+ const agentsMd = ctx.agentsMdFinal ?? ctx.agentsMdDraft ?? "";
5002
+ setPhase({
5003
+ kind: "done",
5004
+ projectName,
5005
+ writtenPath: ctx.writtenPath,
5006
+ checkScript: ctx.checkScriptFinal ?? null,
5007
+ driftWarnings: ctx.driftWarnings ?? [],
5008
+ lowConfidence: agentsMd.includes("LOW-CONFIDENCE:"),
5009
+ alreadyCurrent: ctx.alreadyCurrent
5010
+ });
5011
+ }
5012
+ });
5013
+ const hints = useMemo24(() => phase.kind === "running" ? HINTS_RUNNING17 : HINTS_DONE17, [phase.kind]);
5014
+ useViewHints(hints);
5015
+ return /* @__PURE__ */ jsx47(ViewShell, { title: TITLE21, children: renderBody17(phase) });
5016
+ }
5017
+ function renderBody17(phase) {
5018
+ switch (phase.kind) {
5019
+ case "running":
5020
+ return /* @__PURE__ */ jsx47(Spinner, { label: stepLabel8(phase.step) });
5021
+ case "no-projects":
5022
+ return /* @__PURE__ */ jsx47(ResultCard, { kind: "info", title: "No projects registered" });
5023
+ case "error":
5024
+ return /* @__PURE__ */ jsx47(ResultCard, { kind: "error", title: "Onboarding failed", lines: [phase.message] });
5025
+ case "done": {
5026
+ const fields = [["Project", phase.projectName]];
5027
+ if (phase.writtenPath) fields.push(["Project context file", phase.writtenPath]);
5028
+ if (phase.checkScript) fields.push(["Check script", phase.checkScript]);
5029
+ if (phase.driftWarnings && phase.driftWarnings.length > 0) {
5030
+ fields.push(["Warnings", phase.driftWarnings.join("; ")]);
5031
+ }
5032
+ if (phase.lowConfidence) {
5033
+ fields.push(["Review", "Low-confidence sections present \u2014 re-run interactively to tighten"]);
5034
+ }
5035
+ if (phase.alreadyCurrent) {
5036
+ return /* @__PURE__ */ jsx47(ResultCard, { kind: "info", title: "Already current", fields });
5037
+ }
5038
+ const nextSteps = phase.lowConfidence ? [{ action: "Open the project context file and replace LOW-CONFIDENCE lines with concrete facts." }] : void 0;
5039
+ return /* @__PURE__ */ jsx47(ResultCard, { kind: "success", title: "Repository onboarded", fields, nextSteps });
5040
+ }
5041
+ }
5042
+ }
5043
+ function stepLabel8(step) {
5044
+ if (step === "select-project") return "Awaiting project selection\u2026";
5045
+ return "Inventorying repository and reviewing proposal\u2026";
5046
+ }
5047
+
4807
5048
  // src/integration/ui/tui/views/browse/sprint-list-view.tsx
4808
- import { useEffect as useEffect14, useMemo as useMemo28, useState as useState15 } from "react";
4809
- import { useInput as useInput11 } from "ink";
5049
+ import { useEffect as useEffect21, useMemo as useMemo26, useState as useState21 } from "react";
5050
+ import { useInput as useInput13 } from "ink";
4810
5051
 
4811
5052
  // src/integration/ui/tui/components/list-view.tsx
4812
- import React47, { useMemo as useMemo27, useState as useState14 } from "react";
4813
- import { Box as Box25, Text as Text24, useInput as useInput10 } from "ink";
4814
- import { jsx as jsx46, jsxs as jsxs24 } from "react/jsx-runtime";
5053
+ import React49, { useEffect as useEffect20, useMemo as useMemo25, useState as useState20 } from "react";
5054
+ import { Box as Box25, Text as Text24, useInput as useInput12 } from "ink";
5055
+ import { jsx as jsx48, jsxs as jsxs24 } from "react/jsx-runtime";
4815
5056
  var DEFAULT_PAGE_SIZE = 12;
4816
5057
  function computeWidths(columns, rows, totalWidth) {
4817
5058
  const widths = columns.map((col) => {
@@ -4840,11 +5081,17 @@ function ListView({
4840
5081
  emptyLabel = "(empty)",
4841
5082
  pageSize = DEFAULT_PAGE_SIZE,
4842
5083
  initialCursor = 0,
4843
- disabled = false
5084
+ disabled = false,
5085
+ onCursorChange
4844
5086
  }) {
4845
- const [cursor, setCursor] = useState14(() => Math.max(0, Math.min(initialCursor, rows.length - 1)));
4846
- const widths = useMemo27(() => computeWidths(columns, rows, 72), [columns, rows]);
4847
- useInput10(
5087
+ const [cursor, setCursor] = useState20(() => Math.max(0, Math.min(initialCursor, rows.length - 1)));
5088
+ useEffect20(() => {
5089
+ if (!onCursorChange) return;
5090
+ const row = rows[cursor];
5091
+ if (row !== void 0) onCursorChange(row, cursor);
5092
+ }, [cursor, rows, onCursorChange]);
5093
+ const widths = useMemo25(() => computeWidths(columns, rows, 72), [columns, rows]);
5094
+ useInput12(
4848
5095
  (_input, key) => {
4849
5096
  if (rows.length === 0) return;
4850
5097
  if (key.upArrow) setCursor((c) => Math.max(0, c - 1));
@@ -4859,17 +5106,17 @@ function ListView({
4859
5106
  { isActive: !disabled }
4860
5107
  );
4861
5108
  if (rows.length === 0) {
4862
- return /* @__PURE__ */ jsx46(Box25, { children: /* @__PURE__ */ jsx46(Text24, { dimColor: true, children: emptyLabel }) });
5109
+ return /* @__PURE__ */ jsx48(Box25, { children: /* @__PURE__ */ jsx48(Text24, { dimColor: true, children: emptyLabel }) });
4863
5110
  }
4864
5111
  const windowStart = Math.max(0, Math.min(cursor - Math.floor(pageSize / 2), rows.length - pageSize));
4865
5112
  const windowEnd = Math.min(rows.length, windowStart + pageSize);
4866
5113
  const visible = rows.slice(windowStart, windowEnd);
4867
5114
  return /* @__PURE__ */ jsxs24(Box25, { flexDirection: "column", children: [
4868
5115
  /* @__PURE__ */ jsxs24(Box25, { children: [
4869
- /* @__PURE__ */ jsx46(Text24, { color: inkColors.muted, bold: true, children: " " }),
4870
- columns.map((col, i) => /* @__PURE__ */ jsxs24(React47.Fragment, { children: [
4871
- /* @__PURE__ */ jsx46(Text24, { color: inkColors.muted, bold: true, children: pad(col.header.toUpperCase(), widths[i] ?? col.header.length, col.align) }),
4872
- i < columns.length - 1 ? /* @__PURE__ */ jsx46(Text24, { color: inkColors.muted, children: " " }) : null
5116
+ /* @__PURE__ */ jsx48(Text24, { color: inkColors.muted, bold: true, children: " " }),
5117
+ columns.map((col, i) => /* @__PURE__ */ jsxs24(React49.Fragment, { children: [
5118
+ /* @__PURE__ */ jsx48(Text24, { color: inkColors.muted, bold: true, children: pad(col.header.toUpperCase(), widths[i] ?? col.header.length, col.align) }),
5119
+ i < columns.length - 1 ? /* @__PURE__ */ jsx48(Text24, { color: inkColors.muted, children: " " }) : null
4873
5120
  ] }, col.header))
4874
5121
  ] }),
4875
5122
  visible.map((row, i) => {
@@ -4877,18 +5124,18 @@ function ListView({
4877
5124
  const selected = absoluteIdx === cursor;
4878
5125
  const indicatorColor = selected ? inkColors.highlight : void 0;
4879
5126
  return /* @__PURE__ */ jsxs24(Box25, { children: [
4880
- /* @__PURE__ */ jsx46(Text24, { color: indicatorColor, bold: selected, children: selected ? glyphs.actionCursor : " " }),
5127
+ /* @__PURE__ */ jsx48(Text24, { color: indicatorColor, bold: selected, children: selected ? glyphs.actionCursor : " " }),
4881
5128
  columns.map((col, ci) => {
4882
5129
  const text = pad(col.cell(row), widths[ci] ?? col.cell(row).length, col.align);
4883
5130
  const cellColor = col.color?.(row) ?? (selected ? inkColors.highlight : void 0);
4884
- return /* @__PURE__ */ jsxs24(React47.Fragment, { children: [
4885
- /* @__PURE__ */ jsx46(Text24, { color: cellColor, bold: selected && ci === 0, children: text }),
4886
- ci < columns.length - 1 ? /* @__PURE__ */ jsx46(Text24, { children: " " }) : null
5131
+ return /* @__PURE__ */ jsxs24(React49.Fragment, { children: [
5132
+ /* @__PURE__ */ jsx48(Text24, { color: cellColor, bold: selected && ci === 0, children: text }),
5133
+ ci < columns.length - 1 ? /* @__PURE__ */ jsx48(Text24, { children: " " }) : null
4887
5134
  ] }, col.header);
4888
5135
  })
4889
5136
  ] }, absoluteIdx);
4890
5137
  }),
4891
- rows.length > pageSize ? /* @__PURE__ */ jsx46(Box25, { marginTop: spacing.section, children: /* @__PURE__ */ jsxs24(Text24, { dimColor: true, children: [
5138
+ rows.length > pageSize ? /* @__PURE__ */ jsx48(Box25, { marginTop: spacing.section, children: /* @__PURE__ */ jsxs24(Text24, { dimColor: true, children: [
4892
5139
  String(cursor + 1),
4893
5140
  " / ",
4894
5141
  String(rows.length),
@@ -4920,6 +5167,12 @@ function chipKindForTaskStatus(status) {
4920
5167
  if (status === "in_progress") return "warning";
4921
5168
  return "muted";
4922
5169
  }
5170
+ function chipKindForExecutionStatus(status) {
5171
+ if (status === "running") return "warning";
5172
+ if (status === "completed") return "success";
5173
+ if (status === "failed") return "error";
5174
+ return "muted";
5175
+ }
4923
5176
  function StatusChip({ label, kind = "info" }) {
4924
5177
  return /* @__PURE__ */ jsxs25(Text25, { color: COLOR2[kind], bold: true, children: [
4925
5178
  "[",
@@ -4929,7 +5182,7 @@ function StatusChip({ label, kind = "info" }) {
4929
5182
  }
4930
5183
 
4931
5184
  // src/integration/ui/tui/views/browse/sprint-list-view.tsx
4932
- import { jsx as jsx47 } from "react/jsx-runtime";
5185
+ import { jsx as jsx49 } from "react/jsx-runtime";
4933
5186
  function statusChipText(status) {
4934
5187
  return `[${status.toUpperCase()}]`;
4935
5188
  }
@@ -4987,7 +5240,7 @@ var HINTS_READY2 = [
4987
5240
  { key: "f", action: "filter" },
4988
5241
  { key: "n", action: "new" },
4989
5242
  { key: "c", action: "set current" },
4990
- { key: "x", action: "delete" }
5243
+ { key: "r", action: "remove" }
4991
5244
  ];
4992
5245
  var HINTS_EMPTY = [{ key: "n", action: "new" }];
4993
5246
  var FILTER_CYCLE = ["all", "draft", "active", "closed"];
@@ -4997,10 +5250,10 @@ function nextFilter(current) {
4997
5250
  }
4998
5251
  function SprintListView() {
4999
5252
  const router = useRouter();
5000
- const [state, setState] = useState15({ kind: "loading" });
5001
- const [filter, setFilter] = useState15("all");
5253
+ const [state, setState] = useState21({ kind: "loading" });
5254
+ const [filter, setFilter] = useState21("all");
5002
5255
  useViewHints(state.kind === "ready" ? HINTS_READY2 : HINTS_EMPTY);
5003
- useEffect14(() => {
5256
+ useEffect21(() => {
5004
5257
  const ctl = { cancelled: false };
5005
5258
  void (async () => {
5006
5259
  try {
@@ -5036,7 +5289,7 @@ function SprintListView() {
5036
5289
  ctl.cancelled = true;
5037
5290
  };
5038
5291
  }, []);
5039
- useInput11((input) => {
5292
+ useInput13((input) => {
5040
5293
  if (state.kind === "loading") return;
5041
5294
  if (input === "n") {
5042
5295
  router.push({ id: "sprint-create" });
@@ -5051,17 +5304,17 @@ function SprintListView() {
5051
5304
  router.push({ id: "sprint-set-current" });
5052
5305
  return;
5053
5306
  }
5054
- if (input === "x") {
5307
+ if (input === "r") {
5055
5308
  router.push({ id: "sprint-delete" });
5056
5309
  }
5057
5310
  });
5058
5311
  const title = filter === "all" ? TITLE_BASE : `${TITLE_BASE} \xB7 filter: ${filter}`;
5059
- const filtered = useMemo28(() => {
5312
+ const filtered = useMemo26(() => {
5060
5313
  if (state.kind !== "ready") return [];
5061
5314
  if (filter === "all") return state.sprints;
5062
5315
  return state.sprints.filter((s) => s.status === filter);
5063
5316
  }, [state, filter]);
5064
- return /* @__PURE__ */ jsx47(ViewShell, { title, children: state.kind === "loading" ? /* @__PURE__ */ jsx47(Spinner, { label: "Loading sprints\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx47(ResultCard, { kind: "info", title: "No sprints yet", lines: ["Press `n` to create one."] }) : state.kind === "error" ? /* @__PURE__ */ jsx47(ResultCard, { kind: "error", title: "Could not load sprints", lines: [state.message] }) : filtered.length === 0 ? /* @__PURE__ */ jsx47(ResultCard, { kind: "info", title: `No sprints with status '${filter}'`, lines: ["Press f to cycle the filter."] }) : /* @__PURE__ */ jsx47(
5317
+ return /* @__PURE__ */ jsx49(ViewShell, { title, children: state.kind === "loading" ? /* @__PURE__ */ jsx49(Spinner, { label: "Loading sprints\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx49(ResultCard, { kind: "info", title: "No sprints yet", lines: ["Press `n` to create one."] }) : state.kind === "error" ? /* @__PURE__ */ jsx49(ResultCard, { kind: "error", title: "Could not load sprints", lines: [state.message] }) : filtered.length === 0 ? /* @__PURE__ */ jsx49(ResultCard, { kind: "info", title: `No sprints with status '${filter}'`, lines: ["Press f to cycle the filter."] }) : /* @__PURE__ */ jsx49(
5065
5318
  ListView,
5066
5319
  {
5067
5320
  rows: filtered,
@@ -5075,19 +5328,19 @@ function SprintListView() {
5075
5328
  }
5076
5329
 
5077
5330
  // src/integration/ui/tui/views/browse/sprint-show-view.tsx
5078
- import { useEffect as useEffect15, useMemo as useMemo29, useState as useState16 } from "react";
5079
- import { Box as Box26, Text as Text26, useInput as useInput12 } from "ink";
5080
- import { jsx as jsx48, jsxs as jsxs26 } from "react/jsx-runtime";
5081
- var TITLE21 = "Sprint Details";
5331
+ import { useEffect as useEffect22, useMemo as useMemo27, useState as useState22 } from "react";
5332
+ import { Box as Box26, Text as Text26, useInput as useInput14 } from "ink";
5333
+ import { jsx as jsx50, jsxs as jsxs26 } from "react/jsx-runtime";
5334
+ var TITLE22 = "Sprint Details";
5082
5335
  var HINTS = [
5083
5336
  { key: "\u2191/\u2193", action: "move" },
5084
5337
  { key: "Enter", action: "open" }
5085
5338
  ];
5086
5339
  function SprintShowView({ sprintId }) {
5087
5340
  const router = useRouter();
5088
- const [state, setState] = useState16({ kind: "loading" });
5341
+ const [state, setState] = useState22({ kind: "loading" });
5089
5342
  useViewHints(HINTS);
5090
- useEffect15(() => {
5343
+ useEffect22(() => {
5091
5344
  const ctl = { cancelled: false };
5092
5345
  void (async () => {
5093
5346
  try {
@@ -5103,13 +5356,13 @@ function SprintShowView({ sprintId }) {
5103
5356
  ctl.cancelled = true;
5104
5357
  };
5105
5358
  }, [sprintId]);
5106
- const rows = useMemo29(() => {
5359
+ const rows = useMemo27(() => {
5107
5360
  if (state.kind !== "ready") return [];
5108
5361
  return buildRows2(state.sprint, state.tasks);
5109
5362
  }, [state]);
5110
- const sections = useMemo29(() => rows.filter((r) => r.separator !== true), [rows]);
5111
- const [cursor, setCursor] = useState16(0);
5112
- useInput12(
5363
+ const sections = useMemo27(() => rows.filter((r) => r.separator !== true), [rows]);
5364
+ const [cursor, setCursor] = useState22(0);
5365
+ useInput14(
5113
5366
  (_input, key) => {
5114
5367
  if (sections.length === 0) return;
5115
5368
  if (key.upArrow) setCursor((c) => Math.max(0, c - 1));
@@ -5121,11 +5374,11 @@ function SprintShowView({ sprintId }) {
5121
5374
  },
5122
5375
  { isActive: state.kind === "ready" && sections.length > 0 }
5123
5376
  );
5124
- return /* @__PURE__ */ jsx48(ViewShell, { title: TITLE21, children: renderBody21(state, rows, sections, cursor) });
5377
+ return /* @__PURE__ */ jsx50(ViewShell, { title: TITLE22, children: renderBody18(state, rows, sections, cursor) });
5125
5378
  }
5126
- function renderBody21(state, rows, sections, cursor) {
5127
- if (state.kind === "loading") return /* @__PURE__ */ jsx48(Spinner, { label: "Loading sprint\u2026" });
5128
- if (state.kind === "error") return /* @__PURE__ */ jsx48(ResultCard, { kind: "error", title: "Could not load sprint", lines: [state.message] });
5379
+ function renderBody18(state, rows, sections, cursor) {
5380
+ if (state.kind === "loading") return /* @__PURE__ */ jsx50(Spinner, { label: "Loading sprint\u2026" });
5381
+ if (state.kind === "error") return /* @__PURE__ */ jsx50(ResultCard, { kind: "error", title: "Could not load sprint", lines: [state.message] });
5129
5382
  const { sprint, tasks, project } = state;
5130
5383
  const projectLabel = project ? `${project.displayName} (${project.name})` : sprint.projectId;
5131
5384
  const approved = sprint.tickets.filter((t) => t.requirementStatus === "approved").length;
@@ -5133,11 +5386,11 @@ function renderBody21(state, rows, sections, cursor) {
5133
5386
  const activeSectionId = sections[cursor]?.destination.id;
5134
5387
  return /* @__PURE__ */ jsxs26(Box26, { flexDirection: "column", children: [
5135
5388
  /* @__PURE__ */ jsxs26(Box26, { children: [
5136
- /* @__PURE__ */ jsx48(Text26, { bold: true, children: sprint.name }),
5137
- /* @__PURE__ */ jsx48(Text26, { children: " " }),
5138
- /* @__PURE__ */ jsx48(StatusChip, { label: sprint.status, kind: chipKindForSprintStatus(sprint.status) })
5389
+ /* @__PURE__ */ jsx50(Text26, { bold: true, children: sprint.name }),
5390
+ /* @__PURE__ */ jsx50(Text26, { children: " " }),
5391
+ /* @__PURE__ */ jsx50(StatusChip, { label: sprint.status, kind: chipKindForSprintStatus(sprint.status) })
5139
5392
  ] }),
5140
- /* @__PURE__ */ jsx48(Box26, { marginTop: spacing.section, children: /* @__PURE__ */ jsx48(
5393
+ /* @__PURE__ */ jsx50(Box26, { marginTop: spacing.section, children: /* @__PURE__ */ jsx50(
5141
5394
  FieldList,
5142
5395
  {
5143
5396
  fields: [
@@ -5153,22 +5406,22 @@ function renderBody21(state, rows, sections, cursor) {
5153
5406
  }
5154
5407
  ) }),
5155
5408
  /* @__PURE__ */ jsxs26(Box26, { marginTop: spacing.section, flexDirection: "column", children: [
5156
- /* @__PURE__ */ jsx48(Text26, { color: inkColors.muted, bold: true, children: "Sections" }),
5157
- rows.map((row, i) => /* @__PURE__ */ jsx48(RowRenderer, { row, isActive: row.separator !== true && row.destination.id === activeSectionId }, i))
5409
+ /* @__PURE__ */ jsx50(Text26, { color: inkColors.muted, bold: true, children: "Sections" }),
5410
+ rows.map((row, i) => /* @__PURE__ */ jsx50(RowRenderer, { row, isActive: row.separator !== true && row.destination.id === activeSectionId }, i))
5158
5411
  ] })
5159
5412
  ] });
5160
5413
  }
5161
5414
  function RowRenderer({ row, isActive }) {
5162
5415
  if (row.separator === true) {
5163
- return /* @__PURE__ */ jsx48(Box26, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx48(Text26, { color: inkColors.muted, dimColor: true, children: glyphs.sectionRule.repeat(2) }) });
5416
+ return /* @__PURE__ */ jsx50(Box26, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx50(Text26, { color: inkColors.muted, dimColor: true, children: glyphs.sectionRule.repeat(2) }) });
5164
5417
  }
5165
5418
  return /* @__PURE__ */ jsxs26(Box26, { paddingLeft: spacing.indent, children: [
5166
- /* @__PURE__ */ jsx48(Text26, { color: isActive ? inkColors.highlight : void 0, bold: isActive, children: isActive ? glyphs.actionCursor : " " }),
5419
+ /* @__PURE__ */ jsx50(Text26, { color: isActive ? inkColors.highlight : void 0, bold: isActive, children: isActive ? glyphs.actionCursor : " " }),
5167
5420
  /* @__PURE__ */ jsxs26(Text26, { color: isActive ? inkColors.highlight : void 0, bold: isActive, children: [
5168
5421
  " ",
5169
5422
  row.label
5170
5423
  ] }),
5171
- /* @__PURE__ */ jsx48(Text26, { dimColor: true, children: ` ${glyphs.emDash} ${row.description}` })
5424
+ /* @__PURE__ */ jsx50(Text26, { dimColor: true, children: ` ${glyphs.emDash} ${row.description}` })
5172
5425
  ] });
5173
5426
  }
5174
5427
  function buildRows2(sprint, tasks) {
@@ -5229,9 +5482,9 @@ function buildRows2(sprint, tasks) {
5229
5482
  }
5230
5483
 
5231
5484
  // src/integration/ui/tui/views/browse/ticket-list-view.tsx
5232
- import { useCallback as useCallback9, useEffect as useEffect16, useState as useState17 } from "react";
5233
- import { useInput as useInput13 } from "ink";
5234
- import { jsx as jsx49 } from "react/jsx-runtime";
5485
+ import { useCallback as useCallback9, useEffect as useEffect23, useState as useState23 } from "react";
5486
+ import { useInput as useInput15 } from "ink";
5487
+ import { jsx as jsx51 } from "react/jsx-runtime";
5235
5488
  function requirementColor(status) {
5236
5489
  return status === "approved" ? inkColors.success : inkColors.warning;
5237
5490
  }
@@ -5255,7 +5508,7 @@ function buildColumns2(repoNamesById) {
5255
5508
  }
5256
5509
  ];
5257
5510
  }
5258
- var TITLE22 = "Tickets";
5511
+ var TITLE23 = "Tickets";
5259
5512
  var HINTS_READY3 = [
5260
5513
  { key: "\u2191/\u2193", action: "navigate" },
5261
5514
  { key: "Enter", action: "open" },
@@ -5266,7 +5519,7 @@ var HINTS_READY3 = [
5266
5519
  var HINTS_EMPTY2 = [{ key: "a", action: "add" }];
5267
5520
  function TicketListView({ sprintId }) {
5268
5521
  const router = useRouter();
5269
- const [state, setState] = useState17({ kind: "loading" });
5522
+ const [state, setState] = useState23({ kind: "loading" });
5270
5523
  const load = useCallback9(async () => {
5271
5524
  try {
5272
5525
  const id = await resolveSprintId(sprintId);
@@ -5284,7 +5537,7 @@ function TicketListView({ sprintId }) {
5284
5537
  setState({ kind: "error", message: err instanceof Error ? err.message : String(err) });
5285
5538
  }
5286
5539
  }, [sprintId]);
5287
- useEffect16(() => {
5540
+ useEffect23(() => {
5288
5541
  const ctl = { cancelled: false };
5289
5542
  void (async () => {
5290
5543
  if (!ctl.cancelled) await load();
@@ -5293,7 +5546,7 @@ function TicketListView({ sprintId }) {
5293
5546
  ctl.cancelled = true;
5294
5547
  };
5295
5548
  }, [load]);
5296
- useInput13((input) => {
5549
+ useInput15((input) => {
5297
5550
  if (state.kind === "loading") return;
5298
5551
  if (input === "a") {
5299
5552
  router.push({ id: "ticket-add" });
@@ -5309,7 +5562,7 @@ function TicketListView({ sprintId }) {
5309
5562
  }
5310
5563
  });
5311
5564
  useViewHints(state.kind === "ready" ? HINTS_READY3 : HINTS_EMPTY2);
5312
- return /* @__PURE__ */ jsx49(ViewShell, { title: TITLE22, children: state.kind === "loading" ? /* @__PURE__ */ jsx49(Spinner, { label: "Loading tickets\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx49(ResultCard, { kind: "info", title: "No tickets in this sprint", lines: ["Press `a` to add a ticket."] }) : state.kind === "error" ? /* @__PURE__ */ jsx49(ResultCard, { kind: "error", title: "Could not load tickets", lines: [state.message] }) : /* @__PURE__ */ jsx49(
5565
+ return /* @__PURE__ */ jsx51(ViewShell, { title: TITLE23, children: state.kind === "loading" ? /* @__PURE__ */ jsx51(Spinner, { label: "Loading tickets\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx51(ResultCard, { kind: "info", title: "No tickets in this sprint", lines: ["Press `a` to add a ticket."] }) : state.kind === "error" ? /* @__PURE__ */ jsx51(ResultCard, { kind: "error", title: "Could not load tickets", lines: [state.message] }) : /* @__PURE__ */ jsx51(
5313
5566
  ListView,
5314
5567
  {
5315
5568
  rows: state.tickets,
@@ -5322,16 +5575,16 @@ function TicketListView({ sprintId }) {
5322
5575
  }
5323
5576
 
5324
5577
  // src/integration/ui/tui/views/browse/ticket-show-view.tsx
5325
- import { useEffect as useEffect17, useState as useState18 } from "react";
5326
- import { Box as Box27, Text as Text27, useInput as useInput14 } from "ink";
5327
- import { jsx as jsx50, jsxs as jsxs27 } from "react/jsx-runtime";
5328
- var TITLE23 = "Ticket Details";
5578
+ import { useEffect as useEffect24, useState as useState24 } from "react";
5579
+ import { Box as Box27, Text as Text27, useInput as useInput16 } from "ink";
5580
+ import { jsx as jsx52, jsxs as jsxs27 } from "react/jsx-runtime";
5581
+ var TITLE24 = "Ticket Details";
5329
5582
  var HINTS_READ_ONLY = [];
5330
5583
  var HINTS_EDITABLE = [{ key: "e", action: "edit" }];
5331
5584
  function TicketShowView({ ticketId }) {
5332
5585
  const router = useRouter();
5333
- const [state, setState] = useState18({ kind: "loading" });
5334
- useEffect17(() => {
5586
+ const [state, setState] = useState24({ kind: "loading" });
5587
+ useEffect24(() => {
5335
5588
  const ctl = { cancelled: false };
5336
5589
  void (async () => {
5337
5590
  if (!ticketId) {
@@ -5358,23 +5611,23 @@ function TicketShowView({ ticketId }) {
5358
5611
  ctl.cancelled = true;
5359
5612
  };
5360
5613
  }, [ticketId]);
5361
- useInput14((input) => {
5614
+ useInput16((input) => {
5362
5615
  if (input === "e" && state.kind === "ready" && state.editable && ticketId) {
5363
5616
  router.push({ id: "ticket-edit", props: { ticketId } });
5364
5617
  }
5365
5618
  });
5366
5619
  useViewHints(state.kind === "ready" && state.editable ? HINTS_EDITABLE : HINTS_READ_ONLY);
5367
- return /* @__PURE__ */ jsx50(ViewShell, { title: TITLE23, children: renderBody22(state) });
5620
+ return /* @__PURE__ */ jsx52(ViewShell, { title: TITLE24, children: renderBody19(state) });
5368
5621
  }
5369
- function renderBody22(state) {
5370
- if (state.kind === "loading") return /* @__PURE__ */ jsx50(Spinner, { label: "Loading ticket\u2026" });
5371
- if (state.kind === "error") return /* @__PURE__ */ jsx50(ResultCard, { kind: "error", title: "Could not load ticket", lines: [state.message] });
5622
+ function renderBody19(state) {
5623
+ if (state.kind === "loading") return /* @__PURE__ */ jsx52(Spinner, { label: "Loading ticket\u2026" });
5624
+ if (state.kind === "error") return /* @__PURE__ */ jsx52(ResultCard, { kind: "error", title: "Could not load ticket", lines: [state.message] });
5372
5625
  const { ticket } = state;
5373
5626
  return /* @__PURE__ */ jsxs27(Box27, { flexDirection: "column", children: [
5374
5627
  /* @__PURE__ */ jsxs27(Box27, { children: [
5375
- /* @__PURE__ */ jsx50(Text27, { bold: true, children: ticket.title }),
5376
- /* @__PURE__ */ jsx50(Text27, { children: " " }),
5377
- /* @__PURE__ */ jsx50(
5628
+ /* @__PURE__ */ jsx52(Text27, { bold: true, children: ticket.title }),
5629
+ /* @__PURE__ */ jsx52(Text27, { children: " " }),
5630
+ /* @__PURE__ */ jsx52(
5378
5631
  StatusChip,
5379
5632
  {
5380
5633
  label: ticket.requirementStatus,
@@ -5382,7 +5635,7 @@ function renderBody22(state) {
5382
5635
  }
5383
5636
  )
5384
5637
  ] }),
5385
- /* @__PURE__ */ jsx50(Box27, { marginTop: spacing.section, children: /* @__PURE__ */ jsx50(
5638
+ /* @__PURE__ */ jsx52(Box27, { marginTop: spacing.section, children: /* @__PURE__ */ jsx52(
5386
5639
  FieldList,
5387
5640
  {
5388
5641
  fields: [
@@ -5393,20 +5646,20 @@ function renderBody22(state) {
5393
5646
  }
5394
5647
  ) }),
5395
5648
  ticket.description ? /* @__PURE__ */ jsxs27(Box27, { marginTop: spacing.section, flexDirection: "column", children: [
5396
- /* @__PURE__ */ jsx50(Text27, { color: inkColors.muted, bold: true, children: "Description" }),
5397
- /* @__PURE__ */ jsx50(Box27, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx50(Text27, { children: ticket.description }) })
5649
+ /* @__PURE__ */ jsx52(Text27, { color: inkColors.muted, bold: true, children: "Description" }),
5650
+ /* @__PURE__ */ jsx52(Box27, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx52(Text27, { children: ticket.description }) })
5398
5651
  ] }) : null,
5399
5652
  ticket.requirements ? /* @__PURE__ */ jsxs27(Box27, { marginTop: spacing.section, flexDirection: "column", children: [
5400
- /* @__PURE__ */ jsx50(Text27, { color: inkColors.muted, bold: true, children: "Requirements" }),
5401
- /* @__PURE__ */ jsx50(Box27, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx50(Text27, { children: ticket.requirements }) })
5653
+ /* @__PURE__ */ jsx52(Text27, { color: inkColors.muted, bold: true, children: "Requirements" }),
5654
+ /* @__PURE__ */ jsx52(Box27, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx52(Text27, { children: ticket.requirements }) })
5402
5655
  ] }) : null
5403
5656
  ] });
5404
5657
  }
5405
5658
 
5406
5659
  // src/integration/ui/tui/views/browse/task-list-view.tsx
5407
- import { useEffect as useEffect18, useMemo as useMemo30, useState as useState19 } from "react";
5408
- import { useInput as useInput15 } from "ink";
5409
- import { jsx as jsx51 } from "react/jsx-runtime";
5660
+ import { useEffect as useEffect25, useMemo as useMemo28, useState as useState25 } from "react";
5661
+ import { useInput as useInput17 } from "ink";
5662
+ import { jsx as jsx53 } from "react/jsx-runtime";
5410
5663
  var FILTER_CYCLE2 = ["all", "todo", "active", "done"];
5411
5664
  function nextFilter2(f) {
5412
5665
  const i = FILTER_CYCLE2.indexOf(f);
@@ -5478,10 +5731,10 @@ var HINTS_READY4 = [
5478
5731
  var HINTS_EMPTY3 = [{ key: "a", action: "add" }];
5479
5732
  function TaskListView({ sprintId }) {
5480
5733
  const router = useRouter();
5481
- const [state, setState] = useState19({ kind: "loading" });
5482
- const [filter, setFilter] = useState19("all");
5734
+ const [state, setState] = useState25({ kind: "loading" });
5735
+ const [filter, setFilter] = useState25("all");
5483
5736
  useViewHints(state.kind === "ready" ? HINTS_READY4 : HINTS_EMPTY3);
5484
- useEffect18(() => {
5737
+ useEffect25(() => {
5485
5738
  const ctl = { cancelled: false };
5486
5739
  void (async () => {
5487
5740
  try {
@@ -5510,7 +5763,7 @@ function TaskListView({ sprintId }) {
5510
5763
  ctl.cancelled = true;
5511
5764
  };
5512
5765
  }, [sprintId]);
5513
- useInput15((input) => {
5766
+ useInput17((input) => {
5514
5767
  if (state.kind === "loading") return;
5515
5768
  if (input === "a") {
5516
5769
  router.push({ id: "task-add" });
@@ -5529,13 +5782,13 @@ function TaskListView({ sprintId }) {
5529
5782
  router.push({ id: "task-remove" });
5530
5783
  }
5531
5784
  });
5532
- const filtered = useMemo30(() => {
5785
+ const filtered = useMemo28(() => {
5533
5786
  if (state.kind !== "ready") return [];
5534
5787
  if (filter === "all") return state.tasks;
5535
5788
  return state.tasks.filter((t) => matches(t, filter));
5536
5789
  }, [state, filter]);
5537
5790
  const title = filter === "all" ? TITLE_BASE2 : `${TITLE_BASE2} \xB7 filter: ${filter}`;
5538
- return /* @__PURE__ */ jsx51(ViewShell, { title, children: state.kind === "loading" ? /* @__PURE__ */ jsx51(Spinner, { label: "Loading tasks\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx51(ResultCard, { kind: "info", title: "No tasks in this sprint" }) : state.kind === "error" ? /* @__PURE__ */ jsx51(ResultCard, { kind: "error", title: "Could not load tasks", lines: [state.message] }) : filtered.length === 0 ? /* @__PURE__ */ jsx51(ResultCard, { kind: "info", title: `No tasks with filter '${filter}'`, lines: ["Press f to cycle the filter."] }) : /* @__PURE__ */ jsx51(
5791
+ return /* @__PURE__ */ jsx53(ViewShell, { title, children: state.kind === "loading" ? /* @__PURE__ */ jsx53(Spinner, { label: "Loading tasks\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx53(ResultCard, { kind: "info", title: "No tasks in this sprint" }) : state.kind === "error" ? /* @__PURE__ */ jsx53(ResultCard, { kind: "error", title: "Could not load tasks", lines: [state.message] }) : filtered.length === 0 ? /* @__PURE__ */ jsx53(ResultCard, { kind: "info", title: `No tasks with filter '${filter}'`, lines: ["Press f to cycle the filter."] }) : /* @__PURE__ */ jsx53(
5539
5792
  ListView,
5540
5793
  {
5541
5794
  rows: filtered,
@@ -5548,10 +5801,10 @@ function TaskListView({ sprintId }) {
5548
5801
  }
5549
5802
 
5550
5803
  // src/integration/ui/tui/views/browse/task-show-view.tsx
5551
- import { useEffect as useEffect19, useState as useState20 } from "react";
5552
- import { Box as Box28, Text as Text28, useInput as useInput16 } from "ink";
5553
- import { jsx as jsx52, jsxs as jsxs28 } from "react/jsx-runtime";
5554
- var TITLE24 = "Task Details";
5804
+ import { useEffect as useEffect26, useState as useState26 } from "react";
5805
+ import { Box as Box28, Text as Text28, useInput as useInput18 } from "ink";
5806
+ import { jsx as jsx54, jsxs as jsxs28 } from "react/jsx-runtime";
5807
+ var TITLE25 = "Task Details";
5555
5808
  var HINTS_READY5 = [
5556
5809
  { key: "t", action: "status" },
5557
5810
  { key: "r", action: "remove" }
@@ -5559,9 +5812,9 @@ var HINTS_READY5 = [
5559
5812
  var HINTS_EMPTY4 = [];
5560
5813
  function TaskShowView({ taskId }) {
5561
5814
  const router = useRouter();
5562
- const [state, setState] = useState20({ kind: "loading" });
5815
+ const [state, setState] = useState26({ kind: "loading" });
5563
5816
  useViewHints(state.kind === "ready" ? HINTS_READY5 : HINTS_EMPTY4);
5564
- useInput16((input) => {
5817
+ useInput18((input) => {
5565
5818
  if (state.kind !== "ready") return;
5566
5819
  if (input === "t") {
5567
5820
  router.push({ id: "task-status" });
@@ -5571,7 +5824,7 @@ function TaskShowView({ taskId }) {
5571
5824
  router.push({ id: "task-remove" });
5572
5825
  }
5573
5826
  });
5574
- useEffect19(() => {
5827
+ useEffect26(() => {
5575
5828
  const ctl = { cancelled: false };
5576
5829
  void (async () => {
5577
5830
  if (!taskId) {
@@ -5589,19 +5842,19 @@ function TaskShowView({ taskId }) {
5589
5842
  ctl.cancelled = true;
5590
5843
  };
5591
5844
  }, [taskId]);
5592
- return /* @__PURE__ */ jsx52(ViewShell, { title: TITLE24, children: renderBody23(state) });
5845
+ return /* @__PURE__ */ jsx54(ViewShell, { title: TITLE25, children: renderBody20(state) });
5593
5846
  }
5594
- function renderBody23(state) {
5595
- if (state.kind === "loading") return /* @__PURE__ */ jsx52(Spinner, { label: "Loading task\u2026" });
5596
- if (state.kind === "error") return /* @__PURE__ */ jsx52(ResultCard, { kind: "error", title: "Could not load task", lines: [state.message] });
5847
+ function renderBody20(state) {
5848
+ if (state.kind === "loading") return /* @__PURE__ */ jsx54(Spinner, { label: "Loading task\u2026" });
5849
+ if (state.kind === "error") return /* @__PURE__ */ jsx54(ResultCard, { kind: "error", title: "Could not load task", lines: [state.message] });
5597
5850
  const { task } = state;
5598
5851
  return /* @__PURE__ */ jsxs28(Box28, { flexDirection: "column", children: [
5599
5852
  /* @__PURE__ */ jsxs28(Box28, { children: [
5600
- /* @__PURE__ */ jsx52(Text28, { bold: true, children: task.name }),
5601
- /* @__PURE__ */ jsx52(Text28, { children: " " }),
5602
- /* @__PURE__ */ jsx52(StatusChip, { label: task.status, kind: chipKindForTaskStatus(task.status) })
5853
+ /* @__PURE__ */ jsx54(Text28, { bold: true, children: task.name }),
5854
+ /* @__PURE__ */ jsx54(Text28, { children: " " }),
5855
+ /* @__PURE__ */ jsx54(StatusChip, { label: task.status, kind: chipKindForTaskStatus(task.status) })
5603
5856
  ] }),
5604
- /* @__PURE__ */ jsx52(Box28, { marginTop: spacing.section, children: /* @__PURE__ */ jsx52(
5857
+ /* @__PURE__ */ jsx54(Box28, { marginTop: spacing.section, children: /* @__PURE__ */ jsx54(
5605
5858
  FieldList,
5606
5859
  {
5607
5860
  fields: [
@@ -5616,54 +5869,59 @@ function renderBody23(state) {
5616
5869
  }
5617
5870
  ) }),
5618
5871
  task.description ? /* @__PURE__ */ jsxs28(Box28, { marginTop: spacing.section, flexDirection: "column", children: [
5619
- /* @__PURE__ */ jsx52(Text28, { color: inkColors.muted, bold: true, children: "Description" }),
5620
- /* @__PURE__ */ jsx52(Box28, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx52(Text28, { children: task.description }) })
5872
+ /* @__PURE__ */ jsx54(Text28, { color: inkColors.muted, bold: true, children: "Description" }),
5873
+ /* @__PURE__ */ jsx54(Box28, { paddingLeft: spacing.indent, children: /* @__PURE__ */ jsx54(Text28, { children: task.description }) })
5621
5874
  ] }) : null,
5622
5875
  task.steps.length > 0 ? /* @__PURE__ */ jsxs28(Box28, { marginTop: spacing.section, flexDirection: "column", children: [
5623
- /* @__PURE__ */ jsx52(Text28, { color: inkColors.muted, bold: true, children: "Steps" }),
5876
+ /* @__PURE__ */ jsx54(Text28, { color: inkColors.muted, bold: true, children: "Steps" }),
5624
5877
  task.steps.map((step, i) => /* @__PURE__ */ jsxs28(Box28, { paddingLeft: spacing.indent, children: [
5625
5878
  /* @__PURE__ */ jsxs28(Text28, { dimColor: true, children: [
5626
5879
  String(i + 1).padStart(2, " "),
5627
5880
  ". "
5628
5881
  ] }),
5629
- /* @__PURE__ */ jsx52(Text28, { children: step })
5882
+ /* @__PURE__ */ jsx54(Text28, { children: step })
5630
5883
  ] }, i))
5631
5884
  ] }) : null,
5632
5885
  task.verificationCriteria.length > 0 ? /* @__PURE__ */ jsxs28(Box28, { marginTop: spacing.section, flexDirection: "column", children: [
5633
- /* @__PURE__ */ jsx52(Text28, { color: inkColors.muted, bold: true, children: "Verification" }),
5886
+ /* @__PURE__ */ jsx54(Text28, { color: inkColors.muted, bold: true, children: "Verification" }),
5634
5887
  task.verificationCriteria.map((v, i) => /* @__PURE__ */ jsxs28(Box28, { paddingLeft: spacing.indent, children: [
5635
5888
  /* @__PURE__ */ jsxs28(Text28, { dimColor: true, children: [
5636
5889
  glyphs.bulletListItem,
5637
5890
  " "
5638
5891
  ] }),
5639
- /* @__PURE__ */ jsx52(Text28, { children: v })
5892
+ /* @__PURE__ */ jsx54(Text28, { children: v })
5640
5893
  ] }, i))
5641
5894
  ] }) : null
5642
5895
  ] });
5643
5896
  }
5644
5897
 
5645
5898
  // src/integration/ui/tui/views/browse/project-list-view.tsx
5646
- import { useEffect as useEffect20, useState as useState21 } from "react";
5647
- import { useInput as useInput17 } from "ink";
5648
- import { jsx as jsx53 } from "react/jsx-runtime";
5899
+ import { useCallback as useCallback10, useEffect as useEffect27, useRef as useRef3, useState as useState27 } from "react";
5900
+ import { useInput as useInput19 } from "ink";
5901
+ import { jsx as jsx55 } from "react/jsx-runtime";
5649
5902
  var COLUMNS = [
5650
5903
  { header: "Name", cell: (p) => p.name, width: 16 },
5651
5904
  { header: "Display", cell: (p) => p.displayName, flex: true },
5652
5905
  { header: "Repos", cell: (p) => String(p.repositories.length), align: "right", width: 6 }
5653
5906
  ];
5654
- var TITLE25 = "Projects";
5907
+ var TITLE26 = "Projects";
5655
5908
  var HINTS_READY6 = [
5656
5909
  { key: "\u2191/\u2193", action: "navigate" },
5657
5910
  { key: "Enter", action: "open" },
5658
5911
  { key: "a", action: "add" },
5659
5912
  { key: "e", action: "edit" },
5913
+ { key: "o", action: "onboard" },
5660
5914
  { key: "r", action: "remove" }
5661
5915
  ];
5662
5916
  var HINTS_EMPTY5 = [{ key: "a", action: "add" }];
5663
5917
  function ProjectListView() {
5664
5918
  const router = useRouter();
5665
- const [state, setState] = useState21({ kind: "loading" });
5666
- useEffect20(() => {
5919
+ const [state, setState] = useState27({ kind: "loading" });
5920
+ const highlightedRef = useRef3(null);
5921
+ const handleCursorChange = useCallback10((row) => {
5922
+ highlightedRef.current = row;
5923
+ }, []);
5924
+ useEffect27(() => {
5667
5925
  const ctl = { cancelled: false };
5668
5926
  void (async () => {
5669
5927
  try {
@@ -5680,7 +5938,7 @@ function ProjectListView() {
5680
5938
  ctl.cancelled = true;
5681
5939
  };
5682
5940
  }, []);
5683
- useInput17((input) => {
5941
+ useInput19((input) => {
5684
5942
  if (state.kind === "loading") return;
5685
5943
  if (input === "a") {
5686
5944
  router.push({ id: "project-add" });
@@ -5691,16 +5949,24 @@ function ProjectListView() {
5691
5949
  router.push({ id: "project-edit" });
5692
5950
  return;
5693
5951
  }
5952
+ if (input === "o") {
5953
+ const target = highlightedRef.current ?? state.projects[0];
5954
+ if (target) {
5955
+ router.push({ id: "project-onboard", props: { projectName: target.name } });
5956
+ }
5957
+ return;
5958
+ }
5694
5959
  if (input === "r") {
5695
5960
  router.push({ id: "project-remove" });
5696
5961
  }
5697
5962
  });
5698
5963
  useViewHints(state.kind === "ready" ? HINTS_READY6 : HINTS_EMPTY5);
5699
- return /* @__PURE__ */ jsx53(ViewShell, { title: TITLE25, children: state.kind === "loading" ? /* @__PURE__ */ jsx53(Spinner, { label: "Loading projects\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx53(ResultCard, { kind: "info", title: "No projects registered", lines: ["Press `a` to add one."] }) : state.kind === "error" ? /* @__PURE__ */ jsx53(ResultCard, { kind: "error", title: "Could not load projects", lines: [state.message] }) : /* @__PURE__ */ jsx53(
5964
+ return /* @__PURE__ */ jsx55(ViewShell, { title: TITLE26, children: state.kind === "loading" ? /* @__PURE__ */ jsx55(Spinner, { label: "Loading projects\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx55(ResultCard, { kind: "info", title: "No projects registered", lines: ["Press `a` to add one."] }) : state.kind === "error" ? /* @__PURE__ */ jsx55(ResultCard, { kind: "error", title: "Could not load projects", lines: [state.message] }) : /* @__PURE__ */ jsx55(
5700
5965
  ListView,
5701
5966
  {
5702
5967
  rows: state.projects,
5703
5968
  columns: COLUMNS,
5969
+ onCursorChange: handleCursorChange,
5704
5970
  onSelect: (p) => {
5705
5971
  router.push({ id: "project-show", props: { projectName: p.name } });
5706
5972
  }
@@ -5709,21 +5975,22 @@ function ProjectListView() {
5709
5975
  }
5710
5976
 
5711
5977
  // src/integration/ui/tui/views/browse/project-show-view.tsx
5712
- import { useEffect as useEffect21, useState as useState22 } from "react";
5713
- import { Box as Box29, Text as Text29, useInput as useInput18 } from "ink";
5714
- import { jsx as jsx54, jsxs as jsxs29 } from "react/jsx-runtime";
5715
- var TITLE26 = "Project Details";
5978
+ import { useEffect as useEffect28, useState as useState28 } from "react";
5979
+ import { Box as Box29, Text as Text29, useInput as useInput20 } from "ink";
5980
+ import { jsx as jsx56, jsxs as jsxs29 } from "react/jsx-runtime";
5981
+ var TITLE27 = "Project Details";
5716
5982
  var HINTS_READY7 = [
5717
5983
  { key: "e", action: "edit" },
5718
5984
  { key: "a", action: "add repo" },
5985
+ { key: "o", action: "onboard" },
5719
5986
  { key: "r", action: "remove repo" }
5720
5987
  ];
5721
5988
  var HINTS_EMPTY6 = [];
5722
5989
  function ProjectShowView({ projectName }) {
5723
5990
  const router = useRouter();
5724
- const [state, setState] = useState22({ kind: "loading" });
5991
+ const [state, setState] = useState28({ kind: "loading" });
5725
5992
  useViewHints(state.kind === "ready" ? HINTS_READY7 : HINTS_EMPTY6);
5726
- useInput18((input) => {
5993
+ useInput20((input) => {
5727
5994
  if (state.kind !== "ready") return;
5728
5995
  if (input === "e") {
5729
5996
  router.push({ id: "project-edit" });
@@ -5733,11 +6000,15 @@ function ProjectShowView({ projectName }) {
5733
6000
  router.push({ id: "project-repo-add" });
5734
6001
  return;
5735
6002
  }
6003
+ if (input === "o") {
6004
+ router.push({ id: "project-onboard", props: { projectName: state.project.name } });
6005
+ return;
6006
+ }
5736
6007
  if (input === "r") {
5737
6008
  router.push({ id: "project-repo-remove" });
5738
6009
  }
5739
6010
  });
5740
- useEffect21(() => {
6011
+ useEffect28(() => {
5741
6012
  const ctl = { cancelled: false };
5742
6013
  void (async () => {
5743
6014
  if (!projectName) {
@@ -5755,18 +6026,18 @@ function ProjectShowView({ projectName }) {
5755
6026
  ctl.cancelled = true;
5756
6027
  };
5757
6028
  }, [projectName]);
5758
- return /* @__PURE__ */ jsx54(ViewShell, { title: TITLE26, children: renderBody24(state) });
6029
+ return /* @__PURE__ */ jsx56(ViewShell, { title: TITLE27, children: renderBody21(state) });
5759
6030
  }
5760
- function renderBody24(state) {
5761
- if (state.kind === "loading") return /* @__PURE__ */ jsx54(Spinner, { label: "Loading project\u2026" });
5762
- if (state.kind === "error") return /* @__PURE__ */ jsx54(ResultCard, { kind: "error", title: "Could not load project", lines: [state.message] });
6031
+ function renderBody21(state) {
6032
+ if (state.kind === "loading") return /* @__PURE__ */ jsx56(Spinner, { label: "Loading project\u2026" });
6033
+ if (state.kind === "error") return /* @__PURE__ */ jsx56(ResultCard, { kind: "error", title: "Could not load project", lines: [state.message] });
5763
6034
  const { project } = state;
5764
6035
  return /* @__PURE__ */ jsxs29(Box29, { flexDirection: "column", children: [
5765
6036
  /* @__PURE__ */ jsxs29(Box29, { children: [
5766
- /* @__PURE__ */ jsx54(Text29, { bold: true, children: project.displayName }),
5767
- /* @__PURE__ */ jsx54(Text29, { dimColor: true, children: ` (${project.name})` })
6037
+ /* @__PURE__ */ jsx56(Text29, { bold: true, children: project.displayName }),
6038
+ /* @__PURE__ */ jsx56(Text29, { dimColor: true, children: ` (${project.name})` })
5768
6039
  ] }),
5769
- /* @__PURE__ */ jsx54(Box29, { marginTop: spacing.section, children: /* @__PURE__ */ jsx54(
6040
+ /* @__PURE__ */ jsx56(Box29, { marginTop: spacing.section, children: /* @__PURE__ */ jsx56(
5770
6041
  FieldList,
5771
6042
  {
5772
6043
  fields: [
@@ -5778,28 +6049,29 @@ function renderBody24(state) {
5778
6049
  }
5779
6050
  ) }),
5780
6051
  /* @__PURE__ */ jsxs29(Box29, { marginTop: spacing.section, flexDirection: "column", children: [
5781
- /* @__PURE__ */ jsx54(Text29, { color: inkColors.muted, bold: true, children: "Repositories" }),
6052
+ /* @__PURE__ */ jsx56(Text29, { color: inkColors.muted, bold: true, children: "Repositories" }),
5782
6053
  project.repositories.map((r) => /* @__PURE__ */ jsxs29(Box29, { paddingLeft: spacing.indent, children: [
5783
6054
  /* @__PURE__ */ jsxs29(Text29, { dimColor: true, children: [
5784
6055
  glyphs.bulletListItem,
5785
6056
  " "
5786
6057
  ] }),
5787
- /* @__PURE__ */ jsx54(Text29, { children: r.name }),
5788
- /* @__PURE__ */ jsx54(Text29, { dimColor: true, children: ` ${r.path} ` }),
5789
- /* @__PURE__ */ jsx54(Text29, { dimColor: true, children: `(id: ${r.id})` })
6058
+ /* @__PURE__ */ jsx56(Text29, { children: r.name }),
6059
+ /* @__PURE__ */ jsx56(Text29, { dimColor: true, children: ` ${r.path} ` }),
6060
+ /* @__PURE__ */ jsx56(Text29, { dimColor: true, children: `(id: ${r.id})` })
5790
6061
  ] }, r.id))
5791
6062
  ] })
5792
6063
  ] });
5793
6064
  }
5794
6065
 
5795
6066
  // src/integration/ui/tui/views/browse/doctor-view.tsx
5796
- import { useEffect as useEffect22, useState as useState23 } from "react";
6067
+ import { useEffect as useEffect29, useState as useState29 } from "react";
5797
6068
  import { Box as Box30, Text as Text30 } from "ink";
5798
- import { jsx as jsx55, jsxs as jsxs30 } from "react/jsx-runtime";
5799
- var TITLE27 = "Doctor";
6069
+ import { jsx as jsx57, jsxs as jsxs30 } from "react/jsx-runtime";
6070
+ var TITLE28 = "Doctor";
5800
6071
  var HINTS2 = [];
6072
+ var MIN_LABEL_WIDTH = 20;
5801
6073
  async function runChecks() {
5802
- const checks = [
6074
+ const environment = [
5803
6075
  ["Node.js version", checkNodeVersion()],
5804
6076
  ["git installed", checkGitInstalled()],
5805
6077
  ["git identity", checkGitIdentity()],
@@ -5811,17 +6083,27 @@ async function runChecks() {
5811
6083
  ["Config schema", await checkConfigSchemaValidation()],
5812
6084
  ["Current sprint", await checkCurrentSprint()]
5813
6085
  ];
5814
- return Promise.all(
5815
- checks.map(async ([name, r]) => ({
6086
+ const envRows = await Promise.all(
6087
+ environment.map(async ([name, r]) => ({
5816
6088
  name,
5817
6089
  result: r instanceof Promise ? await r : r
5818
6090
  }))
5819
6091
  );
6092
+ const onboardingResults = await checkRepoOnboarding();
6093
+ const onboardingRows = onboardingResults.map((result) => ({
6094
+ name: result.name.replace(/^Onboarding\s+—\s+/u, ""),
6095
+ result
6096
+ }));
6097
+ const sections = [{ title: "Environment", rows: envRows }];
6098
+ if (onboardingRows.length > 0) {
6099
+ sections.push({ title: "Onboarding", rows: onboardingRows });
6100
+ }
6101
+ return sections;
5820
6102
  }
5821
6103
  function glyph(status) {
5822
6104
  if (status === "pass") return glyphs.check;
5823
- if (status === "warn") return "!";
5824
- if (status === "skip") return glyphs.inlineDot;
6105
+ if (status === "warn") return glyphs.warningGlyph;
6106
+ if (status === "skip") return glyphs.phaseDisabled;
5825
6107
  return glyphs.cross;
5826
6108
  }
5827
6109
  function color(status) {
@@ -5830,38 +6112,64 @@ function color(status) {
5830
6112
  if (status === "skip") return inkColors.muted;
5831
6113
  return inkColors.error;
5832
6114
  }
6115
+ function labelWidth(rows) {
6116
+ return Math.max(MIN_LABEL_WIDTH, ...rows.map((r) => r.name.length));
6117
+ }
6118
+ function Row({ row, width }) {
6119
+ const dim = row.result.status === "skip";
6120
+ return /* @__PURE__ */ jsxs30(Box30, { children: [
6121
+ /* @__PURE__ */ jsx57(Text30, { color: color(row.result.status), bold: true, children: glyph(row.result.status) }),
6122
+ /* @__PURE__ */ jsx57(Text30, { children: ` ` }),
6123
+ /* @__PURE__ */ jsx57(Text30, { bold: true, dimColor: dim, children: row.name.padEnd(width + 2) }),
6124
+ /* @__PURE__ */ jsx57(Text30, { dimColor: true, children: row.result.detail ?? row.result.status.toUpperCase() })
6125
+ ] });
6126
+ }
5833
6127
  function DoctorView() {
5834
- const [state, setState] = useState23({ kind: "running" });
6128
+ const [state, setState] = useState29({ kind: "running" });
5835
6129
  useViewHints(HINTS2);
5836
- useEffect22(() => {
6130
+ useEffect29(() => {
5837
6131
  const ctl = { cancelled: false };
5838
6132
  void (async () => {
5839
- const checks = await runChecks();
5840
- if (!ctl.cancelled) setState({ kind: "done", checks });
6133
+ const sections = await runChecks();
6134
+ if (!ctl.cancelled) setState({ kind: "done", sections });
5841
6135
  })();
5842
6136
  return () => {
5843
6137
  ctl.cancelled = true;
5844
6138
  };
5845
6139
  }, []);
5846
- return /* @__PURE__ */ jsx55(ViewShell, { title: TITLE27, children: state.kind === "running" ? /* @__PURE__ */ jsx55(Spinner, { label: "Running environment checks\u2026" }) : state.checks.map((c) => /* @__PURE__ */ jsxs30(Box30, { children: [
5847
- /* @__PURE__ */ jsx55(Text30, { color: color(c.result.status), bold: true, children: glyph(c.result.status) }),
5848
- /* @__PURE__ */ jsx55(Text30, { children: ` ` }),
5849
- /* @__PURE__ */ jsx55(Text30, { bold: true, children: c.name.padEnd(20) }),
5850
- /* @__PURE__ */ jsx55(Text30, { dimColor: true, children: c.result.detail ?? c.result.status.toUpperCase() })
5851
- ] }, c.name)) });
6140
+ if (state.kind === "running") {
6141
+ return /* @__PURE__ */ jsx57(ViewShell, { title: TITLE28, children: /* @__PURE__ */ jsx57(Spinner, { label: "Running environment checks\u2026" }) });
6142
+ }
6143
+ return /* @__PURE__ */ jsx57(ViewShell, { title: TITLE28, children: state.sections.map((section, i) => {
6144
+ const width = labelWidth(section.rows);
6145
+ const passed = section.rows.filter((r) => r.result.status === "pass").length;
6146
+ const warned = section.rows.filter((r) => r.result.status === "warn").length;
6147
+ const failed = section.rows.filter((r) => r.result.status === "fail").length;
6148
+ const total = section.rows.filter((r) => r.result.status !== "skip").length;
6149
+ const summary = `${String(passed)}/${String(total)} pass${warned > 0 ? ` \xB7 ${String(warned)} warn` : ""}${failed > 0 ? ` \xB7 ${String(failed)} fail` : ""}`;
6150
+ return /* @__PURE__ */ jsxs30(Box30, { flexDirection: "column", children: [
6151
+ i === 0 ? null : /* @__PURE__ */ jsx57(Box30, { marginTop: spacing.section }),
6152
+ /* @__PURE__ */ jsxs30(Box30, { children: [
6153
+ /* @__PURE__ */ jsx57(Text30, { color: inkColors.primary, bold: true, children: section.title.toUpperCase() }),
6154
+ /* @__PURE__ */ jsx57(Text30, { color: inkColors.muted, children: ` ${glyphs.emDash} ` }),
6155
+ /* @__PURE__ */ jsx57(Text30, { color: inkColors.muted, children: summary })
6156
+ ] }),
6157
+ /* @__PURE__ */ jsx57(Box30, { marginTop: spacing.section, flexDirection: "column", children: section.rows.map((row) => /* @__PURE__ */ jsx57(Row, { row, width }, row.name)) })
6158
+ ] }, section.title);
6159
+ }) });
5852
6160
  }
5853
6161
 
5854
6162
  // src/integration/ui/tui/views/browse/progress-show-view.tsx
5855
- import { useEffect as useEffect23, useState as useState24 } from "react";
6163
+ import { useEffect as useEffect30, useState as useState30 } from "react";
5856
6164
  import { Box as Box31, Text as Text31 } from "ink";
5857
- import { jsx as jsx56, jsxs as jsxs31 } from "react/jsx-runtime";
6165
+ import { jsx as jsx58, jsxs as jsxs31 } from "react/jsx-runtime";
5858
6166
  var MAX_TAIL = 80;
5859
- var TITLE28 = "Progress Log";
6167
+ var TITLE29 = "Progress Log";
5860
6168
  var HINTS3 = [];
5861
6169
  function ProgressShowView({ sprintId } = {}) {
5862
- const [state, setState] = useState24({ kind: "loading" });
6170
+ const [state, setState] = useState30({ kind: "loading" });
5863
6171
  useViewHints(HINTS3);
5864
- useEffect23(() => {
6172
+ useEffect30(() => {
5865
6173
  const ctl = { cancelled: false };
5866
6174
  void (async () => {
5867
6175
  try {
@@ -5877,7 +6185,7 @@ function ProgressShowView({ sprintId } = {}) {
5877
6185
  ctl.cancelled = true;
5878
6186
  };
5879
6187
  }, [sprintId]);
5880
- return /* @__PURE__ */ jsx56(ViewShell, { title: TITLE28, children: state.kind === "loading" ? /* @__PURE__ */ jsx56(Spinner, { label: "Loading progress\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx56(ResultCard, { kind: "info", title: "No progress entries yet" }) : state.kind === "error" ? /* @__PURE__ */ jsx56(ResultCard, { kind: "error", title: "Could not load progress", lines: [state.message] }) : renderContent(state.content) });
6188
+ return /* @__PURE__ */ jsx58(ViewShell, { title: TITLE29, children: state.kind === "loading" ? /* @__PURE__ */ jsx58(Spinner, { label: "Loading progress\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx58(ResultCard, { kind: "info", title: "No progress entries yet" }) : state.kind === "error" ? /* @__PURE__ */ jsx58(ResultCard, { kind: "error", title: "Could not load progress", lines: [state.message] }) : renderContent(state.content) });
5881
6189
  }
5882
6190
  function renderContent(content) {
5883
6191
  const lines = content.split("\n");
@@ -5890,19 +6198,19 @@ function renderContent(content) {
5890
6198
  String(lines.length),
5891
6199
  " total)"
5892
6200
  ] }) : null,
5893
- tail.map((line2, i) => /* @__PURE__ */ jsx56(Text31, { dimColor: line2.trim().length === 0, children: line2.length > 0 ? line2 : " " }, i))
6201
+ tail.map((line2, i) => /* @__PURE__ */ jsx58(Text31, { dimColor: line2.trim().length === 0, children: line2.length > 0 ? line2 : " " }, i))
5894
6202
  ] });
5895
6203
  }
5896
6204
 
5897
6205
  // src/integration/ui/tui/views/workflows/progress-log-view.tsx
5898
- import { useMemo as useMemo31 } from "react";
5899
- import { jsx as jsx57 } from "react/jsx-runtime";
5900
- var TITLE29 = "Log Progress";
5901
- var HINTS_RUNNING21 = [
6206
+ import { useMemo as useMemo29 } from "react";
6207
+ import { jsx as jsx59 } from "react/jsx-runtime";
6208
+ var TITLE30 = "Log Progress";
6209
+ var HINTS_RUNNING18 = [
5902
6210
  { key: "Ctrl+D", action: "submit" },
5903
6211
  { key: "Esc", action: "cancel" }
5904
6212
  ];
5905
- var HINTS_DONE21 = [
6213
+ var HINTS_DONE18 = [
5906
6214
  { key: "Enter", action: "home" },
5907
6215
  { key: "Esc", action: "back" }
5908
6216
  ];
@@ -5925,29 +6233,29 @@ function ProgressLogView() {
5925
6233
  setPhase({ kind: "done" });
5926
6234
  }
5927
6235
  });
5928
- const hints = useMemo31(() => phase.kind === "running" ? HINTS_RUNNING21 : HINTS_DONE21, [phase.kind]);
6236
+ const hints = useMemo29(() => phase.kind === "running" ? HINTS_RUNNING18 : HINTS_DONE18, [phase.kind]);
5929
6237
  useViewHints(hints);
5930
- return /* @__PURE__ */ jsx57(ViewShell, { title: TITLE29, children: renderBody25(phase) });
6238
+ return /* @__PURE__ */ jsx59(ViewShell, { title: TITLE30, children: renderBody22(phase) });
5931
6239
  }
5932
- function renderBody25(phase) {
6240
+ function renderBody22(phase) {
5933
6241
  switch (phase.kind) {
5934
6242
  case "running":
5935
- return /* @__PURE__ */ jsx57(Spinner, { label: phase.step === "message" ? "Awaiting progress note\u2026" : "Saving progress note\u2026" });
6243
+ return /* @__PURE__ */ jsx59(Spinner, { label: phase.step === "message" ? "Awaiting progress note\u2026" : "Saving progress note\u2026" });
5936
6244
  case "cancelled":
5937
- return /* @__PURE__ */ jsx57(ResultCard, { kind: "info", title: "No note recorded" });
6245
+ return /* @__PURE__ */ jsx59(ResultCard, { kind: "info", title: "No note recorded" });
5938
6246
  case "error":
5939
- return /* @__PURE__ */ jsx57(ResultCard, { kind: "error", title: "Could not log progress", lines: [phase.message] });
6247
+ return /* @__PURE__ */ jsx59(ResultCard, { kind: "error", title: "Could not log progress", lines: [phase.message] });
5940
6248
  case "done":
5941
- return /* @__PURE__ */ jsx57(ResultCard, { kind: "success", title: "Progress logged" });
6249
+ return /* @__PURE__ */ jsx59(ResultCard, { kind: "success", title: "Progress logged" });
5942
6250
  }
5943
6251
  }
5944
6252
 
5945
6253
  // src/integration/ui/tui/views/workflows/ideate-view.tsx
5946
- import { useMemo as useMemo32 } from "react";
5947
- import { jsx as jsx58 } from "react/jsx-runtime";
5948
- var TITLE30 = "Ideate";
5949
- var HINTS_RUNNING22 = [{ key: "Esc", action: "cancel" }];
5950
- var HINTS_DONE22 = [
6254
+ import { useMemo as useMemo30 } from "react";
6255
+ import { jsx as jsx60 } from "react/jsx-runtime";
6256
+ var TITLE31 = "Ideate";
6257
+ var HINTS_RUNNING19 = [{ key: "Esc", action: "cancel" }];
6258
+ var HINTS_DONE19 = [
5951
6259
  { key: "Enter", action: "home" },
5952
6260
  { key: "Esc", action: "back" }
5953
6261
  ];
@@ -6001,22 +6309,22 @@ function IdeateView() {
6001
6309
  setPhase({ kind: "done", summary });
6002
6310
  }
6003
6311
  });
6004
- const hints = useMemo32(() => phase.kind === "running" ? HINTS_RUNNING22 : HINTS_DONE22, [phase.kind]);
6312
+ const hints = useMemo30(() => phase.kind === "running" ? HINTS_RUNNING19 : HINTS_DONE19, [phase.kind]);
6005
6313
  useViewHints(hints);
6006
- return /* @__PURE__ */ jsx58(ViewShell, { title: TITLE30, children: renderBody26(phase) });
6314
+ return /* @__PURE__ */ jsx60(ViewShell, { title: TITLE31, children: renderBody23(phase) });
6007
6315
  }
6008
- function renderBody26(phase) {
6316
+ function renderBody23(phase) {
6009
6317
  switch (phase.kind) {
6010
6318
  case "running":
6011
- return /* @__PURE__ */ jsx58(Spinner, { label: stepLabel12(phase.step) });
6319
+ return /* @__PURE__ */ jsx60(Spinner, { label: stepLabel9(phase.step) });
6012
6320
  case "no-projects":
6013
- return /* @__PURE__ */ jsx58(ResultCard, { kind: "warning", title: "Register a project first" });
6321
+ return /* @__PURE__ */ jsx60(ResultCard, { kind: "warning", title: "Register a project first" });
6014
6322
  case "no-draft-sprint":
6015
- return /* @__PURE__ */ jsx58(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
6323
+ return /* @__PURE__ */ jsx60(ResultCard, { kind: "warning", title: "Current sprint is not a draft" });
6016
6324
  case "error":
6017
- return /* @__PURE__ */ jsx58(ResultCard, { kind: "error", title: "Ideation failed", lines: [phase.message] });
6325
+ return /* @__PURE__ */ jsx60(ResultCard, { kind: "error", title: "Ideation failed", lines: [phase.message] });
6018
6326
  case "done":
6019
- return /* @__PURE__ */ jsx58(
6327
+ return /* @__PURE__ */ jsx60(
6020
6328
  ResultCard,
6021
6329
  {
6022
6330
  kind: "success",
@@ -6030,7 +6338,7 @@ function renderBody26(phase) {
6030
6338
  );
6031
6339
  }
6032
6340
  }
6033
- function stepLabel12(step) {
6341
+ function stepLabel9(step) {
6034
6342
  if (step === "title") return "Awaiting idea title\u2026";
6035
6343
  if (step === "description") return "Awaiting idea description\u2026";
6036
6344
  if (step === "project") return "Awaiting project selection\u2026";
@@ -6038,11 +6346,11 @@ function stepLabel12(step) {
6038
6346
  }
6039
6347
 
6040
6348
  // src/integration/ui/tui/views/onboarding-view.tsx
6041
- import { useMemo as useMemo33 } from "react";
6042
- import { jsx as jsx59 } from "react/jsx-runtime";
6043
- var TITLE31 = "Welcome";
6044
- var HINTS_RUNNING23 = [{ key: "Esc", action: "skip" }];
6045
- var HINTS_DONE23 = [
6349
+ import { useMemo as useMemo31 } from "react";
6350
+ import { jsx as jsx61 } from "react/jsx-runtime";
6351
+ var TITLE32 = "Welcome";
6352
+ var HINTS_RUNNING20 = [{ key: "Esc", action: "skip" }];
6353
+ var HINTS_DONE20 = [
6046
6354
  { key: "Enter", action: "home" },
6047
6355
  { key: "Esc", action: "home" }
6048
6356
  ];
@@ -6089,31 +6397,31 @@ function OnboardingView() {
6089
6397
  }
6090
6398
  }
6091
6399
  });
6092
- const hints = useMemo33(() => phase.kind === "running" ? HINTS_RUNNING23 : HINTS_DONE23, [phase.kind]);
6400
+ const hints = useMemo31(() => phase.kind === "running" ? HINTS_RUNNING20 : HINTS_DONE20, [phase.kind]);
6093
6401
  useViewHints(hints);
6094
- return /* @__PURE__ */ jsx59(ViewShell, { title: TITLE31, children: renderBody27(phase) });
6402
+ return /* @__PURE__ */ jsx61(ViewShell, { title: TITLE32, children: renderBody24(phase) });
6095
6403
  }
6096
- function renderBody27(phase) {
6404
+ function renderBody24(phase) {
6097
6405
  if (phase.kind === "running") {
6098
- return /* @__PURE__ */ jsx59(Spinner, { label: RUNNING_LABEL2[phase.step] });
6406
+ return /* @__PURE__ */ jsx61(Spinner, { label: RUNNING_LABEL2[phase.step] });
6099
6407
  }
6100
6408
  if (phase.kind === "error") {
6101
- return /* @__PURE__ */ jsx59(ResultCard, { kind: "error", title: "Onboarding failed", lines: [phase.message] });
6409
+ return /* @__PURE__ */ jsx61(ResultCard, { kind: "error", title: "Onboarding failed", lines: [phase.message] });
6102
6410
  }
6103
6411
  const providerLabel = phase.provider === "claude" ? "Claude Code" : phase.provider === "copilot" ? "GitHub Copilot" : "Skipped";
6104
6412
  const nextSteps = phase.addingProject ? [{ action: "Finish adding your project", description: "continuing to the project wizard\u2026" }] : [
6105
6413
  { action: "Register a project", description: "Browse \u2192 Projects \u2192 Add" },
6106
6414
  { action: "Create a sprint", description: "once a project exists" }
6107
6415
  ];
6108
- return /* @__PURE__ */ jsx59(ResultCard, { kind: "success", title: "You're set up", fields: [["AI provider", providerLabel]], nextSteps });
6416
+ return /* @__PURE__ */ jsx61(ResultCard, { kind: "success", title: "You're set up", fields: [["AI provider", providerLabel]], nextSteps });
6109
6417
  }
6110
6418
 
6111
6419
  // src/integration/ui/tui/views/workflows/reactivate-sprint-view.tsx
6112
- import { useMemo as useMemo34 } from "react";
6113
- import { jsx as jsx60 } from "react/jsx-runtime";
6114
- var TITLE32 = "Reactivate Sprint";
6115
- var HINTS_RUNNING24 = [{ key: "Esc", action: "cancel" }];
6116
- var HINTS_DONE24 = [
6420
+ import { useMemo as useMemo32 } from "react";
6421
+ import { jsx as jsx62 } from "react/jsx-runtime";
6422
+ var TITLE33 = "Reactivate Sprint";
6423
+ var HINTS_RUNNING21 = [{ key: "Esc", action: "cancel" }];
6424
+ var HINTS_DONE21 = [
6117
6425
  { key: "Enter", action: "back" },
6118
6426
  { key: "Esc", action: "back" }
6119
6427
  ];
@@ -6147,22 +6455,22 @@ function ReactivateSprintView({ sprintId }) {
6147
6455
  }
6148
6456
  });
6149
6457
  const running = phase.kind === "running" || phase.kind === "loading";
6150
- const hints = useMemo34(() => running ? HINTS_RUNNING24 : HINTS_DONE24, [running]);
6458
+ const hints = useMemo32(() => running ? HINTS_RUNNING21 : HINTS_DONE21, [running]);
6151
6459
  useViewHints(hints);
6152
- return /* @__PURE__ */ jsx60(ViewShell, { title: TITLE32, children: renderBody28(phase) });
6460
+ return /* @__PURE__ */ jsx62(ViewShell, { title: TITLE33, children: renderBody25(phase) });
6153
6461
  }
6154
- function renderBody28(phase) {
6462
+ function renderBody25(phase) {
6155
6463
  switch (phase.kind) {
6156
6464
  case "loading":
6157
- return /* @__PURE__ */ jsx60(Spinner, { label: "Loading sprint\u2026" });
6465
+ return /* @__PURE__ */ jsx62(Spinner, { label: "Loading sprint\u2026" });
6158
6466
  case "running":
6159
- return /* @__PURE__ */ jsx60(Spinner, { label: phase.step === "confirm" ? "Awaiting confirmation\u2026" : "Reactivating sprint\u2026" });
6467
+ return /* @__PURE__ */ jsx62(Spinner, { label: phase.step === "confirm" ? "Awaiting confirmation\u2026" : "Reactivating sprint\u2026" });
6160
6468
  case "cancelled":
6161
- return /* @__PURE__ */ jsx60(ResultCard, { kind: "info", title: "Reactivation cancelled" });
6469
+ return /* @__PURE__ */ jsx62(ResultCard, { kind: "info", title: "Reactivation cancelled" });
6162
6470
  case "missing-id":
6163
- return /* @__PURE__ */ jsx60(ResultCard, { kind: "error", title: "No sprint ID provided" });
6471
+ return /* @__PURE__ */ jsx62(ResultCard, { kind: "error", title: "No sprint ID provided" });
6164
6472
  case "not-closed":
6165
- return /* @__PURE__ */ jsx60(
6473
+ return /* @__PURE__ */ jsx62(
6166
6474
  ResultCard,
6167
6475
  {
6168
6476
  kind: "warning",
@@ -6171,9 +6479,9 @@ function renderBody28(phase) {
6171
6479
  }
6172
6480
  );
6173
6481
  case "error":
6174
- return /* @__PURE__ */ jsx60(ResultCard, { kind: "error", title: "Could not reactivate sprint", lines: [phase.message] });
6482
+ return /* @__PURE__ */ jsx62(ResultCard, { kind: "error", title: "Could not reactivate sprint", lines: [phase.message] });
6175
6483
  case "done":
6176
- return /* @__PURE__ */ jsx60(
6484
+ return /* @__PURE__ */ jsx62(
6177
6485
  ResultCard,
6178
6486
  {
6179
6487
  kind: "success",
@@ -6189,9 +6497,9 @@ function renderBody28(phase) {
6189
6497
  }
6190
6498
 
6191
6499
  // src/integration/ui/tui/views/browse/evaluations-view.tsx
6192
- import { useEffect as useEffect24, useState as useState25 } from "react";
6193
- import { jsx as jsx61 } from "react/jsx-runtime";
6194
- var TITLE33 = "Evaluations";
6500
+ import { useEffect as useEffect31, useState as useState31 } from "react";
6501
+ import { jsx as jsx63 } from "react/jsx-runtime";
6502
+ var TITLE34 = "Evaluations";
6195
6503
  var HINTS4 = [
6196
6504
  { key: "\u2191/\u2193", action: "navigate" },
6197
6505
  { key: "Enter", action: "open" }
@@ -6222,9 +6530,9 @@ var COLUMNS2 = [
6222
6530
  ];
6223
6531
  function EvaluationsView({ sprintId }) {
6224
6532
  const router = useRouter();
6225
- const [state, setState] = useState25({ kind: "loading" });
6533
+ const [state, setState] = useState31({ kind: "loading" });
6226
6534
  useViewHints(HINTS4);
6227
- useEffect24(() => {
6535
+ useEffect31(() => {
6228
6536
  const ctl = { cancelled: false };
6229
6537
  void (async () => {
6230
6538
  try {
@@ -6243,7 +6551,7 @@ function EvaluationsView({ sprintId }) {
6243
6551
  ctl.cancelled = true;
6244
6552
  };
6245
6553
  }, [sprintId]);
6246
- return /* @__PURE__ */ jsx61(ViewShell, { title: TITLE33, children: state.kind === "loading" ? /* @__PURE__ */ jsx61(Spinner, { label: "Loading evaluations\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx61(ResultCard, { kind: "info", title: "No evaluations yet", lines: ["Run the executor to collect evaluator output."] }) : state.kind === "error" ? /* @__PURE__ */ jsx61(ResultCard, { kind: "error", title: "Could not load evaluations", lines: [state.message] }) : /* @__PURE__ */ jsx61(
6554
+ return /* @__PURE__ */ jsx63(ViewShell, { title: TITLE34, children: state.kind === "loading" ? /* @__PURE__ */ jsx63(Spinner, { label: "Loading evaluations\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx63(ResultCard, { kind: "info", title: "No evaluations yet", lines: ["Run the executor to collect evaluator output."] }) : state.kind === "error" ? /* @__PURE__ */ jsx63(ResultCard, { kind: "error", title: "Could not load evaluations", lines: [state.message] }) : /* @__PURE__ */ jsx63(
6247
6555
  ListView,
6248
6556
  {
6249
6557
  rows: state.tasks,
@@ -6256,10 +6564,10 @@ function EvaluationsView({ sprintId }) {
6256
6564
  }
6257
6565
 
6258
6566
  // src/integration/ui/tui/views/browse/evaluation-show-view.tsx
6259
- import { useEffect as useEffect25, useState as useState26 } from "react";
6567
+ import { useEffect as useEffect32, useState as useState32 } from "react";
6260
6568
  import { Box as Box32, Text as Text32 } from "ink";
6261
- import { jsx as jsx62, jsxs as jsxs32 } from "react/jsx-runtime";
6262
- var TITLE34 = "Evaluation";
6569
+ import { jsx as jsx64, jsxs as jsxs32 } from "react/jsx-runtime";
6570
+ var TITLE35 = "Evaluation";
6263
6571
  var HINTS5 = [];
6264
6572
  var MAX_LINES = 200;
6265
6573
  function kindFor(status) {
@@ -6268,9 +6576,9 @@ function kindFor(status) {
6268
6576
  return "warning";
6269
6577
  }
6270
6578
  function EvaluationShowView({ sprintId, taskId }) {
6271
- const [state, setState] = useState26({ kind: "loading" });
6579
+ const [state, setState] = useState32({ kind: "loading" });
6272
6580
  useViewHints(HINTS5);
6273
- useEffect25(() => {
6581
+ useEffect32(() => {
6274
6582
  const ctl = { cancelled: false };
6275
6583
  void (async () => {
6276
6584
  try {
@@ -6290,22 +6598,22 @@ function EvaluationShowView({ sprintId, taskId }) {
6290
6598
  ctl.cancelled = true;
6291
6599
  };
6292
6600
  }, [sprintId, taskId]);
6293
- return /* @__PURE__ */ jsx62(ViewShell, { title: TITLE34, children: renderBody29(state) });
6601
+ return /* @__PURE__ */ jsx64(ViewShell, { title: TITLE35, children: renderBody26(state) });
6294
6602
  }
6295
- function renderBody29(state) {
6296
- if (state.kind === "loading") return /* @__PURE__ */ jsx62(Spinner, { label: "Loading evaluation\u2026" });
6603
+ function renderBody26(state) {
6604
+ if (state.kind === "loading") return /* @__PURE__ */ jsx64(Spinner, { label: "Loading evaluation\u2026" });
6297
6605
  if (state.kind === "error")
6298
- return /* @__PURE__ */ jsx62(ResultCard, { kind: "error", title: "Could not load evaluation", lines: [state.message] });
6606
+ return /* @__PURE__ */ jsx64(ResultCard, { kind: "error", title: "Could not load evaluation", lines: [state.message] });
6299
6607
  const { task, content } = state;
6300
6608
  const lines = content.split("\n");
6301
6609
  const tail = lines.length > MAX_LINES ? lines.slice(-MAX_LINES) : lines;
6302
6610
  return /* @__PURE__ */ jsxs32(Box32, { flexDirection: "column", children: [
6303
6611
  /* @__PURE__ */ jsxs32(Box32, { children: [
6304
- /* @__PURE__ */ jsx62(Text32, { bold: true, children: task.name }),
6305
- /* @__PURE__ */ jsx62(Text32, { children: " " }),
6306
- /* @__PURE__ */ jsx62(StatusChip, { label: task.evaluationStatus ?? "unknown", kind: kindFor(task.evaluationStatus) })
6612
+ /* @__PURE__ */ jsx64(Text32, { bold: true, children: task.name }),
6613
+ /* @__PURE__ */ jsx64(Text32, { children: " " }),
6614
+ /* @__PURE__ */ jsx64(StatusChip, { label: task.evaluationStatus ?? "unknown", kind: kindFor(task.evaluationStatus) })
6307
6615
  ] }),
6308
- content.trim().length === 0 ? /* @__PURE__ */ jsx62(Box32, { marginTop: spacing.section, children: /* @__PURE__ */ jsx62(Text32, { color: inkColors.muted, children: "No evaluator output recorded." }) }) : /* @__PURE__ */ jsxs32(Box32, { marginTop: spacing.section, flexDirection: "column", children: [
6616
+ content.trim().length === 0 ? /* @__PURE__ */ jsx64(Box32, { marginTop: spacing.section, children: /* @__PURE__ */ jsx64(Text32, { color: inkColors.muted, children: "No evaluator output recorded." }) }) : /* @__PURE__ */ jsxs32(Box32, { marginTop: spacing.section, flexDirection: "column", children: [
6309
6617
  lines.length > MAX_LINES ? /* @__PURE__ */ jsxs32(Text32, { dimColor: true, children: [
6310
6618
  "Showing last ",
6311
6619
  String(MAX_LINES),
@@ -6313,21 +6621,21 @@ function renderBody29(state) {
6313
6621
  String(lines.length),
6314
6622
  " total)"
6315
6623
  ] }) : null,
6316
- tail.map((line2, i) => /* @__PURE__ */ jsx62(Text32, { dimColor: line2.trim().length === 0, children: line2.length > 0 ? line2 : " " }, i))
6624
+ tail.map((line2, i) => /* @__PURE__ */ jsx64(Text32, { dimColor: line2.trim().length === 0, children: line2.length > 0 ? line2 : " " }, i))
6317
6625
  ] })
6318
6626
  ] });
6319
6627
  }
6320
6628
 
6321
6629
  // src/integration/ui/tui/views/browse/feedback-view.tsx
6322
- import { useEffect as useEffect26, useState as useState27 } from "react";
6630
+ import { useEffect as useEffect33, useState as useState33 } from "react";
6323
6631
  import { Box as Box33, Text as Text33 } from "ink";
6324
- import { jsx as jsx63, jsxs as jsxs33 } from "react/jsx-runtime";
6325
- var TITLE35 = "Feedback";
6632
+ import { jsx as jsx65, jsxs as jsxs33 } from "react/jsx-runtime";
6633
+ var TITLE36 = "Feedback";
6326
6634
  var HINTS6 = [];
6327
6635
  function FeedbackView({ sprintId }) {
6328
- const [state, setState] = useState27({ kind: "loading" });
6636
+ const [state, setState] = useState33({ kind: "loading" });
6329
6637
  useViewHints(HINTS6);
6330
- useEffect26(() => {
6638
+ useEffect33(() => {
6331
6639
  const ctl = { cancelled: false };
6332
6640
  void (async () => {
6333
6641
  try {
@@ -6344,16 +6652,16 @@ function FeedbackView({ sprintId }) {
6344
6652
  ctl.cancelled = true;
6345
6653
  };
6346
6654
  }, [sprintId]);
6347
- return /* @__PURE__ */ jsx63(ViewShell, { title: TITLE35, children: state.kind === "loading" ? /* @__PURE__ */ jsx63(Spinner, { label: "Loading feedback\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx63(
6655
+ return /* @__PURE__ */ jsx65(ViewShell, { title: TITLE36, children: state.kind === "loading" ? /* @__PURE__ */ jsx65(Spinner, { label: "Loading feedback\u2026" }) : state.kind === "empty" ? /* @__PURE__ */ jsx65(
6348
6656
  ResultCard,
6349
6657
  {
6350
6658
  kind: "info",
6351
6659
  title: "No feedback yet",
6352
6660
  lines: ["Feedback is captured during the post-execution loop."]
6353
6661
  }
6354
- ) : state.kind === "error" ? /* @__PURE__ */ jsx63(ResultCard, { kind: "error", title: "Could not load feedback", lines: [state.message] }) : /* @__PURE__ */ jsx63(Box33, { flexDirection: "column", children: state.entries.map((entry, i) => /* @__PURE__ */ jsxs33(Box33, { marginTop: i === 0 ? 0 : spacing.section, flexDirection: "column", children: [
6355
- /* @__PURE__ */ jsx63(Text33, { color: inkColors.muted, bold: true, children: entry.timestamp }),
6356
- /* @__PURE__ */ jsx63(Text33, { children: entry.preview })
6662
+ ) : state.kind === "error" ? /* @__PURE__ */ jsx65(ResultCard, { kind: "error", title: "Could not load feedback", lines: [state.message] }) : /* @__PURE__ */ jsx65(Box33, { flexDirection: "column", children: state.entries.map((entry, i) => /* @__PURE__ */ jsxs33(Box33, { marginTop: i === 0 ? 0 : spacing.section, flexDirection: "column", children: [
6663
+ /* @__PURE__ */ jsx65(Text33, { color: inkColors.muted, bold: true, children: entry.timestamp }),
6664
+ /* @__PURE__ */ jsx65(Text33, { children: entry.preview })
6357
6665
  ] }, i)) }) });
6358
6666
  }
6359
6667
  function extractFeedback(progress) {
@@ -6371,9 +6679,210 @@ function extractFeedback(progress) {
6371
6679
  return out;
6372
6680
  }
6373
6681
 
6682
+ // src/integration/ui/tui/views/running-executions-view.tsx
6683
+ import { useCallback as useCallback11, useEffect as useEffect34, useMemo as useMemo33, useState as useState34 } from "react";
6684
+ import { Box as Box34, Text as Text34, useInput as useInput21 } from "ink";
6685
+ import { jsx as jsx66, jsxs as jsxs34 } from "react/jsx-runtime";
6686
+ var HINTS_POPULATED = [
6687
+ { key: "\u2191/\u2193", action: "navigate" },
6688
+ { key: "Enter", action: "open" },
6689
+ { key: "X", action: "cancel" },
6690
+ { key: "Esc", action: "back" }
6691
+ ];
6692
+ var HINTS_EMPTY7 = [{ key: "Esc", action: "back" }];
6693
+ function formatRelativeTime(from, now = /* @__PURE__ */ new Date()) {
6694
+ const deltaMs = Math.max(0, now.getTime() - from.getTime());
6695
+ const seconds = Math.floor(deltaMs / 1e3);
6696
+ if (seconds < 60) return `${String(seconds)}s ago`;
6697
+ const minutes = Math.floor(seconds / 60);
6698
+ if (minutes < 60) return `${String(minutes)}m ago`;
6699
+ const hours = Math.floor(minutes / 60);
6700
+ if (hours < 24) return `${String(hours)}h ago`;
6701
+ const days = Math.floor(hours / 24);
6702
+ return `${String(days)}d ago`;
6703
+ }
6704
+ function statusColor6(kind) {
6705
+ const map = {
6706
+ info: inkColors.info,
6707
+ success: inkColors.success,
6708
+ warning: inkColors.warning,
6709
+ error: inkColors.error,
6710
+ muted: inkColors.muted
6711
+ };
6712
+ return map[kind];
6713
+ }
6714
+ function buildColumns4(now) {
6715
+ return [
6716
+ {
6717
+ header: "Status",
6718
+ cell: (e) => `[${e.status.toUpperCase()}]`,
6719
+ color: (e) => statusColor6(chipKindForExecutionStatus(e.status)),
6720
+ width: 12
6721
+ },
6722
+ { header: "Project", cell: (e) => e.projectName, width: 20 },
6723
+ { header: "Sprint", cell: (e) => e.sprint.name, flex: true },
6724
+ {
6725
+ header: "Started",
6726
+ cell: (e) => formatRelativeTime(e.startedAt, now),
6727
+ width: 10,
6728
+ align: "right"
6729
+ }
6730
+ ];
6731
+ }
6732
+ function RunningExecutionsView() {
6733
+ const router = useRouter();
6734
+ const shared = getSharedDeps();
6735
+ const registry = shared.executionRegistry;
6736
+ const executions = useRegistryEvents(registry);
6737
+ const [cursor, setCursor] = useState34(0);
6738
+ const rows = useMemo33(() => {
6739
+ return [...executions].sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
6740
+ }, [executions]);
6741
+ const [now, setNow] = useState34(() => /* @__PURE__ */ new Date());
6742
+ useEffect34(() => {
6743
+ const interval = setInterval(() => {
6744
+ setNow(/* @__PURE__ */ new Date());
6745
+ }, 1e3);
6746
+ return () => {
6747
+ clearInterval(interval);
6748
+ };
6749
+ }, []);
6750
+ const columns = useMemo33(() => buildColumns4(now), [now]);
6751
+ const handleSelect = useCallback11(
6752
+ (execution) => {
6753
+ router.push({
6754
+ id: "execute",
6755
+ props: { sprintId: execution.sprintId, executionId: execution.id }
6756
+ });
6757
+ },
6758
+ [router]
6759
+ );
6760
+ const handleCursorChange = useCallback11((_row, index) => {
6761
+ setCursor(index);
6762
+ }, []);
6763
+ useViewHints(rows.length > 0 ? HINTS_POPULATED : HINTS_EMPTY7);
6764
+ useInput21((input) => {
6765
+ if (rows.length === 0) return;
6766
+ if (input !== "X") return;
6767
+ const target = rows[cursor];
6768
+ if (target?.status === "running") {
6769
+ registry.cancel(target.id);
6770
+ }
6771
+ });
6772
+ if (rows.length === 0) {
6773
+ return /* @__PURE__ */ jsx66(ViewShell, { title: "Running Executions", children: /* @__PURE__ */ jsx66(
6774
+ ResultCard,
6775
+ {
6776
+ kind: "info",
6777
+ title: "No backgrounded executions",
6778
+ lines: ["Start a sprint from home to see it here while it runs."],
6779
+ nextSteps: [{ action: "Press h to return home", description: "Then pick a sprint and start it." }]
6780
+ }
6781
+ ) });
6782
+ }
6783
+ return /* @__PURE__ */ jsx66(ViewShell, { title: "Running Executions", children: /* @__PURE__ */ jsxs34(Box34, { flexDirection: "column", children: [
6784
+ /* @__PURE__ */ jsx66(
6785
+ ListView,
6786
+ {
6787
+ rows,
6788
+ columns,
6789
+ onSelect: handleSelect,
6790
+ onCursorChange: handleCursorChange,
6791
+ emptyLabel: "No executions"
6792
+ }
6793
+ ),
6794
+ /* @__PURE__ */ jsx66(Box34, { marginTop: spacing.section, children: /* @__PURE__ */ jsxs34(Text34, { dimColor: true, children: [
6795
+ rows.length,
6796
+ " execution",
6797
+ rows.length === 1 ? "" : "s",
6798
+ " tracked this session."
6799
+ ] }) })
6800
+ ] }) });
6801
+ }
6802
+
6803
+ // src/integration/ui/tui/components/execution-notification-banner.tsx
6804
+ import { useEffect as useEffect35, useRef as useRef4, useState as useState35 } from "react";
6805
+ import { Box as Box35, Text as Text35 } from "ink";
6806
+ import { jsx as jsx67, jsxs as jsxs35 } from "react/jsx-runtime";
6807
+ function isTerminal(status) {
6808
+ return status !== "running";
6809
+ }
6810
+ function isNotifiable(status) {
6811
+ return status === "completed" || status === "failed";
6812
+ }
6813
+ function bannerColor(status) {
6814
+ if (status === "completed") return inkColors.success;
6815
+ if (status === "failed") return inkColors.error;
6816
+ return inkColors.muted;
6817
+ }
6818
+ function bannerGlyph(status) {
6819
+ if (status === "completed") return glyphs.check;
6820
+ if (status === "failed") return glyphs.cross;
6821
+ return glyphs.warningGlyph;
6822
+ }
6823
+ function bannerLabel(status) {
6824
+ if (status === "completed") return "DONE";
6825
+ if (status === "failed") return "FAILED";
6826
+ return "ENDED";
6827
+ }
6828
+ function ExecutionNotificationBanner({ currentViewId, registry }) {
6829
+ const executions = useRegistryEvents(registry);
6830
+ const shownRef = useRef4(/* @__PURE__ */ new Set());
6831
+ const visitedRef = useRef4(/* @__PURE__ */ new Set());
6832
+ const [pending, setPending] = useState35(null);
6833
+ useEffect35(() => {
6834
+ if (currentViewId === "running-executions") {
6835
+ for (const e of executions) {
6836
+ if (isTerminal(e.status)) visitedRef.current.add(e.id);
6837
+ }
6838
+ if (pending && visitedRef.current.has(pending.id)) {
6839
+ setPending(null);
6840
+ }
6841
+ }
6842
+ }, [currentViewId, executions, pending]);
6843
+ useEffect35(() => {
6844
+ for (const e of executions) {
6845
+ if (!isTerminal(e.status)) continue;
6846
+ if (!isNotifiable(e.status)) continue;
6847
+ if (shownRef.current.has(e.id)) continue;
6848
+ if (visitedRef.current.has(e.id)) {
6849
+ shownRef.current.add(e.id);
6850
+ continue;
6851
+ }
6852
+ shownRef.current.add(e.id);
6853
+ setPending(e);
6854
+ break;
6855
+ }
6856
+ }, [executions]);
6857
+ if (!pending) return null;
6858
+ const color2 = bannerColor(pending.status);
6859
+ const icon = bannerGlyph(pending.status);
6860
+ const label = bannerLabel(pending.status);
6861
+ return /* @__PURE__ */ jsxs35(Box35, { borderStyle: "round", borderColor: color2, paddingX: spacing.cardPadX, marginBottom: spacing.section, children: [
6862
+ /* @__PURE__ */ jsxs35(Text35, { color: color2, bold: true, children: [
6863
+ icon,
6864
+ " "
6865
+ ] }),
6866
+ /* @__PURE__ */ jsxs35(Text35, { bold: true, children: [
6867
+ pending.projectName,
6868
+ " ",
6869
+ glyphs.inlineDot,
6870
+ " ",
6871
+ pending.sprint.name
6872
+ ] }),
6873
+ /* @__PURE__ */ jsxs35(Text35, { color: color2, bold: true, children: [
6874
+ " ",
6875
+ label
6876
+ ] }),
6877
+ /* @__PURE__ */ jsx67(Text35, { dimColor: true, children: ` ${glyphs.emDash} press ` }),
6878
+ /* @__PURE__ */ jsx67(Text35, { bold: true, children: "x" }),
6879
+ /* @__PURE__ */ jsx67(Text35, { dimColor: true, children: " for the runs list" })
6880
+ ] });
6881
+ }
6882
+
6374
6883
  // src/integration/ui/tui/components/version-hint.tsx
6375
- import { useEffect as useEffect27, useState as useState28 } from "react";
6376
- import { Text as Text34 } from "ink";
6884
+ import { useEffect as useEffect36, useState as useState36 } from "react";
6885
+ import { Text as Text36 } from "ink";
6377
6886
 
6378
6887
  // src/integration/external/version-check.ts
6379
6888
  import { mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
@@ -6461,10 +6970,10 @@ async function checkLatestVersion() {
6461
6970
  }
6462
6971
 
6463
6972
  // src/integration/ui/tui/components/version-hint.tsx
6464
- import { jsx as jsx64 } from "react/jsx-runtime";
6973
+ import { jsx as jsx68 } from "react/jsx-runtime";
6465
6974
  function VersionHint() {
6466
- const [check, setCheck] = useState28(null);
6467
- useEffect27(() => {
6975
+ const [check, setCheck] = useState36(null);
6976
+ useEffect36(() => {
6468
6977
  let cancelled = false;
6469
6978
  void checkLatestVersion().then((result) => {
6470
6979
  if (!cancelled && result !== null) setCheck(result);
@@ -6474,167 +6983,180 @@ function VersionHint() {
6474
6983
  };
6475
6984
  }, []);
6476
6985
  if (!check?.updateAvailable) return null;
6477
- return /* @__PURE__ */ jsx64(Text34, { dimColor: true, children: `v${check.latest} available \xB7 npm install -g ralphctl` });
6986
+ return /* @__PURE__ */ jsx68(Text36, { dimColor: true, children: `v${check.latest} available \xB7 npm install -g ralphctl` });
6478
6987
  }
6479
6988
 
6480
6989
  // src/integration/ui/tui/views/view-router.tsx
6481
- import { jsx as jsx65, jsxs as jsxs34 } from "react/jsx-runtime";
6990
+ import { jsx as jsx69, jsxs as jsxs36 } from "react/jsx-runtime";
6482
6991
  var views = {
6483
6992
  home: {
6484
6993
  label: "Home",
6485
- render: () => /* @__PURE__ */ jsx65(HomeView, {})
6994
+ render: () => /* @__PURE__ */ jsx69(HomeView, {})
6486
6995
  },
6487
6996
  settings: {
6488
6997
  label: "Settings",
6489
- render: () => /* @__PURE__ */ jsx65(SettingsView, {})
6998
+ render: () => /* @__PURE__ */ jsx69(SettingsView, {})
6490
6999
  },
6491
7000
  execute: {
6492
7001
  label: "Execute",
6493
7002
  render: (props) => {
6494
7003
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : "";
7004
+ const executionId = typeof props["executionId"] === "string" ? props["executionId"] : void 0;
6495
7005
  const executionOptions = props["executionOptions"];
6496
- return /* @__PURE__ */ jsx65(ExecuteView, { sprintId, executionOptions });
7006
+ return /* @__PURE__ */ jsx69(ExecuteView, { sprintId, executionId, executionOptions });
6497
7007
  }
6498
7008
  },
6499
7009
  dashboard: {
6500
7010
  label: "Dashboard",
6501
- render: () => /* @__PURE__ */ jsx65(DashboardView, {})
7011
+ render: () => /* @__PURE__ */ jsx69(DashboardView, {})
7012
+ },
7013
+ "running-executions": {
7014
+ label: "Runs",
7015
+ render: () => /* @__PURE__ */ jsx69(RunningExecutionsView, {})
6502
7016
  },
6503
7017
  "refine-phase": {
6504
7018
  label: "Refine",
6505
7019
  render: (props) => {
6506
7020
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : "";
6507
- return /* @__PURE__ */ jsx65(RefinePhaseView, { sprintId });
7021
+ return /* @__PURE__ */ jsx69(RefinePhaseView, { sprintId });
6508
7022
  }
6509
7023
  },
6510
7024
  "plan-phase": {
6511
7025
  label: "Plan",
6512
7026
  render: (props) => {
6513
7027
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : "";
6514
- return /* @__PURE__ */ jsx65(PlanPhaseView, { sprintId });
7028
+ return /* @__PURE__ */ jsx69(PlanPhaseView, { sprintId });
6515
7029
  }
6516
7030
  },
6517
7031
  "close-phase": {
6518
7032
  label: "Close",
6519
7033
  render: (props) => {
6520
7034
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : "";
6521
- return /* @__PURE__ */ jsx65(ClosePhaseView, { sprintId });
7035
+ return /* @__PURE__ */ jsx69(ClosePhaseView, { sprintId });
6522
7036
  }
6523
7037
  },
6524
7038
  "sprint-create": {
6525
7039
  label: "Create Sprint",
6526
- render: () => /* @__PURE__ */ jsx65(CreateSprintView, {})
7040
+ render: () => /* @__PURE__ */ jsx69(CreateSprintView, {})
6527
7041
  },
6528
7042
  "sprint-delete": {
6529
7043
  label: "Delete Sprint",
6530
7044
  render: (props) => {
6531
7045
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6532
- return /* @__PURE__ */ jsx65(DeleteSprintView, { sprintId });
7046
+ return /* @__PURE__ */ jsx69(DeleteSprintView, { sprintId });
6533
7047
  }
6534
7048
  },
6535
7049
  "sprint-set-current": {
6536
7050
  label: "Set Current",
6537
- render: () => /* @__PURE__ */ jsx65(SetCurrentSprintView, {})
7051
+ render: () => /* @__PURE__ */ jsx69(SetCurrentSprintView, {})
6538
7052
  },
6539
7053
  "sprint-requirements-export": {
6540
7054
  label: "Requirements",
6541
7055
  render: (props) => {
6542
7056
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6543
- return /* @__PURE__ */ jsx65(RequirementsExportView, { sprintId });
7057
+ return /* @__PURE__ */ jsx69(RequirementsExportView, { sprintId });
6544
7058
  }
6545
7059
  },
6546
7060
  "sprint-context-export": {
6547
7061
  label: "Context",
6548
7062
  render: (props) => {
6549
7063
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6550
- return /* @__PURE__ */ jsx65(ContextExportView, { sprintId });
7064
+ return /* @__PURE__ */ jsx69(ContextExportView, { sprintId });
6551
7065
  }
6552
7066
  },
6553
7067
  "ticket-add": {
6554
7068
  label: "Add Ticket",
6555
- render: () => /* @__PURE__ */ jsx65(TicketAddView, {})
7069
+ render: () => /* @__PURE__ */ jsx69(TicketAddView, {})
6556
7070
  },
6557
7071
  "ticket-edit": {
6558
7072
  label: "Edit Ticket",
6559
7073
  render: (props) => {
6560
7074
  const ticketId = typeof props["ticketId"] === "string" ? props["ticketId"] : void 0;
6561
- return /* @__PURE__ */ jsx65(TicketEditView, { ticketId });
7075
+ return /* @__PURE__ */ jsx69(TicketEditView, { ticketId });
6562
7076
  }
6563
7077
  },
6564
7078
  "ticket-remove": {
6565
7079
  label: "Remove Ticket",
6566
- render: () => /* @__PURE__ */ jsx65(TicketRemoveView, {})
7080
+ render: () => /* @__PURE__ */ jsx69(TicketRemoveView, {})
6567
7081
  },
6568
7082
  "ticket-refine": {
6569
7083
  label: "Re-Refine Ticket",
6570
- render: () => /* @__PURE__ */ jsx65(TicketRefineView, {})
7084
+ render: () => /* @__PURE__ */ jsx69(TicketRefineView, {})
6571
7085
  },
6572
- "task-add": { label: "Add Task", render: () => /* @__PURE__ */ jsx65(TaskAddView, {}) },
6573
- "task-import": { label: "Import Tasks", render: () => /* @__PURE__ */ jsx65(TaskImportView, {}) },
6574
- "task-status": { label: "Task Status", render: () => /* @__PURE__ */ jsx65(TaskStatusView, {}) },
6575
- "task-reorder": { label: "Reorder Task", render: () => /* @__PURE__ */ jsx65(TaskReorderView, {}) },
6576
- "task-remove": { label: "Remove Task", render: () => /* @__PURE__ */ jsx65(TaskRemoveView, {}) },
6577
- "task-next": { label: "Next Task", render: () => /* @__PURE__ */ jsx65(TaskNextView, {}) },
6578
- "project-add": { label: "Add Project", render: () => /* @__PURE__ */ jsx65(ProjectAddView, {}) },
6579
- "project-remove": { label: "Remove Project", render: () => /* @__PURE__ */ jsx65(ProjectRemoveView, {}) },
6580
- "project-repo-add": { label: "Add Repository", render: () => /* @__PURE__ */ jsx65(ProjectRepoAddView, {}) },
6581
- "project-repo-remove": { label: "Remove Repository", render: () => /* @__PURE__ */ jsx65(ProjectRepoRemoveView, {}) },
6582
- "project-edit": { label: "Edit Project", render: () => /* @__PURE__ */ jsx65(ProjectEditView, {}) },
6583
- "sprint-list": { label: "Sprints", render: () => /* @__PURE__ */ jsx65(SprintListView, {}) },
7086
+ "task-add": { label: "Add Task", render: () => /* @__PURE__ */ jsx69(TaskAddView, {}) },
7087
+ "task-import": { label: "Import Tasks", render: () => /* @__PURE__ */ jsx69(TaskImportView, {}) },
7088
+ "task-status": { label: "Task Status", render: () => /* @__PURE__ */ jsx69(TaskStatusView, {}) },
7089
+ "task-reorder": { label: "Reorder Task", render: () => /* @__PURE__ */ jsx69(TaskReorderView, {}) },
7090
+ "task-remove": { label: "Remove Task", render: () => /* @__PURE__ */ jsx69(TaskRemoveView, {}) },
7091
+ "task-next": { label: "Next Task", render: () => /* @__PURE__ */ jsx69(TaskNextView, {}) },
7092
+ "project-add": { label: "Add Project", render: () => /* @__PURE__ */ jsx69(ProjectAddView, {}) },
7093
+ "project-remove": { label: "Remove Project", render: () => /* @__PURE__ */ jsx69(ProjectRemoveView, {}) },
7094
+ "project-repo-add": { label: "Add Repository", render: () => /* @__PURE__ */ jsx69(ProjectRepoAddView, {}) },
7095
+ "project-repo-remove": { label: "Remove Repository", render: () => /* @__PURE__ */ jsx69(ProjectRepoRemoveView, {}) },
7096
+ "project-edit": { label: "Edit Project", render: () => /* @__PURE__ */ jsx69(ProjectEditView, {}) },
7097
+ "project-onboard": {
7098
+ label: "Onboard Repository",
7099
+ render: (props) => {
7100
+ const projectName = typeof props["projectName"] === "string" ? props["projectName"] : void 0;
7101
+ const repo = typeof props["repo"] === "string" ? props["repo"] : void 0;
7102
+ return /* @__PURE__ */ jsx69(ProjectOnboardView, { projectName, repo });
7103
+ }
7104
+ },
7105
+ "sprint-list": { label: "Sprints", render: () => /* @__PURE__ */ jsx69(SprintListView, {}) },
6584
7106
  "sprint-show": {
6585
7107
  label: "Sprint",
6586
7108
  render: (props) => {
6587
7109
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6588
- return /* @__PURE__ */ jsx65(SprintShowView, { sprintId });
7110
+ return /* @__PURE__ */ jsx69(SprintShowView, { sprintId });
6589
7111
  }
6590
7112
  },
6591
- "ticket-list": { label: "Tickets", render: () => /* @__PURE__ */ jsx65(TicketListView, {}) },
7113
+ "ticket-list": { label: "Tickets", render: () => /* @__PURE__ */ jsx69(TicketListView, {}) },
6592
7114
  "ticket-show": {
6593
7115
  label: "Ticket",
6594
7116
  render: (props) => {
6595
7117
  const ticketId = typeof props["ticketId"] === "string" ? props["ticketId"] : void 0;
6596
- return /* @__PURE__ */ jsx65(TicketShowView, { ticketId });
7118
+ return /* @__PURE__ */ jsx69(TicketShowView, { ticketId });
6597
7119
  }
6598
7120
  },
6599
- "task-list": { label: "Tasks", render: () => /* @__PURE__ */ jsx65(TaskListView, {}) },
7121
+ "task-list": { label: "Tasks", render: () => /* @__PURE__ */ jsx69(TaskListView, {}) },
6600
7122
  "task-show": {
6601
7123
  label: "Task",
6602
7124
  render: (props) => {
6603
7125
  const taskId = typeof props["taskId"] === "string" ? props["taskId"] : void 0;
6604
- return /* @__PURE__ */ jsx65(TaskShowView, { taskId });
7126
+ return /* @__PURE__ */ jsx69(TaskShowView, { taskId });
6605
7127
  }
6606
7128
  },
6607
- "project-list": { label: "Projects", render: () => /* @__PURE__ */ jsx65(ProjectListView, {}) },
7129
+ "project-list": { label: "Projects", render: () => /* @__PURE__ */ jsx69(ProjectListView, {}) },
6608
7130
  "project-show": {
6609
7131
  label: "Project",
6610
7132
  render: (props) => {
6611
7133
  const projectName = typeof props["projectName"] === "string" ? props["projectName"] : void 0;
6612
- return /* @__PURE__ */ jsx65(ProjectShowView, { projectName });
7134
+ return /* @__PURE__ */ jsx69(ProjectShowView, { projectName });
6613
7135
  }
6614
7136
  },
6615
- doctor: { label: "Doctor", render: () => /* @__PURE__ */ jsx65(DoctorView, {}) },
6616
- "progress-log": { label: "Log Progress", render: () => /* @__PURE__ */ jsx65(ProgressLogView, {}) },
7137
+ doctor: { label: "Doctor", render: () => /* @__PURE__ */ jsx69(DoctorView, {}) },
7138
+ "progress-log": { label: "Log Progress", render: () => /* @__PURE__ */ jsx69(ProgressLogView, {}) },
6617
7139
  "progress-show": {
6618
7140
  label: "Progress",
6619
7141
  render: (props) => {
6620
7142
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6621
- return /* @__PURE__ */ jsx65(ProgressShowView, { sprintId });
7143
+ return /* @__PURE__ */ jsx69(ProgressShowView, { sprintId });
6622
7144
  }
6623
7145
  },
6624
- ideate: { label: "Ideate", render: () => /* @__PURE__ */ jsx65(IdeateView, {}) },
6625
- onboarding: { label: "Welcome", render: () => /* @__PURE__ */ jsx65(OnboardingView, {}) },
7146
+ ideate: { label: "Ideate", render: () => /* @__PURE__ */ jsx69(IdeateView, {}) },
7147
+ onboarding: { label: "Welcome", render: () => /* @__PURE__ */ jsx69(OnboardingView, {}) },
6626
7148
  "sprint-reactivate": {
6627
7149
  label: "Reactivate Sprint",
6628
7150
  render: (props) => {
6629
7151
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6630
- return /* @__PURE__ */ jsx65(ReactivateSprintView, { sprintId });
7152
+ return /* @__PURE__ */ jsx69(ReactivateSprintView, { sprintId });
6631
7153
  }
6632
7154
  },
6633
7155
  evaluations: {
6634
7156
  label: "Evaluations",
6635
7157
  render: (props) => {
6636
7158
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6637
- return /* @__PURE__ */ jsx65(EvaluationsView, { sprintId });
7159
+ return /* @__PURE__ */ jsx69(EvaluationsView, { sprintId });
6638
7160
  }
6639
7161
  },
6640
7162
  "evaluation-show": {
@@ -6642,28 +7164,32 @@ var views = {
6642
7164
  render: (props) => {
6643
7165
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : "";
6644
7166
  const taskId = typeof props["taskId"] === "string" ? props["taskId"] : "";
6645
- return /* @__PURE__ */ jsx65(EvaluationShowView, { sprintId, taskId });
7167
+ return /* @__PURE__ */ jsx69(EvaluationShowView, { sprintId, taskId });
6646
7168
  }
6647
7169
  },
6648
7170
  feedback: {
6649
7171
  label: "Feedback",
6650
7172
  render: (props) => {
6651
7173
  const sprintId = typeof props["sprintId"] === "string" ? props["sprintId"] : void 0;
6652
- return /* @__PURE__ */ jsx65(FeedbackView, { sprintId });
7174
+ return /* @__PURE__ */ jsx69(FeedbackView, { sprintId });
6653
7175
  }
6654
7176
  }
6655
7177
  };
6656
7178
  function ViewRouter({ initialStack }) {
6657
- const app = useApp();
6658
- const [stack, setStack] = useState29(() => {
7179
+ const [stack, setStack] = useState37(() => {
6659
7180
  if (initialStack.length === 0) {
6660
7181
  return [{ id: "home" }];
6661
7182
  }
6662
7183
  return collapseAdjacentDuplicates(initialStack);
6663
7184
  });
6664
- const stackRef = useRef3(stack);
6665
- stackRef.current = stack;
6666
- const push = useCallback10((entry) => {
7185
+ const registry = useMemo34(() => {
7186
+ try {
7187
+ return getSharedDeps().executionRegistry;
7188
+ } catch {
7189
+ return null;
7190
+ }
7191
+ }, []);
7192
+ const push = useCallback12((entry) => {
6667
7193
  setStack((s) => {
6668
7194
  const top = s[s.length - 1];
6669
7195
  if (top?.id === entry.id && samePropsBag(top.props, entry.props)) {
@@ -6672,72 +7198,43 @@ function ViewRouter({ initialStack }) {
6672
7198
  return [...s, entry];
6673
7199
  });
6674
7200
  }, []);
6675
- const pop = useCallback10(() => {
7201
+ const pop = useCallback12(() => {
6676
7202
  setStack((s) => s.length > 1 ? s.slice(0, -1) : s);
6677
7203
  }, []);
6678
- const replace = useCallback10((entry) => {
7204
+ const replace = useCallback12((entry) => {
6679
7205
  setStack((s) => s.length === 0 ? [entry] : [...s.slice(0, -1), entry]);
6680
7206
  }, []);
6681
- const reset = useCallback10((entry) => {
7207
+ const reset = useCallback12((entry) => {
6682
7208
  setStack(() => [entry]);
6683
7209
  }, []);
6684
7210
  const current = stack[stack.length - 1];
6685
7211
  if (current === void 0) {
6686
7212
  throw new Error("ViewRouter stack is empty");
6687
7213
  }
6688
- const api = useMemo35(
7214
+ const api = useMemo34(
6689
7215
  () => ({ current, stack, push, pop, replace, reset }),
6690
7216
  [current, stack, push, pop, replace, reset]
6691
7217
  );
6692
- const currentPrompt = useCurrentPrompt();
6693
- const routerHotkeysActive = currentPrompt === null;
6694
- useInput19(
6695
- (input, key) => {
6696
- if (key.escape) {
6697
- pop();
6698
- return;
6699
- }
6700
- if (input === "h") {
6701
- reset({ id: "home" });
6702
- return;
6703
- }
6704
- if (input === "s" && current.id !== "settings") {
6705
- push({ id: "settings" });
6706
- return;
6707
- }
6708
- if (input === "d" && current.id !== "dashboard") {
6709
- push({ id: "dashboard" });
6710
- return;
6711
- }
6712
- if (input === "?" && current.id !== "doctor") {
6713
- push({ id: "doctor" });
6714
- return;
6715
- }
6716
- if (input === "q" && stackRef.current.length === 1 && current.id === "home") {
6717
- app.exit();
6718
- }
6719
- },
6720
- { isActive: routerHotkeysActive }
6721
- );
6722
7218
  const meta = views[current.id];
6723
7219
  const props = current.props ?? {};
6724
- return /* @__PURE__ */ jsx65(RouterProvider, { value: api, children: /* @__PURE__ */ jsx65(ViewHintsProvider, { children: /* @__PURE__ */ jsxs34(Box34, { flexDirection: "column", children: [
6725
- /* @__PURE__ */ jsx65(Banner, {}),
7220
+ return /* @__PURE__ */ jsx69(RouterProvider, { value: api, children: /* @__PURE__ */ jsx69(ViewHintsProvider, { children: /* @__PURE__ */ jsxs36(Box36, { flexDirection: "column", children: [
7221
+ /* @__PURE__ */ jsx69(Banner, {}),
7222
+ /* @__PURE__ */ jsx69(ExecutionNotificationBanner, { currentViewId: current.id, registry }),
6726
7223
  meta.render(props),
6727
- /* @__PURE__ */ jsx65(PromptHost, {}),
6728
- /* @__PURE__ */ jsx65(Box34, { marginTop: 1, children: /* @__PURE__ */ jsx65(KeyboardHints, {}) }),
6729
- /* @__PURE__ */ jsxs34(
6730
- Box34,
7224
+ /* @__PURE__ */ jsx69(PromptHost, {}),
7225
+ /* @__PURE__ */ jsx69(Box36, { marginTop: spacing.section, children: /* @__PURE__ */ jsx69(KeyboardHints, {}) }),
7226
+ /* @__PURE__ */ jsxs36(
7227
+ Box36,
6731
7228
  {
6732
- marginTop: 1,
7229
+ marginTop: spacing.section,
6733
7230
  borderStyle: "round",
6734
7231
  borderColor: inkColors.primary,
6735
7232
  borderDimColor: true,
6736
7233
  paddingX: spacing.indent,
6737
7234
  justifyContent: "space-between",
6738
7235
  children: [
6739
- /* @__PURE__ */ jsx65(StatusBar, { breadcrumb: stack.map((e) => views[e.id].label), hints: buildHints(current.id, stack.length) }),
6740
- /* @__PURE__ */ jsx65(VersionHint, {})
7236
+ /* @__PURE__ */ jsx69(StatusBar, { breadcrumb: stack.map((e) => views[e.id].label), hints: buildHints(current.id, stack.length) }),
7237
+ /* @__PURE__ */ jsx69(VersionHint, {})
6741
7238
  ]
6742
7239
  }
6743
7240
  )
@@ -6777,6 +7274,9 @@ function buildHints(currentId, depth) {
6777
7274
  if (currentId !== "doctor") {
6778
7275
  hints.push({ key: "?", action: "doctor" });
6779
7276
  }
7277
+ if (currentId !== "running-executions") {
7278
+ hints.push({ key: "x", action: "runs" });
7279
+ }
6780
7280
  if (currentId === "home" && depth === 1) {
6781
7281
  hints.push({ key: "b", action: "browse" });
6782
7282
  hints.push({ key: "q", action: "quit" });
@@ -6785,7 +7285,7 @@ function buildHints(currentId, depth) {
6785
7285
  }
6786
7286
 
6787
7287
  // src/integration/ui/tui/views/app.tsx
6788
- import { jsx as jsx66 } from "react/jsx-runtime";
7288
+ import { jsx as jsx70 } from "react/jsx-runtime";
6789
7289
  var MAX_CONTENT_WIDTH = 160;
6790
7290
  async function isFirstRun() {
6791
7291
  try {
@@ -6812,8 +7312,8 @@ function buildInitialStack(initialView, mountOptions) {
6812
7312
  }
6813
7313
  function useTerminalWidth() {
6814
7314
  const { stdout } = useStdout();
6815
- const [width, setWidth] = useState30(stdout.columns);
6816
- useEffect28(() => {
7315
+ const [width, setWidth] = useState38(stdout.columns);
7316
+ useEffect37(() => {
6817
7317
  const onResize = () => {
6818
7318
  setWidth(stdout.columns);
6819
7319
  };
@@ -6827,8 +7327,8 @@ function useTerminalWidth() {
6827
7327
  function App({ initialView, mountOptions }) {
6828
7328
  const terminalWidth = useTerminalWidth();
6829
7329
  const contentWidth = Math.min(terminalWidth, MAX_CONTENT_WIDTH);
6830
- const [stack, setStack] = useState30(null);
6831
- useEffect28(() => {
7330
+ const [stack, setStack] = useState38(null);
7331
+ useEffect37(() => {
6832
7332
  let cancelled = false;
6833
7333
  const decideInitial = async () => {
6834
7334
  if (initialView === "execute" && mountOptions.sprintId !== void 0) {
@@ -6845,13 +7345,13 @@ function App({ initialView, mountOptions }) {
6845
7345
  };
6846
7346
  }, [initialView, mountOptions]);
6847
7347
  if (stack === null) {
6848
- return /* @__PURE__ */ jsx66(Box35, { width: terminalWidth });
7348
+ return /* @__PURE__ */ jsx70(Box37, { width: terminalWidth });
6849
7349
  }
6850
- return /* @__PURE__ */ jsx66(Box35, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsx66(Box35, { flexDirection: "column", width: contentWidth, children: /* @__PURE__ */ jsx66(ViewRouter, { initialStack: stack }) }) });
7350
+ return /* @__PURE__ */ jsx70(Box37, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsx70(Box37, { flexDirection: "column", width: contentWidth, children: /* @__PURE__ */ jsx70(ViewRouter, { initialStack: stack }) }) });
6851
7351
  }
6852
7352
 
6853
7353
  // src/integration/ui/tui/runtime/mount.tsx
6854
- import { jsx as jsx67 } from "react/jsx-runtime";
7354
+ import { jsx as jsx71 } from "react/jsx-runtime";
6855
7355
  function canMountInk() {
6856
7356
  if (process.env["RALPHCTL_NO_TUI"]) return false;
6857
7357
  if (process.env["CI"]) return false;
@@ -6870,7 +7370,7 @@ async function mountInkApp(options) {
6870
7370
  setSharedDeps(createSharedDeps({ logger, signalBus, prompt }));
6871
7371
  enterAltScreen();
6872
7372
  const releaseHost = registerExternalHost();
6873
- const app = render(/* @__PURE__ */ jsx67(App, { initialView: options.initialView, mountOptions: options }), {
7373
+ const app = render(/* @__PURE__ */ jsx71(App, { initialView: options.initialView, mountOptions: options }), {
6874
7374
  exitOnCtrlC: false
6875
7375
  // We own Ctrl+C inside the app for prompt cancellation.
6876
7376
  });