@kenkaiiii/ggcoder 4.3.206 → 4.3.208

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 (80) hide show
  1. package/dist/cli.js +10 -7
  2. package/dist/cli.js.map +1 -1
  3. package/dist/core/goal-controller.js +13 -13
  4. package/dist/core/goal-controller.js.map +1 -1
  5. package/dist/core/goal-controller.test.js +54 -12
  6. package/dist/core/goal-controller.test.js.map +1 -1
  7. package/dist/core/goal-worker-dev-server-lifecycle.test.d.ts +2 -0
  8. package/dist/core/goal-worker-dev-server-lifecycle.test.d.ts.map +1 -0
  9. package/dist/core/goal-worker-dev-server-lifecycle.test.js +68 -0
  10. package/dist/core/goal-worker-dev-server-lifecycle.test.js.map +1 -0
  11. package/dist/core/goal-worker.d.ts +7 -3
  12. package/dist/core/goal-worker.d.ts.map +1 -1
  13. package/dist/core/goal-worker.js +15 -5
  14. package/dist/core/goal-worker.js.map +1 -1
  15. package/dist/core/goal-worker.test.js +19 -1
  16. package/dist/core/goal-worker.test.js.map +1 -1
  17. package/dist/core/model-registry.test.js +51 -1
  18. package/dist/core/model-registry.test.js.map +1 -1
  19. package/dist/core/process-manager-dev-server-repro.test.d.ts +2 -0
  20. package/dist/core/process-manager-dev-server-repro.test.d.ts.map +1 -0
  21. package/dist/core/process-manager-dev-server-repro.test.js +100 -0
  22. package/dist/core/process-manager-dev-server-repro.test.js.map +1 -0
  23. package/dist/core/process-manager.js +2 -2
  24. package/dist/core/process-manager.js.map +1 -1
  25. package/dist/core/prompt-commands.d.ts.map +1 -1
  26. package/dist/core/prompt-commands.js +2 -1
  27. package/dist/core/prompt-commands.js.map +1 -1
  28. package/dist/core/prompt-commands.test.js +2 -0
  29. package/dist/core/prompt-commands.test.js.map +1 -1
  30. package/dist/core/repomap.js +8 -1
  31. package/dist/core/repomap.js.map +1 -1
  32. package/dist/core/repomap.test.js +32 -0
  33. package/dist/core/repomap.test.js.map +1 -1
  34. package/dist/system-prompt.d.ts.map +1 -1
  35. package/dist/system-prompt.js +1 -0
  36. package/dist/system-prompt.js.map +1 -1
  37. package/dist/tools/edit.d.ts.map +1 -1
  38. package/dist/tools/edit.js +22 -12
  39. package/dist/tools/edit.js.map +1 -1
  40. package/dist/tools/edit.test.js +29 -6
  41. package/dist/tools/edit.test.js.map +1 -1
  42. package/dist/ui/App.d.ts +39 -4
  43. package/dist/ui/App.d.ts.map +1 -1
  44. package/dist/ui/App.js +216 -147
  45. package/dist/ui/App.js.map +1 -1
  46. package/dist/ui/app-state-persistence.test.js +80 -6
  47. package/dist/ui/app-state-persistence.test.js.map +1 -1
  48. package/dist/ui/components/GoalOverlay.d.ts +54 -1
  49. package/dist/ui/components/GoalOverlay.d.ts.map +1 -1
  50. package/dist/ui/components/GoalOverlay.js +392 -53
  51. package/dist/ui/components/GoalOverlay.js.map +1 -1
  52. package/dist/ui/components/InputArea.d.ts.map +1 -1
  53. package/dist/ui/components/InputArea.js +38 -1
  54. package/dist/ui/components/InputArea.js.map +1 -1
  55. package/dist/ui/components/InputArea.test.d.ts +2 -0
  56. package/dist/ui/components/InputArea.test.d.ts.map +1 -0
  57. package/dist/ui/components/InputArea.test.js +79 -0
  58. package/dist/ui/components/InputArea.test.js.map +1 -0
  59. package/dist/ui/components/ToolExecution.d.ts.map +1 -1
  60. package/dist/ui/components/ToolExecution.js +2 -2
  61. package/dist/ui/components/ToolExecution.js.map +1 -1
  62. package/dist/ui/goal-events.d.ts +88 -1
  63. package/dist/ui/goal-events.d.ts.map +1 -1
  64. package/dist/ui/goal-events.js +249 -28
  65. package/dist/ui/goal-events.js.map +1 -1
  66. package/dist/ui/goal-events.test.js +89 -4
  67. package/dist/ui/goal-events.test.js.map +1 -1
  68. package/dist/ui/goal-overlay.test.js +155 -1
  69. package/dist/ui/goal-overlay.test.js.map +1 -1
  70. package/dist/ui/render.d.ts +3 -1
  71. package/dist/ui/render.d.ts.map +1 -1
  72. package/dist/ui/render.js +2 -0
  73. package/dist/ui/render.js.map +1 -1
  74. package/dist/ui/scroll-stabilization.test.js +49 -2
  75. package/dist/ui/scroll-stabilization.test.js.map +1 -1
  76. package/dist/ui/slash-command-images.test.d.ts +2 -0
  77. package/dist/ui/slash-command-images.test.d.ts.map +1 -0
  78. package/dist/ui/slash-command-images.test.js +47 -0
  79. package/dist/ui/slash-command-images.test.js.map +1 -0
  80. package/package.json +5 -5
@@ -1,9 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useRef, useState } from "react";
3
3
  import { Box, Text, useInput } from "ink";
4
- import { randomUUID } from "node:crypto";
5
4
  import { basename } from "node:path";
