@kenkaiiii/ggcoder 4.3.205 → 4.3.206

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 (173) hide show
  1. package/dist/cli.js +38 -26
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.js +2 -2
  4. package/dist/core/compaction/compactor.d.ts.map +1 -1
  5. package/dist/core/compaction/compactor.js +6 -0
  6. package/dist/core/compaction/compactor.js.map +1 -1
  7. package/dist/core/compaction/compactor.test.js +16 -0
  8. package/dist/core/compaction/compactor.test.js.map +1 -1
  9. package/dist/core/goal-controller.d.ts +57 -0
  10. package/dist/core/goal-controller.d.ts.map +1 -0
  11. package/dist/core/goal-controller.js +285 -0
  12. package/dist/core/goal-controller.js.map +1 -0
  13. package/dist/core/goal-controller.test.d.ts +2 -0
  14. package/dist/core/goal-controller.test.d.ts.map +1 -0
  15. package/dist/core/goal-controller.test.js +377 -0
  16. package/dist/core/goal-controller.test.js.map +1 -0
  17. package/dist/core/goal-lifecycle-smoke.test.d.ts +2 -0
  18. package/dist/core/goal-lifecycle-smoke.test.d.ts.map +1 -0
  19. package/dist/core/goal-lifecycle-smoke.test.js +207 -0
  20. package/dist/core/goal-lifecycle-smoke.test.js.map +1 -0
  21. package/dist/core/goal-store.d.ts +164 -0
  22. package/dist/core/goal-store.d.ts.map +1 -0
  23. package/dist/core/goal-store.js +721 -0
  24. package/dist/core/goal-store.js.map +1 -0
  25. package/dist/core/goal-store.test.d.ts +2 -0
  26. package/dist/core/goal-store.test.d.ts.map +1 -0
  27. package/dist/core/goal-store.test.js +341 -0
  28. package/dist/core/goal-store.test.js.map +1 -0
  29. package/dist/core/goal-verifier.d.ts +17 -0
  30. package/dist/core/goal-verifier.d.ts.map +1 -0
  31. package/dist/core/goal-verifier.js +84 -0
  32. package/dist/core/goal-verifier.js.map +1 -0
  33. package/dist/core/goal-verifier.test.d.ts +2 -0
  34. package/dist/core/goal-verifier.test.d.ts.map +1 -0
  35. package/dist/core/goal-verifier.test.js +88 -0
  36. package/dist/core/goal-verifier.test.js.map +1 -0
  37. package/dist/core/goal-worker.d.ts +47 -0
  38. package/dist/core/goal-worker.d.ts.map +1 -0
  39. package/dist/core/goal-worker.js +329 -0
  40. package/dist/core/goal-worker.js.map +1 -0
  41. package/dist/core/goal-worker.test.d.ts +2 -0
  42. package/dist/core/goal-worker.test.d.ts.map +1 -0
  43. package/dist/core/goal-worker.test.js +206 -0
  44. package/dist/core/goal-worker.test.js.map +1 -0
  45. package/dist/core/oauth/gemini.d.ts.map +1 -1
  46. package/dist/core/oauth/gemini.js +138 -30
  47. package/dist/core/oauth/gemini.js.map +1 -1
  48. package/dist/core/oauth/gemini.test.d.ts +2 -0
  49. package/dist/core/oauth/gemini.test.d.ts.map +1 -0
  50. package/dist/core/oauth/gemini.test.js +154 -0
  51. package/dist/core/oauth/gemini.test.js.map +1 -0
  52. package/dist/core/prompt-commands.d.ts.map +1 -1
  53. package/dist/core/prompt-commands.js +124 -0
  54. package/dist/core/prompt-commands.js.map +1 -1
  55. package/dist/core/prompt-commands.test.js +36 -0
  56. package/dist/core/prompt-commands.test.js.map +1 -1
  57. package/dist/index.d.ts +1 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +1 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/interactive.d.ts.map +1 -1
  62. package/dist/interactive.js +20 -11
  63. package/dist/interactive.js.map +1 -1
  64. package/dist/system-prompt.d.ts.map +1 -1
  65. package/dist/system-prompt.js +18 -50
  66. package/dist/system-prompt.js.map +1 -1
  67. package/dist/system-prompt.test.js +124 -1
  68. package/dist/system-prompt.test.js.map +1 -1
  69. package/dist/tools/edit-diff.d.ts.map +1 -1
  70. package/dist/tools/edit-diff.js +71 -32
  71. package/dist/tools/edit-diff.js.map +1 -1
  72. package/dist/tools/edit-diff.test.js +14 -0
  73. package/dist/tools/edit-diff.test.js.map +1 -1
  74. package/dist/tools/edit.d.ts.map +1 -1
  75. package/dist/tools/edit.js +16 -6
  76. package/dist/tools/edit.js.map +1 -1
  77. package/dist/tools/edit.test.js +27 -0
  78. package/dist/tools/edit.test.js.map +1 -1
  79. package/dist/tools/enter-plan.d.ts.map +1 -1
  80. package/dist/tools/enter-plan.js +1 -0
  81. package/dist/tools/enter-plan.js.map +1 -1
  82. package/dist/tools/goals.d.ts +110 -0
  83. package/dist/tools/goals.d.ts.map +1 -0
  84. package/dist/tools/goals.js +500 -0
  85. package/dist/tools/goals.js.map +1 -0
  86. package/dist/tools/goals.test.d.ts +2 -0
  87. package/dist/tools/goals.test.d.ts.map +1 -0
  88. package/dist/tools/goals.test.js +431 -0
  89. package/dist/tools/goals.test.js.map +1 -0
  90. package/dist/tools/index.d.ts +2 -0
  91. package/dist/tools/index.d.ts.map +1 -1
  92. package/dist/tools/index.js +6 -0
  93. package/dist/tools/index.js.map +1 -1
  94. package/dist/tools/prompt-hints.d.ts.map +1 -1
  95. package/dist/tools/prompt-hints.js +2 -0
  96. package/dist/tools/prompt-hints.js.map +1 -1
  97. package/dist/tools/source-path.d.ts +9 -0
  98. package/dist/tools/source-path.d.ts.map +1 -0
  99. package/dist/tools/source-path.js +119 -0
  100. package/dist/tools/source-path.js.map +1 -0
  101. package/dist/tools/source-path.test.d.ts +2 -0
  102. package/dist/tools/source-path.test.d.ts.map +1 -0
  103. package/dist/tools/source-path.test.js +80 -0
  104. package/dist/tools/source-path.test.js.map +1 -0
  105. package/dist/tools/subagent.js +16 -0
  106. package/dist/tools/subagent.js.map +1 -1
  107. package/dist/ui/App.d.ts +36 -3
  108. package/dist/ui/App.d.ts.map +1 -1
  109. package/dist/ui/App.js +879 -57
  110. package/dist/ui/App.js.map +1 -1
  111. package/dist/ui/activity-phrases.d.ts.map +1 -1
  112. package/dist/ui/activity-phrases.js +7 -3
  113. package/dist/ui/activity-phrases.js.map +1 -1
  114. package/dist/ui/app-state-persistence.test.d.ts +2 -0
  115. package/dist/ui/app-state-persistence.test.d.ts.map +1 -0
  116. package/dist/ui/app-state-persistence.test.js +56 -0
  117. package/dist/ui/app-state-persistence.test.js.map +1 -0
  118. package/dist/ui/components/BackgroundTasksBar.d.ts +16 -1
  119. package/dist/ui/components/BackgroundTasksBar.d.ts.map +1 -1
  120. package/dist/ui/components/BackgroundTasksBar.js +15 -2
  121. package/dist/ui/components/BackgroundTasksBar.js.map +1 -1
  122. package/dist/ui/components/Banner.d.ts +2 -1
  123. package/dist/ui/components/Banner.d.ts.map +1 -1
  124. package/dist/ui/components/Banner.js +3 -3
  125. package/dist/ui/components/Banner.js.map +1 -1
  126. package/dist/ui/components/GoalOverlay.d.ts +21 -0
  127. package/dist/ui/components/GoalOverlay.d.ts.map +1 -0
  128. package/dist/ui/components/GoalOverlay.js +336 -0
  129. package/dist/ui/components/GoalOverlay.js.map +1 -0
  130. package/dist/ui/components/GoalStatusBar.d.ts +24 -0
  131. package/dist/ui/components/GoalStatusBar.d.ts.map +1 -0
  132. package/dist/ui/components/GoalStatusBar.js +113 -0
  133. package/dist/ui/components/GoalStatusBar.js.map +1 -0
  134. package/dist/ui/components/InputArea.d.ts +2 -1
  135. package/dist/ui/components/InputArea.d.ts.map +1 -1
  136. package/dist/ui/components/InputArea.js +6 -1
  137. package/dist/ui/components/InputArea.js.map +1 -1
  138. package/dist/ui/components/ToolExecution.d.ts.map +1 -1
  139. package/dist/ui/components/ToolExecution.js +94 -1
  140. package/dist/ui/components/ToolExecution.js.map +1 -1
  141. package/dist/ui/footer-status-layout.test.d.ts +2 -0
  142. package/dist/ui/footer-status-layout.test.d.ts.map +1 -0
  143. package/dist/ui/footer-status-layout.test.js +56 -0
  144. package/dist/ui/footer-status-layout.test.js.map +1 -0
  145. package/dist/ui/goal-events.d.ts +20 -0
  146. package/dist/ui/goal-events.d.ts.map +1 -0
  147. package/dist/ui/goal-events.js +102 -0
  148. package/dist/ui/goal-events.js.map +1 -0
  149. package/dist/ui/goal-events.test.d.ts +2 -0
  150. package/dist/ui/goal-events.test.d.ts.map +1 -0
  151. package/dist/ui/goal-events.test.js +208 -0
  152. package/dist/ui/goal-events.test.js.map +1 -0
  153. package/dist/ui/goal-overlay.test.d.ts +2 -0
  154. package/dist/ui/goal-overlay.test.d.ts.map +1 -0
  155. package/dist/ui/goal-overlay.test.js +122 -0
  156. package/dist/ui/goal-overlay.test.js.map +1 -0
  157. package/dist/ui/goal-status-bar.test.d.ts +2 -0
  158. package/dist/ui/goal-status-bar.test.d.ts.map +1 -0
  159. package/dist/ui/goal-status-bar.test.js +143 -0
  160. package/dist/ui/goal-status-bar.test.js.map +1 -0
  161. package/dist/ui/live-item-flush.test.js +48 -0
  162. package/dist/ui/live-item-flush.test.js.map +1 -1
  163. package/dist/ui/render.d.ts +8 -3
  164. package/dist/ui/render.d.ts.map +1 -1
  165. package/dist/ui/render.js +10 -3
  166. package/dist/ui/render.js.map +1 -1
  167. package/dist/ui/scroll-stabilization.test.d.ts +2 -0
  168. package/dist/ui/scroll-stabilization.test.d.ts.map +1 -0
  169. package/dist/ui/scroll-stabilization.test.js +23 -0
  170. package/dist/ui/scroll-stabilization.test.js.map +1 -0
  171. package/dist/utils/format.js +44 -0
  172. package/dist/utils/format.js.map +1 -1
  173. package/package.json +6 -5