6
- import { createGoalRun, formatGoalPrerequisiteInstruction, goalHasBlockingPrerequisites, isBlockingGoalPrerequisite, loadGoalRuns, saveGoalRuns, summarizeGoalCountsFromRuns, } from "../../core/goal-store.js";
5
+ import { formatGoalPrerequisiteInstruction, goalHasBlockingPrerequisites, isBlockingGoalPrerequisite, loadGoalRuns, saveGoalRuns, summarizeGoalCountsFromRuns, } from "../../core/goal-store.js";
7
6
  import { useTerminalSize } from "../hooks/useTerminalSize.js";
8
7
  import { useTheme } from "../theme/theme.js";
9
8
  const GOAL_LOGO = [" ▄▀▀▀ ▄▀▀▀", " █ ▀█ █ ▀█", " ▀▄▄▀ ▀▄▄▀"];
@@ -63,10 +62,203 @@ export function formatGoalVerifierSummary(run) {
63
62
  return "verifier described";
64
63
  return "no verifier";
65
64
  }
65
+ export function getGoalReadinessText(run) {
66
+ if (goalHasBlockingPrerequisites(run))
67
+ return "needs user input";
68
+ if (run.status === "running" || run.status === "verifying")
69
+ return "work in progress";
70
+ if (run.status === "passed")
71
+ return "verified";
72
+ if (run.verifier?.command)
73
+ return "ready to verify";
74
+ if (run.tasks.length > 0)
75
+ return "ready to run";
76
+ return "drafting plan";
77
+ }
78
+ export function formatGoalProgressText(run) {
79
+ const prereqTotal = run.prerequisites.length;
80
+ const prereqMet = run.prerequisites.filter((item) => item.status === "met").length;
81
+ const taskTotal = run.tasks.length;
82
+ const taskDone = run.tasks.filter((item) => item.status === "done").length;
83
+ const prereq = prereqTotal > 0 ? `prereqs ${prereqMet}/${prereqTotal}` : "no prereqs";
84
+ const tasks = taskTotal > 0 ? `tasks ${taskDone}/${taskTotal}` : "no tasks";
85
+ return `${prereq} · ${tasks}`;
86
+ }
66
87
  export function getGoalStatusCountsText(runs) {
67
88
  const counts = summarizeGoalCountsFromRuns(runs);
68
89
  return `${counts.passed} passed · ${counts.running} running · ${counts.pending} pending · ${counts.blocked} blocked`;
69
90
  }
91
+ export function clampGoalScrollOffset(offset, itemCount, viewportRows) {
92
+ const visibleRows = Math.max(1, Math.floor(viewportRows));
93
+ const maxOffset = Math.max(0, itemCount - visibleRows);
94
+ if (!Number.isFinite(offset))
95
+ return 0;
96
+ return Math.min(Math.max(0, Math.floor(offset)), maxOffset);
97
+ }
98
+ export function getGoalOverlayViewportRows(terminalRows, reservedRows = 8) {
99
+ if (!Number.isFinite(terminalRows))
100
+ return 8;
101
+ return Math.max(4, Math.floor(terminalRows) - reservedRows);
102
+ }
103
+ export function getGoalScrollOffsetForSelection({ selectedIndex, currentOffset, itemCount, viewportRows, }) {
104
+ const selected = clampGoalSelectedIndex(selectedIndex, itemCount);
105
+ const offset = clampGoalScrollOffset(currentOffset, itemCount, viewportRows);
106
+ const rows = Math.max(1, Math.floor(viewportRows));
107
+ if (selected < offset)
108
+ return selected;
109
+ if (selected >= offset + rows)
110
+ return clampGoalScrollOffset(selected - rows + 1, itemCount, rows);
111
+ return offset;
112
+ }
113
+ export function getGoalDetailRowCount(run) {
114
+ let count = 2;
115
+ count += 1 + Math.max(1, run.successCriteria.length);
116
+ if (run.prerequisites.length > 0) {
117
+ count += 1;
118
+ for (const prerequisite of run.prerequisites) {
119
+ count += 1;
120
+ if (isBlockingGoalPrerequisite(prerequisite) || prerequisite.evidence)
121
+ count += 1;
122
+ }
123
+ }
124
+ count += 1;
125
+ if (run.tasks.length === 0) {
126
+ count += 1;
127
+ }
128
+ else {
129
+ for (const task of run.tasks) {
130
+ count += 1;
131
+ if (task.lastSummary)
132
+ count += 1;
133
+ }
134
+ }
135
+ if (run.harness.length > 0)
136
+ count += 1 + run.harness.length;
137
+ if (run.evidencePlan.length > 0)
138
+ count += 1 + run.evidencePlan.length;
139
+ if (run.verifier)
140
+ count += 2;
141
+ if (run.blockers.length > 0)
142
+ count += 1 + run.blockers.length;
143
+ if (run.evidence.length > 0)
144
+ count += 1 + Math.min(5, run.evidence.length);
145
+ return count;
146
+ }
147
+ export function getGoalCardExtraRowCount(run) {
148
+ let count = 0;
149
+ if (goalHasBlockingPrerequisites(run))
150
+ count += 1;
151
+ else if (run.status === "running" || run.status === "verifying")
152
+ count += 1;
153
+ if (run.blockers.length > 0)
154
+ count += 1;
155
+ return count;
156
+ }
157
+ export function getGoalListCardRowCount({ run }) {
158
+ const compactCardRows = 1 + // title/status row
159
+ 2 + // compact summary rows
160
+ getGoalCardExtraRowCount(run);
161
+ const marginRows = 1;
162
+ return compactCardRows + marginRows;
163
+ }
164
+ function compareGoalListWindows({ candidate, current, selectedIndex, }) {
165
+ if (!current)
166
+ return candidate;
167
+ const candidateCount = candidate.end - candidate.start;
168
+ const currentCount = current.end - current.start;
169
+ if (candidateCount !== currentCount)
170
+ return candidateCount > currentCount ? candidate : current;
171
+ if (candidate.rowsUsed !== current.rowsUsed)
172
+ return candidate.rowsUsed > current.rowsUsed ? candidate : current;
173
+ const candidateBalance = Math.abs(selectedIndex - candidate.start - (candidate.end - selectedIndex - 1));
174
+ const currentBalance = Math.abs(selectedIndex - current.start - (current.end - selectedIndex - 1));
175
+ if (candidateBalance !== currentBalance)
176
+ return candidateBalance < currentBalance ? candidate : current;
177
+ return candidate.start > current.start ? candidate : current;
178
+ }
179
+ export function getGoalListWindow({ runs, selectedIndex, viewportRows, }) {
180
+ const rows = Number.isFinite(viewportRows) ? Math.max(1, Math.floor(viewportRows)) : 8;
181
+ const fixedRows = 1;
182
+ if (runs.length === 0) {
183
+ return { start: 0, end: 0, hiddenBefore: 0, hiddenAfter: 0, rowsUsed: fixedRows };
184
+ }
185
+ const selected = clampGoalSelectedIndex(selectedIndex, runs.length);
186
+ let best = null;
187
+ for (let start = 0; start <= selected; start++) {
188
+ let cardRows = 0;
189
+ for (let end = start + 1; end <= runs.length; end++) {
190
+ const index = end - 1;
191
+ const run = runs[index];
192
+ if (!run)
193
+ continue;
194
+ cardRows += getGoalListCardRowCount({ run });
195
+ if (end <= selected)
196
+ continue;
197
+ const hiddenBefore = start;
198
+ const hiddenAfter = runs.length - end;
199
+ const indicatorRows = (hiddenBefore > 0 ? 1 : 0) + (hiddenAfter > 0 ? 1 : 0);
200
+ const rowsUsed = fixedRows + indicatorRows + cardRows;
201
+ if (rowsUsed > rows)
202
+ continue;
203
+ best = compareGoalListWindows({
204
+ candidate: { start, end, hiddenBefore, hiddenAfter, rowsUsed },
205
+ current: best,
206
+ selectedIndex: selected,
207
+ });
208
+ }
209
+ }
210
+ if (best)
211
+ return best;
212
+ const start = selected;
213
+ const end = selected + 1;
214
+ const hiddenBefore = start;
215
+ const hiddenAfter = runs.length - end;
216
+ const indicatorRows = (hiddenBefore > 0 ? 1 : 0) + (hiddenAfter > 0 ? 1 : 0);
217
+ const run = runs[selected];
218
+ const cardRows = run ? getGoalListCardRowCount({ run }) : 0;
219
+ return {
220
+ start,
221
+ end,
222
+ hiddenBefore,
223
+ hiddenAfter,
224
+ rowsUsed: fixedRows + indicatorRows + cardRows,
225
+ };
226
+ }
227
+ export function getGoalExpandedDetailViewportRows({ viewportRows, cardExtraRows, }) {
228
+ const rows = Number.isFinite(viewportRows) ? Math.max(1, Math.floor(viewportRows)) : 8;
229
+ const selectedCardRows = 1 + Math.max(0, Math.floor(cardExtraRows));
230
+ const fixedRows = 1 + // Goals heading
231
+ selectedCardRows +
232
+ 2 + // selected card border
233
+ 1 + // detail top margin
234
+ 1; // selected card bottom margin
235
+ return Math.max(1, rows - fixedRows);
236
+ }
237
+ export function clampGoalDetailScrollOffset(offset, detailRowCount, viewportRows) {
238
+ const visibleRows = Math.max(1, Math.floor(viewportRows));
239
+ const scrolledBodyRows = Math.max(1, visibleRows - 1);
240
+ const maxOffset = Math.max(0, detailRowCount - scrolledBodyRows);
241
+ if (!Number.isFinite(offset))
242
+ return 0;
243
+ return Math.min(Math.max(0, Math.floor(offset)), maxOffset);
244
+ }
245
+ export function getGoalDetailScrollWindow({ detailRowCount, scrollOffset, viewportRows, }) {
246
+ const rows = Math.max(1, Math.floor(viewportRows));
247
+ const start = clampGoalDetailScrollOffset(scrollOffset, detailRowCount, rows);
248
+ const topIndicatorRows = start > 0 && rows > 2 ? 1 : 0;
249
+ let bodyRows = Math.max(1, rows - topIndicatorRows);
250
+ let hiddenAfter = Math.max(0, detailRowCount - start - bodyRows);
251
+ if (hiddenAfter > 0 && bodyRows > 1) {
252
+ bodyRows -= 1;
253
+ hiddenAfter = Math.max(0, detailRowCount - start - bodyRows);
254
+ }
255
+ return {
256
+ start,
257
+ end: Math.min(detailRowCount, start + bodyRows),
258
+ hiddenBefore: start,
259
+ hiddenAfter,
260
+ };
261
+ }
70
262
  export function sortGoalRunsForOverlay(runs) {
71
263
  return [...runs].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
72
264
  }
@@ -116,6 +308,21 @@ function statusColor(status) {
116
308
  return "";
117
309
  }
118
310
  }