@@ -0,0 +1,721 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { mkdir, readdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { basename, join, resolve } from "node:path";
5
+ const GOALS_BASE_ENV = "GG_GOALS_BASE";
6
+ const DEFAULT_PROJECT_DIR_NAME = "projects";
7
+ let writeQueue = Promise.resolve();
8
+ function goalsBaseDir() {
9
+ return process.env[GOALS_BASE_ENV] ?? join(homedir(), ".gg", "goals", DEFAULT_PROJECT_DIR_NAME);
10
+ }
11
+ export function normalizeProjectPath(cwd) {
12
+ return resolve(cwd);
13
+ }
14
+ function nowIso() {
15
+ return new Date().toISOString();
16
+ }
17
+ function isObject(value) {
18
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19
+ }
20
+ function stringArray(value) {
21
+ return Array.isArray(value)
22
+ ? value.filter((item) => typeof item === "string")
23
+ : [];
24
+ }
25
+ function optionalString(value) {
26
+ return typeof value === "string" && value.length > 0 ? value : undefined;
27
+ }
28
+ function isRunStatus(value) {
29
+ return (value === "draft" ||
30
+ value === "blocked" ||
31
+ value === "ready" ||
32
+ value === "running" ||
33
+ value === "verifying" ||
34
+ value === "passed" ||
35
+ value === "failed" ||
36
+ value === "paused");
37
+ }
38
+ function isTaskStatus(value) {
39
+ return (value === "pending" ||
40
+ value === "running" ||
41
+ value === "verifying" ||
42
+ value === "done" ||
43
+ value === "failed" ||
44
+ value === "blocked");
45
+ }
46
+ function isPrerequisiteStatus(value) {
47
+ return value === "unknown" || value === "met" || value === "missing";
48
+ }
49
+ function isEvidenceKind(value) {
50
+ return (value === "log" ||
51
+ value === "command" ||
52
+ value === "screenshot" ||
53
+ value === "file" ||
54
+ value === "summary");
55
+ }
56
+ function isEvidenceMechanism(value) {
57
+ return (value === "command" ||
58
+ value === "test" ||
59
+ value === "script" ||
60
+ value === "fixture" ||
61
+ value === "log" ||
62
+ value === "screenshot" ||
63
+ value === "video" ||
64
+ value === "browser" ||
65
+ value === "device" ||
66
+ value === "source" ||
67
+ value === "manual");
68
+ }
69
+ function isEvidencePlanStatus(value) {
70
+ return value === "planned" || value === "ready" || value === "blocked";
71
+ }
72
+ function isVerificationStatus(value) {
73
+ return value === "pass" || value === "fail" || value === "unknown";
74
+ }
75
+ function normalizeVerification(value) {
76
+ if (!isObject(value))
77
+ return undefined;
78
+ return {
79
+ status: isVerificationStatus(value.status) ? value.status : "unknown",
80
+ summary: typeof value.summary === "string" ? value.summary : "",
81
+ ...(optionalString(value.command) ? { command: optionalString(value.command) } : {}),
82
+ ...(typeof value.exitCode === "number" ? { exitCode: value.exitCode } : {}),
83
+ ...(optionalString(value.outputPath) ? { outputPath: optionalString(value.outputPath) } : {}),
84
+ checkedAt: typeof value.checkedAt === "string" ? value.checkedAt : nowIso(),
85
+ };
86
+ }
87
+ function normalizePrerequisite(value) {
88
+ if (!isObject(value))
89
+ return null;
90
+ const label = typeof value.label === "string" ? value.label : "Prerequisite";
91
+ return {
92
+ id: typeof value.id === "string" ? value.id : randomUUID(),
93
+ label,
94
+ status: isPrerequisiteStatus(value.status) ? value.status : "unknown",
95
+ ...(optionalString(value.checkCommand)
96
+ ? { checkCommand: optionalString(value.checkCommand) }
97
+ : {}),
98
+ ...(optionalString(value.instructions)
99
+ ? { instructions: optionalString(value.instructions) }
100
+ : {}),
101
+ ...(optionalString(value.evidence) ? { evidence: optionalString(value.evidence) } : {}),
102
+ };
103
+ }
104
+ function normalizeHarnessItem(value) {
105
+ if (!isObject(value))
106
+ return null;
107
+ const label = typeof value.label === "string" ? value.label : "Harness";
108
+ return {
109
+ id: typeof value.id === "string" ? value.id : randomUUID(),
110
+ label,
111
+ ...(optionalString(value.command) ? { command: optionalString(value.command) } : {}),
112
+ ...(optionalString(value.path) ? { path: optionalString(value.path) } : {}),
113
+ ...(optionalString(value.description)
114
+ ? { description: optionalString(value.description) }
115
+ : {}),
116
+ };
117
+ }
118
+ function normalizeEvidencePlanItem(value) {
119
+ if (!isObject(value))
120
+ return null;
121
+ const label = typeof value.label === "string" ? value.label : "Evidence path";
122
+ const description = typeof value.description === "string" ? value.description : label;
123
+ return {
124
+ id: typeof value.id === "string" ? value.id : randomUUID(),
125
+ label,
126
+ mechanism: isEvidenceMechanism(value.mechanism) ? value.mechanism : "command",
127
+ description,
128
+ status: isEvidencePlanStatus(value.status) ? value.status : "planned",
129
+ ...(optionalString(value.command) ? { command: optionalString(value.command) } : {}),
130
+ ...(optionalString(value.path) ? { path: optionalString(value.path) } : {}),
131
+ ...(optionalString(value.instructions)
132
+ ? { instructions: optionalString(value.instructions) }
133
+ : {}),
134
+ ...(optionalString(value.evidence) ? { evidence: optionalString(value.evidence) } : {}),
135
+ };
136
+ }
137
+ function normalizeTask(value) {
138
+ if (!isObject(value))
139
+ return null;
140
+ const title = typeof value.title === "string" ? value.title : "Goal task";
141
+ const prompt = typeof value.prompt === "string" ? value.prompt : title;
142
+ return {
143
+ id: typeof value.id === "string" ? value.id : randomUUID(),
144
+ title,
145
+ prompt,
146
+ status: isTaskStatus(value.status) ? value.status : "pending",
147
+ ...(optionalString(value.workerId) ? { workerId: optionalString(value.workerId) } : {}),
148
+ attempts: typeof value.attempts === "number" && value.attempts >= 0 ? value.attempts : 0,
149
+ ...(normalizeVerification(value.verification)
150
+ ? { verification: normalizeVerification(value.verification) }
151
+ : {}),
152
+ ...(optionalString(value.lastSummary)
153
+ ? { lastSummary: optionalString(value.lastSummary) }
154
+ : {}),
155
+ };
156
+ }
157
+ function normalizeEvidence(value) {
158
+ if (!isObject(value))
159
+ return null;
160
+ const label = typeof value.label === "string" ? value.label : "Evidence";
161
+ return {
162
+ id: typeof value.id === "string" ? value.id : randomUUID(),
163
+ kind: isEvidenceKind(value.kind) ? value.kind : "summary",
164
+ label,
165
+ ...(optionalString(value.path) ? { path: optionalString(value.path) } : {}),
166
+ ...(optionalString(value.content) ? { content: optionalString(value.content) } : {}),
167
+ createdAt: typeof value.createdAt === "string" ? value.createdAt : nowIso(),
168
+ };
169
+ }
170
+ function normalizeVerifier(value) {
171
+ if (!isObject(value))
172
+ return undefined;
173
+ const description = typeof value.description === "string" ? value.description : "Goal verifier";
174
+ return {
175
+ description,
176
+ ...(optionalString(value.command) ? { command: optionalString(value.command) } : {}),
177
+ ...(normalizeVerification(value.lastResult)
178
+ ? { lastResult: normalizeVerification(value.lastResult) }
179
+ : {}),
180
+ };
181
+ }
182
+ function normalizeRun(value, fallbackProjectPath) {
183
+ if (!isObject(value))
184
+ return null;
185
+ const title = typeof value.title === "string" ? value.title : "Untitled goal";
186
+ const goal = typeof value.goal === "string" ? value.goal : title;
187
+ const createdAt = typeof value.createdAt === "string" ? value.createdAt : nowIso();
188
+ const projectPath = typeof value.projectPath === "string" ? value.projectPath : fallbackProjectPath;
189
+ const prerequisites = Array.isArray(value.prerequisites)
190
+ ? value.prerequisites
191
+ .map(normalizePrerequisite)
192
+ .filter((item) => !!item)
193
+ : [];
194
+ const tasks = Array.isArray(value.tasks)
195
+ ? value.tasks.map(normalizeTask).filter((item) => !!item)
196
+ : [];
197
+ const computedStatus = deriveRunnableStatus(isRunStatus(value.status) ? value.status : "draft", prerequisites);
198
+ return {
199
+ id: typeof value.id === "string" ? value.id : randomUUID(),
200
+ title,
201
+ goal,
202
+ status: computedStatus,
203
+ createdAt,
204
+ updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : createdAt,
205
+ projectPath,
206
+ successCriteria: stringArray(value.successCriteria),
207
+ prerequisites,
208
+ harness: Array.isArray(value.harness)
209
+ ? value.harness.map(normalizeHarnessItem).filter((item) => !!item)
210
+ : [],
211
+ evidencePlan: Array.isArray(value.evidencePlan)
212
+ ? value.evidencePlan
213
+ .map(normalizeEvidencePlanItem)
214
+ .filter((item) => !!item)
215
+ : [],
216
+ tasks,
217
+ evidence: Array.isArray(value.evidence)
218
+ ? value.evidence.map(normalizeEvidence).filter((item) => !!item)
219
+ : [],
220
+ ...(normalizeVerifier(value.verifier) ? { verifier: normalizeVerifier(value.verifier) } : {}),
221
+ blockers: stringArray(value.blockers),
222
+ ...(optionalString(value.activeWorkerId)
223
+ ? { activeWorkerId: optionalString(value.activeWorkerId) }
224
+ : {}),
225
+ ...(optionalString(value.continueRequestedAt)
226
+ ? { continueRequestedAt: optionalString(value.continueRequestedAt) }
227
+ : {}),
228
+ };
229
+ }
230
+ function sortNewestFirst(runs) {
231
+ return [...runs].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
232
+ }
233
+ function isActiveGoalRun(run) {
234
+ return (run.status === "running" ||
235
+ run.status === "verifying" ||
236
+ run.activeWorkerId !== undefined ||
237
+ run.tasks.some((task) => task.status === "running" || task.status === "verifying"));
238
+ }
239
+ function wouldEraseActiveGoalRuns(previousRuns, nextRuns) {
240
+ if (nextRuns.length > 0)
241
+ return false;
242
+ return previousRuns.some(isActiveGoalRun);
243
+ }
244
+ function deriveRunnableStatus(requestedStatus, prerequisites) {
245
+ if (requestedStatus === "passed" ||
246
+ requestedStatus === "failed" ||
247
+ requestedStatus === "paused") {
248
+ return requestedStatus;
249
+ }
250
+ if (hasBlockingGoalPrerequisites(prerequisites))
251
+ return "blocked";
252
+ return requestedStatus;
253
+ }
254
+ function enqueueWrite(fn) {
255
+ const result = writeQueue.then(fn);
256
+ writeQueue = result.then(() => { }, () => { });
257
+ return result;
258
+ }
259
+ async function writeGoalRunsFile(cwd, runs) {
260
+ const normalizedCwd = normalizeProjectPath(cwd);
261
+ const dir = projectDir(normalizedCwd);
262
+ await mkdir(dir, { recursive: true });
263
+ const goalsPath = join(dir, "goals.json");
264
+ const existingRuns = await readGoalRunsFile(normalizedCwd);
265
+ if (wouldEraseActiveGoalRuns(existingRuns, runs)) {
266
+ const timestamp = nowIso();
267
+ const repairedRuns = existingRuns.map((run) => ({
268
+ ...run,
269
+ evidence: [
270
+ ...run.evidence,
271
+ createGoalEvidence({
272
+ kind: "summary",
273
+ label: "Goal store write rejected",
274
+ content: "Rejected an attempted empty Goal overwrite while active work was present; preserving existing durable state.",
275
+ createdAt: timestamp,
276
+ }),
277
+ ],
278
+ updatedAt: timestamp,
279
+ }));
280
+ await atomicWriteJson(goalsPath, sortNewestFirst(repairedRuns));
281
+ await Promise.all(repairedRuns.map((run) => writeGoalProgressJournalFromRun(normalizedCwd, run)));
282
+ return;
283
+ }
284
+ const sorted = sortNewestFirst([...runs]);
285
+ await atomicWriteJson(goalsPath, sorted);
286
+ await atomicWriteJson(join(dir, "meta.json"), {
287
+ path: normalizedCwd,
288
+ name: basename(normalizedCwd),
289
+ });
290
+ await Promise.all(sorted.map((run) => writeGoalProgressJournalFromRun(normalizedCwd, run)));
291
+ }
292
+ async function atomicWriteJson(path, value) {
293
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
294
+ await writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf-8");
295
+ await rename(tmpPath, path);
296
+ }
297
+ async function readGoalRunsFile(cwd) {
298
+ const normalizedCwd = normalizeProjectPath(cwd);
299
+ try {
300
+ const data = await readFile(join(projectDir(normalizedCwd), "goals.json"), "utf-8");
301
+ const parsed = JSON.parse(data);
302
+ if (!Array.isArray(parsed))
303
+ return [];
304
+ return sortNewestFirst(parsed
305
+ .map((item) => normalizeRun(item, normalizedCwd))
306
+ .filter((run) => run !== null));
307
+ }
308
+ catch {
309
+ return [];
310
+ }
311
+ }
312
+ export function hashPath(cwd) {
313
+ return createHash("sha256").update(normalizeProjectPath(cwd)).digest("hex").slice(0, 16);
314
+ }
315
+ export function projectDir(cwd) {
316
+ return join(goalsBaseDir(), hashPath(cwd));
317
+ }
318
+ async function discoverGoalRunsById(id) {
319
+ try {
320
+ const entries = await readdir(goalsBaseDir(), { withFileTypes: true });
321
+ const matches = [];
322
+ for (const entry of entries) {
323
+ if (!entry.isDirectory())
324
+ continue;
325
+ try {
326
+ const dir = join(goalsBaseDir(), entry.name);
327
+ const meta = await readProjectMeta(dir);
328
+ const fallbackProjectPath = meta?.path ?? dir;
329
+ const data = await readFile(join(dir, "goals.json"), "utf-8");
330
+ const parsed = JSON.parse(data);
331
+ if (!Array.isArray(parsed))
332
+ continue;
333
+ for (const item of parsed) {
334
+ const run = normalizeRun(item, fallbackProjectPath);
335
+ if (run && (run.id === id || run.id.startsWith(id)))
336
+ matches.push(run);
337
+ }
338
+ }
339
+ catch {
340
+ continue;
341
+ }
342
+ }
343
+ return sortNewestFirst(matches)[0] ?? null;
344
+ }
345
+ catch {
346
+ return null;
347
+ }
348
+ }
349
+ async function readProjectMeta(dir) {
350
+ try {
351
+ const parsed = JSON.parse(await readFile(join(dir, "meta.json"), "utf-8"));
352
+ return isObject(parsed) && typeof parsed.path === "string" ? { path: parsed.path } : null;
353
+ }
354
+ catch {
355
+ return null;
356
+ }
357
+ }
358
+ export function createGoalTask(input) {
359
+ return {
360
+ id: input.id ?? randomUUID(),
361
+ title: input.title,
362
+ prompt: input.prompt,
363
+ status: input.status ?? "pending",
364
+ ...(input.workerId ? { workerId: input.workerId } : {}),
365
+ attempts: input.attempts ?? 0,
366
+ ...(input.verification ? { verification: input.verification } : {}),
367
+ ...(input.lastSummary ? { lastSummary: input.lastSummary } : {}),
368
+ };
369
+ }
370
+ export function createGoalEvidence(input) {
371
+ return {
372
+ id: input.id ?? randomUUID(),
373
+ kind: input.kind,
374
+ label: input.label,
375
+ ...(input.path ? { path: input.path } : {}),
376
+ ...(input.content ? { content: input.content } : {}),
377
+ createdAt: input.createdAt ?? nowIso(),
378
+ };
379
+ }
380
+ export function createGoalRun(cwd, input) {
381
+ const normalizedCwd = normalizeProjectPath(cwd);
382
+ const timestamp = nowIso();
383
+ const prerequisites = input.prerequisites ?? [];
384
+ const status = deriveRunnableStatus(input.status ?? "draft", prerequisites);
385
+ return {
386
+ id: input.id ?? randomUUID(),
387
+ title: input.title,
388
+ goal: input.goal,
389
+ status,
390
+ createdAt: timestamp,
391
+ updatedAt: timestamp,
392
+ projectPath: normalizedCwd,
393
+ successCriteria: input.successCriteria ?? [],
394
+ prerequisites,
395
+ harness: input.harness ?? [],
396
+ evidencePlan: input.evidencePlan ?? [],
397
+ tasks: input.tasks ?? [],
398
+ evidence: input.evidence ?? [],
399
+ ...(input.verifier ? { verifier: input.verifier } : {}),
400
+ blockers: input.blockers ?? [],
401
+ ...(input.activeWorkerId ? { activeWorkerId: input.activeWorkerId } : {}),
402
+ ...(input.continueRequestedAt ? { continueRequestedAt: input.continueRequestedAt } : {}),
403
+ };
404
+ }
405
+ export async function loadGoalRuns(cwd) {
406
+ return readGoalRunsFile(cwd);
407
+ }
408
+ export async function saveGoalRuns(cwd, runs) {
409
+ return enqueueWrite(() => writeGoalRunsFile(cwd, runs));
410
+ }
411
+ export async function reconcileActiveGoalRuns(cwd, options = {}) {
412
+ return enqueueWrite(async () => {
413
+ const runs = await loadGoalRuns(cwd);
414
+ const timestamp = nowIso();
415
+ const repairedRunIds = [];
416
+ let evidenceCount = 0;
417
+ const nextRuns = runs.map((run) => {
418
+ let next = run;
419
+ const evidence = [];
420
+ const blockers = new Set(run.blockers);
421
+ let repaired = false;
422
+ if (run.activeWorkerId && !options.isWorkerActive?.(run.activeWorkerId, run)) {
423
+ const workerId = run.activeWorkerId;
424
+ next = { ...next, activeWorkerId: undefined };
425
+ evidence.push(createGoalEvidence({
426
+ kind: "summary",
427
+ label: "Goal worker reconciled",
428
+ content: `Cleared stale activeWorkerId ${workerId}; no in-memory Goal worker was observed after startup/runtime reconciliation.`,
429
+ createdAt: timestamp,
430
+ }));
431
+ repaired = true;
432
+ }
433
+ const tasks = next.tasks.map((task) => {
434
+ if (task.status !== "running" && task.status !== "verifying")
435
+ return task;
436
+ const workerId = task.workerId;
437
+ if (workerId && options.isWorkerActive?.(workerId, run))
438
+ return task;
439
+ repaired = true;
440
+ evidence.push(createGoalEvidence({
441
+ kind: "summary",
442
+ label: "Goal task reconciled",
443
+ content: `Reset stale task "${task.title}" (${task.id}) from ${task.status} to pending; no in-memory worker/verifier was observed.`,
444
+ createdAt: timestamp,
445
+ }));
446
+ return {
447
+ ...task,
448
+ status: "pending",
449
+ lastSummary: `Reset from stale ${task.status} state during Goal reconciliation.`,
450
+ };
451
+ });
452
+ next = { ...next, tasks };
453
+ if (next.status === "running") {
454
+ const hasRunningWorker = next.activeWorkerId !== undefined &&
455
+ options.isWorkerActive?.(next.activeWorkerId, run) === true;
456
+ const hasActiveTask = next.tasks.some((task) => (task.status === "running" || task.status === "verifying") &&
457
+ task.workerId !== undefined &&
458
+ options.isWorkerActive?.(task.workerId, run) === true);
459
+ if (!hasRunningWorker && !hasActiveTask) {
460
+ next = { ...next, status: "ready" };
461
+ repaired = true;
462
+ }
463
+ }
464
+ else if (next.status === "verifying" && !options.isVerifierActive?.(next)) {
465
+ const blocker = "Verifier was interrupted; rerun or continue the Goal to verify again.";
466
+ blockers.add(blocker);
467
+ evidence.push(createGoalEvidence({
468
+ kind: "summary",
469
+ label: "Goal verifier reconciled",
470
+ content: "Reset stale verifying state to ready because no in-memory verifier process was observed.",
471
+ createdAt: timestamp,
472
+ }));
473
+ next = { ...next, status: "ready" };
474
+ repaired = true;
475
+ }
476
+ if (!repaired)
477
+ return run;
478
+ repairedRunIds.push(run.id);
479
+ evidenceCount += evidence.length;
480
+ return {
481
+ ...next,
482
+ evidence: [...next.evidence, ...evidence],
483
+ blockers: [...blockers],
484
+ updatedAt: timestamp,
485
+ };
486
+ });
487
+ if (repairedRunIds.length > 0)
488
+ await writeGoalRunsFile(cwd, nextRuns);
489
+ return { runs: sortNewestFirst(nextRuns), repairedRunIds, evidenceCount };
490
+ });
491
+ }
492
+ export async function upsertGoalRun(cwd, input) {
493
+ return enqueueWrite(async () => {
494
+ const runs = await loadGoalRuns(cwd);
495
+ const existingIndex = input.id ? runs.findIndex((run) => run.id === input.id) : -1;
496
+ const existing = existingIndex >= 0 ? runs[existingIndex] : undefined;
497
+ const merged = existing
498
+ ? {
499
+ ...existing,
500
+ ...input,
501
+ id: existing.id,
502
+ projectPath: normalizeProjectPath(cwd),
503
+ createdAt: existing.createdAt,
504
+ updatedAt: nowIso(),
505
+ successCriteria: input.successCriteria ?? existing.successCriteria,
506
+ prerequisites: input.prerequisites ?? existing.prerequisites,
507
+ harness: input.harness ?? existing.harness,
508
+ evidencePlan: input.evidencePlan ?? existing.evidencePlan,
509
+ tasks: input.tasks ?? existing.tasks,
510
+ evidence: input.evidence ?? existing.evidence,
511
+ blockers: input.blockers ?? existing.blockers,
512
+ status: deriveRunnableStatus(input.status ?? existing.status, input.prerequisites ?? existing.prerequisites),
513
+ }
514
+ : createGoalRun(cwd, input);
515
+ const nextRuns = existingIndex >= 0 ? [...runs] : [merged, ...runs];
516
+ if (existingIndex >= 0)
517
+ nextRuns[existingIndex] = merged;
518
+ await writeGoalRunsFile(cwd, nextRuns);
519
+ return merged;
520
+ });
521
+ }
522
+ export async function getGoalRun(cwd, id) {
523
+ const runs = await loadGoalRuns(cwd);
524
+ return (runs.find((run) => run.id === id || run.id.startsWith(id)) ?? (await discoverGoalRunsById(id)));
525
+ }
526
+ export async function getActiveGoalRun(cwd) {
527
+ const runs = await loadGoalRuns(cwd);
528
+ return (runs.find((run) => run.status === "running" || run.status === "verifying") ??
529
+ runs.find((run) => run.status === "ready" || run.status === "blocked") ??
530
+ runs.find((run) => run.status === "draft" || run.status === "paused") ??
531
+ runs[0] ??
532
+ null);
533
+ }
534
+ export async function appendGoalDecision(cwd, runId, decision) {
535
+ const parts = [`kind=${decision.kind}`];
536
+ if ("reason" in decision && decision.reason)
537
+ parts.push(`reason=${decision.reason}`);
538
+ if ("content" in decision && decision.content)
539
+ parts.push(decision.content);
540
+ if ("task" in decision && decision.task) {
541
+ const task = decision.task;
542
+ parts.push(`task=${task.id}`, `title=${task.title}`);
543
+ if (task.workerId)
544
+ parts.push(`worker=${task.workerId}`);
545
+ }
546
+ if ("attempts" in decision && typeof decision.attempts === "number")
547
+ parts.push(`attempts=${decision.attempts}`);
548
+ if ("workerId" in decision && decision.workerId)
549
+ parts.push(`worker=${decision.workerId}`);
550
+ if ("command" in decision && decision.command)
551
+ parts.push(`verifier=${decision.command}`);
552
+ if ("status" in decision && decision.status)
553
+ parts.push(`status=${decision.status}`);
554
+ parts.push(`timestamp=${nowIso()}`);
555
+ return appendGoalEvidence(cwd, runId, {
556
+ kind: "summary",
557
+ label: `Goal decision: ${decision.kind}`,
558
+ content: parts.join("; "),
559
+ });
560
+ }
561
+ export async function appendGoalEvidence(cwd, runId, input) {
562
+ const discovered = await getGoalRun(cwd, runId);
563
+ const writeCwd = discovered?.projectPath ?? cwd;
564
+ return enqueueWrite(async () => {
565
+ const runs = await loadGoalRuns(writeCwd);
566
+ const index = runs.findIndex((run) => run.id === runId || run.id.startsWith(runId));
567
+ if (index === -1)
568
+ return null;
569
+ const run = runs[index];
570
+ const updated = {
571
+ ...run,
572
+ evidence: [...run.evidence, createGoalEvidence(input)],
573
+ updatedAt: nowIso(),
574
+ };
575
+ const nextRuns = [...runs];
576
+ nextRuns[index] = updated;
577
+ await writeGoalRunsFile(writeCwd, nextRuns);
578
+ return updated;
579
+ });
580
+ }
581
+ export async function updateGoalTask(cwd, runId, taskId, patch) {
582
+ const discovered = await getGoalRun(cwd, runId);
583
+ const writeCwd = discovered?.projectPath ?? cwd;
584
+ return enqueueWrite(async () => {
585
+ const runs = await loadGoalRuns(writeCwd);
586
+ const runIndex = runs.findIndex((run) => run.id === runId || run.id.startsWith(runId));
587
+ if (runIndex === -1)
588
+ return null;
589
+ const run = runs[runIndex];
590
+ const taskIndex = run.tasks.findIndex((task) => task.id === taskId || task.id.startsWith(taskId));
591
+ const tasks = [...run.tasks];
592
+ if (taskIndex === -1) {
593
+ if ("title" in patch && "prompt" in patch && patch.title && patch.prompt) {
594
+ tasks.push(createGoalTask({
595
+ ...patch,
596
+ title: patch.title,
597
+ prompt: patch.prompt,
598
+ }));
599
+ }
600
+ else {
601
+ return null;
602
+ }
603
+ }
604
+ else {
605
+ const existingTask = tasks[taskIndex];
606
+ tasks[taskIndex] = {
607
+ ...existingTask,
608
+ ...patch,
609
+ id: existingTask.id,
610
+ title: patch.title ?? existingTask.title,
611
+ prompt: patch.prompt ?? existingTask.prompt,
612
+ };
613
+ }
614
+ const updated = { ...run, tasks, updatedAt: nowIso() };
615
+ const nextRuns = [...runs];
616
+ nextRuns[runIndex] = updated;
617
+ await writeGoalRunsFile(writeCwd, nextRuns);
618
+ return updated;
619
+ });
620
+ }
621
+ export function isBlockingGoalPrerequisite(item) {
622
+ return item.status === "missing" || (item.status === "unknown" && !item.evidence);
623
+ }
624
+ export function hasBlockingGoalPrerequisites(prerequisites) {
625
+ return prerequisites.some(isBlockingGoalPrerequisite);
626
+ }
627
+ export function goalHasBlockingPrerequisites(run) {
628
+ return hasBlockingGoalPrerequisites(run.prerequisites);
629
+ }
630
+ export function formatGoalPrerequisiteInstruction(item) {
631
+ return item.instructions?.trim() || "User must provide this prerequisite.";
632
+ }
633
+ export function formatGoalBlockingPrerequisiteList(prerequisites) {
634
+ const missing = prerequisites.filter(isBlockingGoalPrerequisite);
635
+ if (missing.length === 0)
636
+ return "Goal has no missing user prerequisites.";
637
+ return missing
638
+ .map((item) => `${item.label}: ${formatGoalPrerequisiteInstruction(item)}`)
639
+ .join("; ");
640
+ }
641
+ export function formatGoalBlockingPrerequisites(run) {
642
+ return formatGoalBlockingPrerequisiteList(run.prerequisites);
643
+ }
644
+ export function summarizeGoalCountsFromRuns(runs) {
645
+ const counts = {
646
+ total: runs.length,
647
+ active: 0,
648
+ blocked: 0,
649
+ pending: 0,
650
+ running: 0,
651
+ passed: 0,
652
+ failed: 0,
653
+ };
654
+ for (const run of runs) {
655
+ if (run.status === "blocked")
656
+ counts.blocked++;
657
+ if (run.status === "running" || run.status === "verifying")
658
+ counts.running++;
659
+ if (run.status === "passed")
660
+ counts.passed++;
661
+ if (run.status === "failed")
662
+ counts.failed++;
663
+ if (run.status === "draft" || run.status === "ready" || run.status === "paused")
664
+ counts.pending++;
665
+ if (run.status !== "passed" && run.status !== "failed")
666
+ counts.active++;
667
+ }
668
+ return counts;
669
+ }
670
+ export async function summarizeGoalCounts(cwd) {
671
+ return summarizeGoalCountsFromRuns(await loadGoalRuns(cwd));
672
+ }
673
+ async function writeGoalProgressJournalFromRun(cwd, run) {
674
+ const dir = join(projectDir(cwd), "journals");
675
+ await mkdir(dir, { recursive: true });
676
+ const path = join(dir, `${run.id}.md`);
677
+ const lines = [
678
+ `# ${run.title}`,
679
+ "",
680
+ `Status: ${run.status}`,
681
+ `Goal: ${run.goal}`,
682
+ "",
683
+ "## Success criteria",
684
+ ...(run.successCriteria.length
685
+ ? run.successCriteria.map((item) => `- ${item}`)
686
+ : ["- none recorded"]),
687
+ "",
688
+ "## Prerequisites",
689
+ ...(run.prerequisites.length
690
+ ? run.prerequisites.map((item) => `- [${item.status}] ${item.label}${item.evidence ? ` — ${item.evidence}` : ""}`)
691
+ : ["- none"]),
692
+ "",
693
+ "## Tasks",
694
+ ...(run.tasks.length
695
+ ? run.tasks.map((task) => `- [${task.status}] ${task.title} (attempts: ${task.attempts})${task.lastSummary ? ` — ${task.lastSummary}` : ""}`)
696
+ : ["- none"]),
697
+ "",
698
+ "## Verifier",
699
+ run.verifier?.lastResult
700
+ ? `- ${run.verifier.lastResult.status}: ${run.verifier.lastResult.summary}${run.verifier.lastResult.outputPath ? ` (${run.verifier.lastResult.outputPath})` : ""}`
701
+ : `- ${run.verifier?.command ?? "none"}`,
702
+ "",
703
+ "## Blockers",
704
+ ...(run.blockers.length ? run.blockers.map((item) => `- ${item}`) : ["- none"]),
705
+ "",
706
+ "## Recent evidence",
707
+ ...run.evidence
708
+ .slice(-10)
709
+ .map((item) => `- ${item.createdAt} [${item.kind}] ${item.label}${item.path ? ` (${item.path})` : ""}${item.content ? ` — ${item.content}` : ""}`),
710
+ "",
711
+ ];
712
+ await writeFile(path, lines.join("\n"), "utf-8");
713
+ return path;
714
+ }
715
+ export async function writeGoalProgressJournal(cwd, runId) {
716
+ const run = await getGoalRun(cwd, runId);
717
+ if (!run)
718
+ return null;
719
+ return writeGoalProgressJournalFromRun(run.projectPath, run);
720
+ }
721
+ //# sourceMappingURL=goal-store.js.map