311
+ export function getGoalCardStatusColor({ status, selected, primaryColor, textColor, }) {
312
+ return statusColor(status) || (selected ? primaryColor : textColor);
313
+ }
314
+ export function getGoalCardTitleColor({ selected, primaryColor, textColor, }) {
315
+ return selected ? primaryColor : textColor;
316
+ }
317
+ function verifierSummaryColor(run, fallbackColor) {
318
+ if (run.verifier?.lastResult)
319
+ return verifierStatusColor(run.verifier.lastResult.status);
320
+ if (run.verifier?.command)
321
+ return "cyan";
322
+ if (run.verifier?.description)
323
+ return "magenta";
324
+ return fallbackColor;
325
+ }
119
326
  function taskStatusColor(status) {
120
327
  switch (status) {
121
328
  case "done":
@@ -141,6 +348,40 @@ function prerequisiteStatusColor(status) {
141
348
  return "cyan";
142
349
  }
143
350
  }
351
+ function evidencePlanStatusColor(status) {
352
+ switch (status) {
353
+ case "ready":
354
+ return "green";
355
+ case "blocked":
356
+ return "yellow";
357
+ case "planned":
358
+ return "cyan";
359
+ }
360
+ }
361
+ function verifierStatusColor(status) {
362
+ switch (status) {
363
+ case "pass":
364
+ return "green";
365
+ case "fail":
366
+ return "red";
367
+ case "unknown":
368
+ return "yellow";
369
+ }
370
+ }
371
+ function evidenceKindColor(kind) {
372
+ switch (kind) {
373
+ case "command":
374
+ return "cyan";
375
+ case "file":
376
+ return "blue";
377
+ case "log":
378
+ return "yellow";
379
+ case "screenshot":
380
+ return "magenta";
381
+ case "summary":
382
+ return "green";
383
+ }
384
+ }
144
385
  export function getGoalDetailTaskHeading(run) {
145
386
  return run.prerequisites.length > 0 ? "2. Worker tasks" : "Worker tasks";
146
387
  }
@@ -156,6 +397,10 @@ export function formatGoalTaskDetailSummary(summary) {
156
397
  return "";
157
398
  return firstLine.length > 180 ? `${firstLine.slice(0, 177)}…` : firstLine;
158
399
  }
400
+ function truncateGoalDetailText(text, maxLength = 220) {
401
+ const collapsed = text.replace(/\s+/g, " ").trim();
402
+ return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 1)}…` : collapsed;
403
+ }
159
404
  function GoalHeader({ cwd, runs, agentRunning, }) {
160
405
  const theme = useTheme();
161
406
  const { columns } = useTerminalSize();
@@ -166,11 +411,83 @@ function GoalHeader({ cwd, runs, agentRunning, }) {
166
411
  }
167
412
  return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, width: columns, children: [_jsxs(Box, { children: [_jsx(GoalGradientText, { text: GOAL_LOGO[0] }), _jsx(Text, { children: GAP }), _jsx(Text, { color: GOAL_SUCCESS, bold: true, children: "Goal Pane" }), agentRunning && _jsx(Text, { color: GOAL_ACTIVE, children: " (agent running)" })] }), _jsxs(Box, { children: [_jsx(GoalGradientText, { text: GOAL_LOGO[1] }), _jsx(Text, { children: GAP }), _jsx(Text, { color: theme.textDim, wrap: "truncate", children: displayPath })] }), _jsxs(Box, { children: [_jsx(GoalGradientText, { text: GOAL_LOGO[2] }), _jsx(Text, { children: GAP }), _jsxs(Text, { children: [_jsxs(Text, { color: GOAL_SUCCESS, children: [counts.passed, " passed"] }), _jsx(Text, { color: theme.textDim, children: " \u00B7 " }), _jsxs(Text, { color: GOAL_ACTIVE, children: [counts.running, " active"] }), _jsx(Text, { color: theme.textDim, children: " \u00B7 " }), _jsxs(Text, { color: theme.text, children: [counts.pending, " pending"] }), _jsx(Text, { color: theme.textDim, children: " \u00B7 " }), _jsxs(Text, { color: GOAL_ACTIVE, children: [counts.blocked, " blocked"] })] })] })] }));
168
413
  }
169
- function GoalDetail({ run }) {
414
+ function StatusChip({ label, color }) {
415
+ return (_jsxs(Text, { color: color, bold: true, children: ["\u25D6 ", label, " \u25D7"] }));
416
+ }
417
+ function GoalDetail({ run, maxRows, scrollOffset, }) {
170
418
  const theme = useTheme();
171
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [run.prerequisites.length > 0 ? (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: theme.textDim, bold: true, children: getGoalUserPrerequisiteHeading(run) }), run.prerequisites.map((prerequisite) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsxs(Text, { color: prerequisiteStatusColor(prerequisite.status), children: ["[", prerequisite.status, "]"] }), _jsxs(Text, { color: theme.text, children: [" ", prerequisite.label] }), isBlockingGoalPrerequisite(prerequisite) ? (_jsx(Text, { color: theme.warning, children: " \u00B7 required from user" })) : null] }), isBlockingGoalPrerequisite(prerequisite) ? (_jsxs(Text, { color: theme.textDim, wrap: "wrap", children: [" ", formatGoalPrerequisiteInstruction(prerequisite)] })) : prerequisite.evidence ? (_jsxs(Text, { color: theme.textDim, wrap: "wrap", children: [" ", prerequisite.evidence] })) : null] }, prerequisite.id)))] })) : null, _jsx(Text, { color: theme.textDim, bold: true, children: getGoalDetailTaskHeading(run) }), run.tasks.length === 0 ? (_jsx(Text, { color: theme.textDim, children: goalHasBlockingPrerequisites(run)
172
- ? "Waiting for user prerequisites before worker tasks can begin."
173
- : "No worker tasks yet." })) : (run.tasks.map((task) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsxs(Text, { color: taskStatusColor(task.status), children: ["[", task.status, "]"] }), _jsxs(Text, { color: theme.text, children: [" ", task.title] }), _jsxs(Text, { color: theme.textDim, children: [" \u00B7 attempts ", task.attempts] }), task.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", task.workerId] }) : null] }), task.lastSummary ? (_jsxs(Text, { color: theme.textDim, children: [" ", formatGoalTaskDetailSummary(task.lastSummary)] })) : null] }, task.id))))] }));
419
+ const rows = [
420
+ _jsxs(Text, { children: [_jsx(Text, { color: theme.primary, bold: true, children: "Goal" }), _jsx(Text, { color: theme.textDim, children: " \u00B7 " }), _jsx(Text, { color: statusColor(run.status) || theme.secondary, children: getGoalReadinessText(run) }), _jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", formatGoalProgressText(run)] })] }, "goal-heading"),
421
+ _jsx(Text, { color: theme.text, children: truncateGoalDetailText(run.goal || run.title) }, "goal-text"),
422
+ _jsx(Text, { color: theme.primary, bold: true, children: "Success criteria" }, "success-heading"),
423
+ ];
424
+ if (run.successCriteria.length === 0) {
425
+ rows.push(_jsx(Text, { color: theme.textDim, children: "- none recorded" }, "success-none"));
426
+ }
427
+ else {
428
+ for (const [index, criterion] of run.successCriteria.entries()) {
429
+ rows.push(_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "\u2713 " }), _jsx(Text, { color: theme.text, children: truncateGoalDetailText(criterion) })] }, `success-${index}`));
430
+ }
431
+ }
432
+ if (run.prerequisites.length > 0) {
433
+ rows.push(_jsx(Text, { color: theme.primary, bold: true, children: getGoalUserPrerequisiteHeading(run) }, "prereq-heading"));
434
+ for (const prerequisite of run.prerequisites) {
435
+ rows.push(_jsxs(Text, { children: [_jsxs(Text, { color: prerequisiteStatusColor(prerequisite.status), children: ["\u25CF ", prerequisite.status] }), _jsxs(Text, { color: theme.text, bold: isBlockingGoalPrerequisite(prerequisite), children: [" ", prerequisite.label] }), isBlockingGoalPrerequisite(prerequisite) ? (_jsx(Text, { color: theme.warning, children: " \u00B7 user action required" })) : null] }, `prereq-${prerequisite.id}`));
436
+ if (isBlockingGoalPrerequisite(prerequisite)) {
437
+ rows.push(_jsxs(Text, { color: theme.textDim, wrap: "truncate", children: ["\u2514\u2500 ", formatGoalPrerequisiteInstruction(prerequisite)] }, `prereq-${prerequisite.id}-instruction`));
438
+ }
439
+ else if (prerequisite.evidence) {
440
+ rows.push(_jsxs(Text, { color: theme.textDim, wrap: "truncate", children: ["\u2514\u2500 ", prerequisite.evidence] }, `prereq-${prerequisite.id}-evidence`));
441
+ }
442
+ }
443
+ }
444
+ rows.push(_jsx(Text, { color: theme.primary, bold: true, children: getGoalDetailTaskHeading(run) }, "task-heading"));
445
+ if (run.tasks.length === 0) {
446
+ rows.push(_jsx(Text, { color: theme.textDim, children: goalHasBlockingPrerequisites(run)
447
+ ? "⏸ Waiting for prerequisites before workers can start."
448
+ : "✨ No worker tasks yet — run the goal to generate focused work." }, "no-tasks"));
449
+ }
450
+ else {
451
+ for (const task of run.tasks) {
452
+ rows.push(_jsxs(Text, { children: [_jsxs(Text, { color: taskStatusColor(task.status), children: ["\u25CF ", task.status] }), _jsxs(Text, { color: theme.text, children: [" ", task.title] }), _jsxs(Text, { color: theme.textDim, children: [" \u00B7 try ", task.attempts] }), task.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", task.workerId] }) : null] }, `task-${task.id}`));
453
+ if (task.lastSummary) {
454
+ rows.push(_jsxs(Text, { color: theme.textDim, wrap: "truncate", children: ["\u2514\u2500 ", formatGoalTaskDetailSummary(task.lastSummary)] }, `task-${task.id}-summary`));
455
+ }
456
+ }
457
+ }
458
+ if (run.harness.length > 0) {
459
+ rows.push(_jsx(Text, { color: theme.primary, bold: true, children: "Harness" }, "harness-heading"));
460
+ for (const item of run.harness) {
461
+ rows.push(_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "\u25E6 " }), _jsx(Text, { color: theme.text, children: item.label }), item.command ? _jsxs(Text, { color: theme.secondary, children: [" \u00B7 ", item.command] }) : null, !item.command && item.path ? _jsxs(Text, { color: theme.secondary, children: [" \u00B7 ", item.path] }) : null] }, `harness-${item.id}`));
462
+ }
463
+ }
464
+ if (run.evidencePlan.length > 0) {
465
+ rows.push(_jsx(Text, { color: theme.primary, bold: true, children: "Evidence plan" }, "evidence-plan-heading"));
466
+ for (const item of run.evidencePlan) {
467
+ rows.push(_jsxs(Text, { children: [_jsxs(Text, { color: evidencePlanStatusColor(item.status), children: ["\u25CF ", item.status] }), _jsxs(Text, { color: theme.text, children: [" \u00B7 ", item.label] }), item.command ? _jsxs(Text, { color: theme.secondary, children: [" \u00B7 ", item.command] }) : null, !item.command && item.path ? _jsxs(Text, { color: theme.secondary, children: [" \u00B7 ", item.path] }) : null] }, `evidence-plan-${item.id}`));
468
+ }
469
+ }
470
+ if (run.verifier) {
471
+ rows.push(_jsx(Text, { color: theme.primary, bold: true, children: "Verifier" }, "verifier-heading"), _jsxs(Text, { wrap: "truncate", children: [run.verifier.lastResult ? (_jsxs(Text, { color: verifierStatusColor(run.verifier.lastResult.status), children: ["\u25CF ", formatGoalVerifierSummary(run)] })) : (_jsxs(Text, { color: run.verifier.command ? "cyan" : theme.textDim, children: ["\u25CF ", formatGoalVerifierSummary(run)] })), run.verifier.command ? (_jsxs(Text, { color: theme.secondary, children: [" \u00B7 ", run.verifier.command] })) : null] }, "verifier-summary"));
472
+ }
473
+ if (run.blockers.length > 0) {
474
+ rows.push(_jsx(Text, { color: theme.warning, bold: true, children: "Blockers" }, "blockers-heading"));
475
+ for (const [index, blocker] of run.blockers.entries()) {
476
+ rows.push(_jsxs(Text, { color: theme.warning, children: ["- ", truncateGoalDetailText(blocker)] }, `blocker-${index}`));
477
+ }
478
+ }
479
+ if (run.evidence.length > 0) {
480
+ rows.push(_jsx(Text, { color: theme.primary, bold: true, children: "Recent evidence" }, "evidence-heading"));
481
+ for (const item of run.evidence.slice(-5)) {
482
+ rows.push(_jsxs(Text, { children: [_jsxs(Text, { color: evidenceKindColor(item.kind), children: ["[", item.kind, "]"] }), _jsxs(Text, { color: theme.text, children: [" ", item.label] }), item.path ? _jsxs(Text, { color: theme.secondary, children: [" \u00B7 ", item.path] }) : null] }, `evidence-${item.id}`));
483
+ }
484
+ }
485
+ const window = getGoalDetailScrollWindow({
486
+ detailRowCount: rows.length,
487
+ scrollOffset,
488
+ viewportRows: maxRows,
489
+ });
490
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, height: maxRows, overflowY: "hidden", children: [window.hiddenBefore > 0 ? (_jsxs(Text, { color: theme.secondary, children: ["\u2191 ", window.hiddenBefore, " detail row(s) above \u00B7 PgUp"] })) : null, rows.slice(window.start, window.end), window.hiddenAfter > 0 ? (_jsxs(Text, { color: theme.secondary, children: ["\u2193 ", window.hiddenAfter, " more detail row(s) \u00B7 PgDn"] })) : null] }));
174
491
  }
175
492
  export function GoalOverlay({ cwd, onClose, onRunGoal, onVerifyGoal, onPauseGoal, agentRunning, }) {
176
493
  const theme = useTheme();
@@ -179,8 +496,8 @@ export function GoalOverlay({ cwd, onClose, onRunGoal, onVerifyGoal, onPauseGoal
179
496
  const [loaded, setLoaded] = useState(false);
180
497
  const [expandedRunId, setExpandedRunId] = useState(null);
181
498
  const [mode, setMode] = useState("normal");
182
- const [inputText, setInputText] = useState("");
183
499
  const [status, setStatus] = useState("");
500
+ const [detailScrollOffset, setDetailScrollOffset] = useState(0);
184
501
  const statusTimer = useRef(null);
185
502
  const saveTimer = useRef(null);
186
503
  const lastPersistedRunsRef = useRef([]);
@@ -235,46 +552,39 @@ export function GoalOverlay({ cwd, onClose, onRunGoal, onVerifyGoal, onPauseGoal
235
552
  void saveGoalRuns(cwd, runs);
236
553
  }, 100);
237
554
  }, [cwd, loaded, runs]);
555
+ const { rows } = useTerminalSize();
556
+ const viewportRows = getGoalOverlayViewportRows(rows);
238
557
  const selectedRun = runs[selectedIndex];
239
558
  const expandedRun = selectedRun && selectedRun.id === expandedRunId ? selectedRun : null;
559
+ const selectedCardExtraRows = selectedRun ? getGoalCardExtraRowCount(selectedRun) : 0;
560
+ const expandedCardExtraRows = expandedRun ? selectedCardExtraRows : 0;
561
+ const detailViewportRows = expandedRun
562
+ ? getGoalExpandedDetailViewportRows({
563
+ viewportRows,
564
+ cardExtraRows: expandedCardExtraRows,
565
+ })
566
+ : 0;
567
+ const listWindow = expandedRun
568
+ ? null
569
+ : getGoalListWindow({
570
+ runs,
571
+ selectedIndex,
572
+ viewportRows,
573
+ });
574
+ const scrollOffset = expandedRun ? selectedIndex : (listWindow?.start ?? 0);
575
+ const visibleRuns = expandedRun
576
+ ? [expandedRun]
577
+ : runs.slice(listWindow?.start ?? 0, listWindow?.end ?? 0);
578
+ const hiddenBefore = expandedRun ? 0 : (listWindow?.hiddenBefore ?? 0);
579
+ const hiddenAfter = expandedRun ? 0 : (listWindow?.hiddenAfter ?? 0);
580
+ const detailRowCount = expandedRun ? getGoalDetailRowCount(expandedRun) : 0;
581
+ useEffect(() => {
582
+ setDetailScrollOffset(0);
583
+ }, [expandedRunId]);
584
+ useEffect(() => {
585
+ setDetailScrollOffset((offset) => clampGoalDetailScrollOffset(offset, detailRowCount, detailViewportRows));
586
+ }, [detailRowCount, detailViewportRows]);
240
587
  useInput((input, key) => {
241
- if (mode === "adding") {
242
- if (key.escape) {
243
- setMode("normal");
244
- setInputText("");
245
- return;
246
- }
247
- if (key.return) {
248
- const text = inputText.trim();
249
- if (text) {
250
- const run = createGoalRun(cwd, {
251
- id: randomUUID(),
252
- title: text.slice(0, 80),
253
- goal: text,
254
- status: "draft",
255
- successCriteria: [],
256
- prerequisites: [],
257
- harness: [],
258
- tasks: [],
259
- evidence: [],
260
- blockers: [],
261
- });
262
- setRuns((previousRuns) => sortGoalRunsForOverlay([run, ...previousRuns]));
263
- setSelectedIndex(0);
264
- showStatus("Draft goal added");
265
- }
266
- setMode("normal");
267
- setInputText("");
268
- return;
269
- }
270
- if (key.backspace || key.delete) {
271
- setInputText((previous) => previous.slice(0, -1));
272
- return;
273
- }
274
- if (input && !key.ctrl && !key.meta)
275
- setInputText((previous) => previous + input);
276
- return;
277
- }
278
588
  if (mode === "confirmDelete") {
279
589
  if (key.escape || input === "n") {
280
590
  setMode("normal");
@@ -293,6 +603,30 @@ export function GoalOverlay({ cwd, onClose, onRunGoal, onVerifyGoal, onPauseGoal
293
603
  onClose();
294
604
  return;
295
605
  }
606
+ if (expandedRun && (key.pageUp || input === "[")) {
607
+ setDetailScrollOffset((offset) => clampGoalDetailScrollOffset(offset - Math.max(1, detailViewportRows - 1), detailRowCount, detailViewportRows));
608
+ return;
609
+ }
610
+ if (expandedRun && (key.pageDown || input === "]")) {
611
+ setDetailScrollOffset((offset) => clampGoalDetailScrollOffset(offset + Math.max(1, detailViewportRows - 1), detailRowCount, detailViewportRows));
612
+ return;
613
+ }
614
+ if (expandedRun && key.home) {
615
+ setDetailScrollOffset(0);
616
+ return;
617
+ }
618
+ if (expandedRun && key.end) {
619
+ setDetailScrollOffset(clampGoalDetailScrollOffset(detailRowCount, detailRowCount, detailViewportRows));
620
+ return;
621
+ }
622
+ if (expandedRun && (key.upArrow || input === "k")) {
623
+ setDetailScrollOffset((offset) => clampGoalDetailScrollOffset(offset - 1, detailRowCount, detailViewportRows));
624
+ return;
625
+ }
626
+ if (expandedRun && (key.downArrow || input === "j")) {
627
+ setDetailScrollOffset((offset) => clampGoalDetailScrollOffset(offset + 1, detailRowCount, detailViewportRows));
628
+ return;
629
+ }
296
630
  if (key.upArrow || input === "k") {
297
631
  setSelectedIndex((index) => clampGoalSelectedIndex(index - 1, runs.length));
298
632
  return;
@@ -317,20 +651,25 @@ export function GoalOverlay({ cwd, onClose, onRunGoal, onVerifyGoal, onPauseGoal
317
651
  onPauseGoal(selectedRun);
318
652
  return;
319
653
  }
320
- if (input === "a") {
321
- setMode("adding");
322
- setInputText("");
323
- return;
324
- }
325
654
  if (input === "x" && selectedRun) {
326
655
  setMode("confirmDelete");
327
656
  showStatus("Archive goal? y/n");
328
657
  }
329
658
  });
330
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(GoalHeader, { cwd: cwd, runs: runs, agentRunning: agentRunning }), agentRunning ? (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: theme.textDim, children: "Agent is running; Goal pane stays available without resetting chat." }) })) : null, !loaded ? (_jsx(Text, { color: theme.textDim, children: "Loading goals\u2026" })) : runs.length === 0 ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: theme.textDim, children: "No goals. Run /goal <objective> or press (a)dd." }), mode === "adding" ? _jsxs(Text, { color: theme.primary, children: ["New goal: ", inputText] }) : null] })) : (_jsxs(Box, { flexDirection: "column", children: [runs.map((run, index) => {
659
+ return (_jsxs(Box, { flexDirection: "column", height: rows, overflow: "hidden", children: [_jsx(GoalHeader, { cwd: cwd, runs: runs, agentRunning: agentRunning }), agentRunning ? (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: theme.textDim, children: "Agent is running; Goal pane stays available without resetting chat." }) })) : null, !loaded ? (_jsx(Box, { borderStyle: "round", borderColor: theme.textDim, paddingX: 1, children: _jsx(Text, { color: theme.textDim, children: "Loading goals\u2026" }) })) : runs.length === 0 ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: theme.primary, paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: theme.primary, bold: true, children: "Start a durable Goal run" }), _jsx(Text, { color: theme.textDim, children: "No goals yet. Ask the agent to start a durable Goal." }), _jsx(Text, { color: theme.textDim, children: "Prerequisites, worker tasks, evidence, and verifier results will appear in this pane." })] })) : (_jsxs(Box, { flexDirection: "column", height: viewportRows, overflowY: "hidden", children: [_jsx(Text, { color: theme.textDim, bold: true, children: "Goals" }), hiddenBefore > 0 ? (_jsxs(Text, { color: theme.textDim, children: ["\u2191 ", hiddenBefore, " earlier goal", hiddenBefore === 1 ? "" : "s"] })) : null, visibleRuns.map((run, visibleIndex) => {
660
+ const index = scrollOffset + visibleIndex;
331
661
  const selected = index === selectedIndex;
332
662
  const blocked = goalHasBlockingPrerequisites(run);
333
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: selected ? theme.primary : theme.textDim, children: selected ? "" : " " }), _jsxs(Text, { color: statusColor(run.status) || (selected ? theme.primary : theme.text), children: ["[", run.status, "]"] }), _jsxs(Text, { color: selected ? theme.primary : theme.text, bold: selected, children: [" ", run.title] }), _jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", run.id.slice(0, 8)] }), blocked ? _jsx(Text, { color: theme.warning, children: " \u00B7 blocked" }) : null] }), _jsxs(Text, { color: theme.textDim, children: [" ", formatGoalPrerequisiteSummary(run), " \u00B7 ", formatGoalTaskSummary(run), " \u00B7", " ", formatGoalVerifierSummary(run)] }), run.blockers.length > 0 ? (_jsxs(Text, { color: theme.warning, children: [" ", "blocker: ", run.blockers[0]] })) : null, expandedRun?.id === run.id ? _jsx(GoalDetail, { run: run }) : null] }, run.id));
334
- }), mode === "adding" ? _jsxs(Text, { color: theme.primary, children: ["New goal: ", inputText] }) : null] })), _jsx(Box, { marginTop: 1, children: mode === "confirmDelete" ? (_jsx(Text, { color: theme.warning, children: "Confirm archive selected goal: y/n" })) : mode === "adding" ? (_jsxs(Text, { color: theme.textDim, children: [_jsx(Text, { color: theme.primary, children: "Enter" }), " add · ", _jsx(Text, { color: theme.primary, children: "ESC" }), " cancel"] })) : (_jsxs(Text, { color: theme.textDim, children: [_jsx(Text, { color: theme.primary, children: "\u2191\u2193" }), " move · (", _jsx(Text, { color: theme.primary, children: "d" }), ")etail · (", _jsx(Text, { color: theme.primary, children: "r" }), ")un · (", _jsx(Text, { color: theme.primary, children: "v" }), ")erify · (", _jsx(Text, { color: theme.primary, children: "p" }), ")ause · (", _jsx(Text, { color: theme.primary, children: "a" }), ")dd · (", _jsx(Text, { color: theme.primary, children: "x" }), ")archive · ", _jsx(Text, { color: theme.primary, children: "ESC" }), " close"] })) }), status ? (_jsx(Box, { children: _jsx(Text, { color: theme.secondary, children: status }) })) : null] }));
663
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: expandedRun?.id === run.id ? "round" : undefined, borderColor: expandedRun?.id === run.id ? theme.primary : undefined, paddingX: expandedRun?.id === run.id ? 1 : 0, children: [_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: selected ? theme.primary : theme.textDim, children: selected ? " " : " " }), _jsx(StatusChip, { label: run.status, color: getGoalCardStatusColor({
664
+ status: run.status,
665
+ selected,
666
+ primaryColor: theme.primary,
667
+ textColor: theme.text,
668
+ }) }), _jsxs(Text, { color: getGoalCardTitleColor({
669
+ selected,
670
+ primaryColor: theme.primary,
671
+ textColor: theme.text,
672
+ }), bold: selected, children: [" ", run.title] }), _jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", run.id.slice(0, 8)] })] }), expandedRun?.id === run.id ? null : (_jsxs(_Fragment, { children: [_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: theme.textDim, children: selected ? " " : " " }), _jsx(Text, { color: statusColor(run.status) || theme.secondary, children: getGoalReadinessText(run) }), _jsx(Text, { color: theme.textDim, children: " \u00B7 " }), _jsx(Text, { color: theme.text, children: formatGoalProgressText(run) }), _jsx(Text, { color: theme.textDim, children: " \u00B7 " }), _jsx(Text, { color: verifierSummaryColor(run, theme.textDim), children: formatGoalVerifierSummary(run) })] }), _jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: theme.textDim, children: selected ? " " : " " }), _jsx(Text, { color: goalHasBlockingPrerequisites(run) ? theme.warning : GOAL_SUCCESS, children: formatGoalPrerequisiteSummary(run) }), _jsx(Text, { color: theme.textDim, children: " \u00B7 " }), _jsx(Text, { color: run.tasks.length > 0 ? GOAL_SUCCESS : theme.text, children: formatGoalTaskSummary(run) })] })] })), blocked ? (_jsxs(Text, { color: theme.warning, wrap: "truncate", children: [selected ? " " : " ", "\u26A0 prerequisite needed before workers continue"] })) : run.status === "running" || run.status === "verifying" ? (_jsxs(Text, { color: GOAL_ACTIVE, wrap: "truncate", children: [selected ? " " : " ", "\u25CF active \u2014 watching worker/verifier progress"] })) : null, run.blockers.length > 0 ? (_jsxs(Text, { color: theme.warning, wrap: "truncate", children: [selected ? " " : " ", "blocker: ", run.blockers[0]] })) : null, expandedRun?.id === run.id ? (_jsx(GoalDetail, { run: run, maxRows: detailViewportRows, scrollOffset: detailScrollOffset })) : null] }, run.id));
673
+ }), hiddenAfter > 0 ? (_jsxs(Text, { color: theme.textDim, children: ["\u2193 ", hiddenAfter, " later goal", hiddenAfter === 1 ? "" : "s"] })) : null] })), _jsx(Box, { marginTop: 1, children: mode === "confirmDelete" ? (_jsx(Text, { color: theme.warning, children: "Confirm archive selected goal: y/n" })) : (_jsxs(Text, { color: theme.textDim, children: [_jsx(Text, { color: theme.primary, children: "\u2191\u2193/jk" }), expandedRun ? " scroll detail · " : " select · ", _jsx(Text, { color: theme.primary, children: "Enter/d" }), expandedRun ? " close detail · " : " detail · ", expandedRun ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.primary, children: "PgUp/PgDn" }), " page detail · "] })) : null, _jsx(Text, { color: theme.primary, children: "r" }), " run · ", _jsx(Text, { color: theme.primary, children: "v" }), " verify · ", _jsx(Text, { color: theme.primary, children: "p" }), " pause · ", _jsx(Text, { color: theme.primary, children: "x" }), " archive · ", _jsx(Text, { color: theme.primary, children: "Esc" }), " close"] })) }), status ? (_jsx(Box, { children: _jsx(Text, { color: theme.secondary, children: status }) })) : null] }));
335
674
  }
336
675
  //# sourceMappingURL=GoalOverlay.js.map