@quantmind-br/pi-goal-x 0.0.0-stage → 0.35.1

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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -2
  3. package/assets/badge-dark.svg +18 -0
  4. package/assets/badge-light.svg +18 -0
  5. package/docs/advanced-usage.md +106 -0
  6. package/docs/agent-flow-design.md +306 -0
  7. package/docs/agentic-runtime-prd.md +773 -0
  8. package/docs/architecture.md +368 -0
  9. package/docs/controls.md +140 -0
  10. package/docs/pi-autoresearch-survey.md +50 -0
  11. package/docs/pi-goal-x-unified-dashboard-plan.md +1667 -0
  12. package/docs/prompt-caching.md +31 -0
  13. package/docs/unified-dashboard.md +286 -0
  14. package/extensions/auditor-selector.ts +74 -0
  15. package/extensions/goal-accounting.ts +107 -0
  16. package/extensions/goal-activity.ts +202 -0
  17. package/extensions/goal-auditor.ts +480 -0
  18. package/extensions/goal-commands.ts +866 -0
  19. package/extensions/goal-compaction.ts +197 -0
  20. package/extensions/goal-completion.ts +173 -0
  21. package/extensions/goal-context.ts +12 -0
  22. package/extensions/goal-contract.ts +58 -0
  23. package/extensions/goal-controls.ts +202 -0
  24. package/extensions/goal-core-tools.ts +549 -0
  25. package/extensions/goal-core.ts +78 -0
  26. package/extensions/goal-detail.ts +65 -0
  27. package/extensions/goal-dialog-controls.ts +31 -0
  28. package/extensions/goal-draft.ts +244 -0
  29. package/extensions/goal-drafting.ts +535 -0
  30. package/extensions/goal-events.ts +692 -0
  31. package/extensions/goal-format.ts +376 -0
  32. package/extensions/goal-interruption.ts +53 -0
  33. package/extensions/goal-ledger-index.ts +68 -0
  34. package/extensions/goal-ledger.ts +1007 -0
  35. package/extensions/goal-live-retention.ts +239 -0
  36. package/extensions/goal-oracle.ts +346 -0
  37. package/extensions/goal-policy.ts +370 -0
  38. package/extensions/goal-pool.ts +103 -0
  39. package/extensions/goal-prompt-cache.ts +80 -0
  40. package/extensions/goal-questionnaire.ts +1073 -0
  41. package/extensions/goal-record.ts +388 -0
  42. package/extensions/goal-recovery.ts +256 -0
  43. package/extensions/goal-runtime.ts +277 -0
  44. package/extensions/goal-scheduler-state.ts +100 -0
  45. package/extensions/goal-scheduler.ts +436 -0
  46. package/extensions/goal-service.ts +843 -0
  47. package/extensions/goal-session-health.ts +130 -0
  48. package/extensions/goal-session-safety.ts +65 -0
  49. package/extensions/goal-settings.ts +1370 -0
  50. package/extensions/goal-state.ts +1069 -0
  51. package/extensions/goal-status.ts +305 -0
  52. package/extensions/goal-task-confirmation.ts +174 -0
  53. package/extensions/goal-task-count.ts +59 -0
  54. package/extensions/goal-task-derive.ts +46 -0
  55. package/extensions/goal-task-index.ts +57 -0
  56. package/extensions/goal-task-tools.ts +595 -0
  57. package/extensions/goal-tool-inputs.ts +102 -0
  58. package/extensions/goal-tool-names.ts +73 -0
  59. package/extensions/goal-tools.ts +16 -0
  60. package/extensions/goal-widget.ts +455 -0
  61. package/extensions/goal.ts +42 -0
  62. package/extensions/network-error-backoff.ts +55 -0
  63. package/extensions/prompts/goal-prompts.ts +330 -0
  64. package/extensions/storage/atomic-rename.ts +52 -0
  65. package/extensions/storage/goal-files.ts +695 -0
  66. package/extensions/storage/goal-lock.ts +131 -0
  67. package/extensions/storage/goal-root.ts +95 -0
  68. package/extensions/widgets/auditor-dashboard-model.ts +174 -0
  69. package/extensions/widgets/dialog-scaffold.ts +26 -0
  70. package/extensions/widgets/goal-controls-dialog.ts +329 -0
  71. package/extensions/widgets/goal-dashboard-model.ts +530 -0
  72. package/extensions/widgets/goal-dashboard-renderer.ts +674 -0
  73. package/extensions/widgets/goal-escape-dialog.ts +148 -0
  74. package/extensions/widgets/goal-notifications.ts +9 -0
  75. package/extensions/widgets/goal-widget.ts +723 -0
  76. package/extensions/widgets/task-list-overlay.ts +409 -0
  77. package/extensions/widgets/text-cache.ts +30 -0
  78. package/package.json +117 -4
  79. package/scripts/recover-session-checkpoints.mjs +289 -0
@@ -0,0 +1,409 @@
1
+ import { matchesKey, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { truncateToWidth, wrapTextWithAnsi } from "./text-cache.ts";
3
+ import { borderedLine, dialogInnerWidth, horizontalRule } from "./dialog-scaffold.ts";
4
+ import type { Component, TUI } from "@earendil-works/pi-tui";
5
+ import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
6
+ import { displayObjectiveTitle, statusLabel } from "../goal-core.ts";
7
+ import { openGoalsFromPool } from "../goal-pool.ts";
8
+ import type { GoalRecord, GoalTask } from "../goal-record.ts";
9
+ import { countAllTasks, countTaskSubtree } from "../goal-task-count.ts";
10
+
11
+ // ── Structured line entries ──────────────────────────────────────────
12
+
13
+ type LineEntry =
14
+ | { type: "separator" }
15
+ | { type: "goal-header"; icon: string; title: string; status: string }
16
+ | { type: "task-summary"; text: string }
17
+ | { type: "task"; goalId: string; taskId: string; status: "pending" | "complete" | "skipped"; prefix: string; title: string }
18
+ | { type: "empty-message"; text: string };
19
+
20
+ // ── Public API ───────────────────────────────────────────────────────
21
+
22
+ /**
23
+ * Show a scrollable modal overlay displaying the current goal's task list.
24
+ * Press 'a' to toggle between the current goal and all open goals.
25
+ * Triggered by Ctrl+Shift+T. Dismisses on Escape.
26
+ */
27
+ export async function showTaskListOverlay(
28
+ ctx: ExtensionContext,
29
+ goalsById: Map<string, GoalRecord>,
30
+ focusedGoalId: string | null,
31
+ opts: { onToggleTask?: (goalId: string, taskId: string) => Promise<{ ok: boolean; message?: string }> } = {},
32
+ ): Promise<void> {
33
+ if (!ctx.hasUI) return;
34
+
35
+ await ctx.ui.custom<void>(
36
+ (tui: TUI, theme: Theme, _keybindings: unknown, done: () => void): Component => {
37
+ // ── Theme helpers ─────────────────────────────────────────────
38
+ const accent = (s: string) => theme.fg("accent", s);
39
+ const dim = (s: string) => theme.fg("dim", s);
40
+ const success = (s: string) => theme.fg("success", s);
41
+ const muted = (s: string) => theme.fg("muted", s);
42
+ const bold = (s: string) => theme.bold(s);
43
+
44
+ // ── State ─────────────────────────────────────────────────────
45
+ let showAllGoals = false;
46
+ let entries: LineEntry[] = [];
47
+ let totalOpenGoals = 0;
48
+ let totalTaskCount = 0;
49
+ // F3: interactive cursor over task rows; Enter toggles the task.
50
+ let cursorEntryIndex = -1;
51
+ let cursorMessage = "";
52
+
53
+ function rebuildEntries(): void {
54
+ entries = [];
55
+ totalOpenGoals = 0;
56
+ totalTaskCount = 0;
57
+
58
+ const openGoals = openGoalsFromPool(goalsById);
59
+ const targetGoals = showAllGoals
60
+ ? openGoals
61
+ : openGoals.filter((g) => g.id === focusedGoalId);
62
+
63
+ if (targetGoals.length === 0) {
64
+ entries.push({ type: "empty-message", text: "No open goals to display." });
65
+ return;
66
+ }
67
+
68
+ for (const [gIdx, goal] of targetGoals.entries()) {
69
+ if (goal.taskList) {
70
+ totalTaskCount += countAllTasks(goal.taskList.tasks);
71
+ }
72
+
73
+ if (gIdx > 0) {
74
+ entries.push({ type: "separator" });
75
+ }
76
+
77
+ // Goal header
78
+ const icon = goal.status === "paused" ? "⏸"
79
+ : goal.sisyphus ? "◆" : "●";
80
+ const title = displayObjectiveTitle(goal.objective);
81
+ const label = statusLabel(goal);
82
+ entries.push({ type: "goal-header", icon, title, status: label });
83
+
84
+ // Task summary & task list
85
+ if (goal.taskList && goal.taskList.tasks.length > 0) {
86
+ const { total, complete, skipped } = countTaskSubtree(goal.taskList.tasks);
87
+ const summary = `${complete}/${total} done${skipped > 0 ? ` (${skipped} skipped)` : ""}`;
88
+ entries.push({ type: "task-summary", text: summary });
89
+
90
+ const tasks = goal.taskList.tasks;
91
+ for (let i = 0; i < tasks.length; i++) {
92
+ const isLast = i === tasks.length - 1;
93
+ collectTaskEntries(tasks[i]!, 1, isLast, entries, goal.id);
94
+ }
95
+ } else {
96
+ entries.push({ type: "empty-message", text: "(no tasks)" });
97
+ }
98
+
99
+ totalOpenGoals++;
100
+ }
101
+
102
+ // F3: reset the cursor to the first task row after a rebuild.
103
+ cursorEntryIndex = entries.findIndex((e) => e.type === "task");
104
+ cursorMessage = "";
105
+ }
106
+
107
+ // Build initial entries
108
+ rebuildEntries();
109
+
110
+ // ── Render helpers ────────────────────────────────────────────
111
+
112
+ /** Render a single line entry into styled wrapped lines */
113
+ function renderEntry(entry: LineEntry, innerWidth: number): string[] {
114
+ switch (entry.type) {
115
+ case "separator":
116
+ return [dim("·")];
117
+
118
+ case "goal-header": {
119
+ const icon = entry.icon === "⏸" ? muted("⏸") : entry.icon === "◆" ? accent("◆") : accent("●");
120
+ const status = dim(entry.status);
121
+ const prefix = `${icon} ${bold("")}`; // placeholder, we'll build below
122
+ const rawPrefix = `${entry.icon} `; // raw width: icon + 2 spaces
123
+ const prefixWidth = visibleWidth(rawPrefix);
124
+ const available = innerWidth - prefixWidth;
125
+ const wrappedTitle = wrapTextWithAnsi(bold(entry.title), Math.max(1, available));
126
+ const lines: string[] = [];
127
+ wrappedTitle.forEach((segment, i) => {
128
+ if (i === 0) {
129
+ lines.push(`${icon} ${segment} ${status}`);
130
+ } else {
131
+ lines.push(` ${" ".repeat(rawPrefix.length - 3)}${segment}`);
132
+ }
133
+ });
134
+ // If title alone fits, status needs to be on same line
135
+ // But we already handle it above for i=0. The status might overflow.
136
+ // If status doesn't fit on first line after title, put it on its own line
137
+ const firstLine = lines[0]!;
138
+ const allOnFirst = `${icon} ${entry.title} ${entry.status}`;
139
+ if (visibleWidth(firstLine) > innerWidth) {
140
+ // Status overflowed — put status on a separate dim line
141
+ lines[0] = `${icon} ${wrappedTitle[0]}`;
142
+ if (wrappedTitle.length === 1) {
143
+ lines.push(` ${dim(entry.status)}`);
144
+ } else {
145
+ // insert status after last wrapped title segment
146
+ const lastLine = wrappedTitle[wrappedTitle.length - 1];
147
+ lines[lines.length - 1] = ` ${" ".repeat(rawPrefix.length - 3)}${lastLine}`;
148
+ lines.push(` ${dim(entry.status)}`);
149
+ }
150
+ }
151
+ // Re-truncate any over-long lines just in case
152
+ return lines.map((l) => visibleWidth(l) > innerWidth ? truncateToWidth(l, innerWidth, "…") : l);
153
+ }
154
+
155
+ case "task-summary": {
156
+ const content = dim(entry.text);
157
+ return [visibleWidth(content) > innerWidth ? truncateToWidth(content, innerWidth, "…") : content];
158
+ }
159
+
160
+ case "task": {
161
+ const prefixWidth = visibleWidth(entry.prefix);
162
+ const available = innerWidth - prefixWidth;
163
+ const wrappedTitle = wrapTextWithAnsi(entry.title, Math.max(1, available));
164
+ const lines: string[] = [];
165
+ wrappedTitle.forEach((segment, i) => {
166
+ if (i === 0) {
167
+ lines.push(`${dim(entry.prefix)} ${segment}`);
168
+ } else {
169
+ lines.push(`${" ".repeat(prefixWidth + 1)}${segment}`);
170
+ }
171
+ });
172
+ return lines.map((l) => visibleWidth(l) > innerWidth ? truncateToWidth(l, innerWidth, "…") : l);
173
+ }
174
+
175
+ case "empty-message": {
176
+ const content = dim(entry.text);
177
+ return [visibleWidth(content) > innerWidth ? truncateToWidth(content, innerWidth, "…") : content];
178
+ }
179
+
180
+ default:
181
+ return [];
182
+ }
183
+ }
184
+
185
+ // ── Scroll state ──────────────────────────────────────────────
186
+ let scrollOffset = 0;
187
+ let lastRenderWidth = 80;
188
+
189
+ // The "logical line count" is the number of entries (before wrapping).
190
+ // We store rendered line count too since wrapped entries expand.
191
+ let renderedLineCount = 0;
192
+
193
+ function computeVisibleHeight(innerWidth: number): number {
194
+ return Math.max(8, Math.floor(innerWidth / 2.8));
195
+ }
196
+
197
+ const wasHardwareCursorShown = tui.getShowHardwareCursor();
198
+ tui.setShowHardwareCursor(false);
199
+
200
+ // ── Component ─────────────────────────────────────────────────
201
+ const component: Component & { dispose?(): void } = {
202
+ dispose() {
203
+ tui.setShowHardwareCursor(wasHardwareCursorShown);
204
+ },
205
+
206
+ invalidate(): void {},
207
+
208
+ render(width: number): string[] {
209
+ lastRenderWidth = width;
210
+
211
+ const termWidth = Math.min(width, 100);
212
+ const innerWidth = dialogInnerWidth(termWidth, 90);
213
+ const p = " ";
214
+
215
+ const line = (content: string): string => borderedLine(accent, innerWidth, content);
216
+
217
+ // ── Render all entries into flat text lines ────────────
218
+ const renderedLines: string[] = [];
219
+ for (const entry of entries) {
220
+ const wrapped = renderEntry(entry, innerWidth);
221
+ renderedLines.push(...wrapped);
222
+ }
223
+ renderedLineCount = renderedLines.length;
224
+
225
+ const horiz = horizontalRule(innerWidth);
226
+ const out: string[] = [];
227
+
228
+ out.push(accent(`┌${horiz}┐`));
229
+
230
+ // Header
231
+ const viewLabel = showAllGoals
232
+ ? `${totalOpenGoals} ${totalOpenGoals === 1 ? "goal" : "goals"}`
233
+ : "current goal";
234
+ const taskWord = totalTaskCount === 1 ? "task" : "tasks";
235
+ const h = bold(` Tasks (${viewLabel}, ${totalTaskCount} ${taskWord})`);
236
+ out.push(line(p + h));
237
+ out.push(accent(`├${horiz}┤`));
238
+
239
+ // Content with scrolling
240
+ const visibleHeight = computeVisibleHeight(innerWidth);
241
+ const maxOffset = Math.max(0, renderedLineCount - visibleHeight);
242
+ if (scrollOffset > maxOffset) scrollOffset = maxOffset;
243
+
244
+ const canScrollUp = scrollOffset > 0;
245
+ const canScrollDown = scrollOffset < maxOffset;
246
+
247
+ if (canScrollUp) {
248
+ out.push(line(p + dim(`▴ ${scrollOffset}/${renderedLineCount} lines`)));
249
+ }
250
+
251
+ const end = Math.min(scrollOffset + visibleHeight, renderedLineCount);
252
+ for (let i = scrollOffset; i < end; i++) {
253
+ const raw = renderedLines[i]!;
254
+ // Each rendered line from renderEntry already wraps/truncates,
255
+ // but double-check for safety
256
+ const safe = visibleWidth(raw) > innerWidth
257
+ ? truncateToWidth(raw, innerWidth, "…")
258
+ : raw;
259
+ out.push(line(p + safe));
260
+ }
261
+
262
+ if (canScrollDown) {
263
+ out.push(line(p + dim(`▾ ${renderedLineCount - end} more lines`)));
264
+ }
265
+
266
+ if (renderedLineCount === 0) {
267
+ out.push(line(p + dim("No tasks to display.")));
268
+ }
269
+
270
+ out.push(accent(`├${horiz}┤`));
271
+ const toggleHint = showAllGoals ? "show current" : "show all";
272
+ const interactive = opts.onToggleTask ? " · Enter toggle task" : "";
273
+ const footer = dim(`↑↓/jk scroll · 'a' to ${toggleHint} · Esc close${interactive}`);
274
+ out.push(line(p + footer));
275
+ if (cursorMessage) {
276
+ out.push(line(p + theme.fg("warning", cursorMessage)));
277
+ }
278
+ out.push(accent(`└${horiz}┘`));
279
+
280
+ return out;
281
+ },
282
+
283
+ handleInput(data: string): void {
284
+ const tw = Math.min(lastRenderWidth, 100);
285
+ const innerW = Math.min(tw, 90) - 2;
286
+ const visibleH = computeVisibleHeight(innerW);
287
+ const maxO = Math.max(0, renderedLineCount - visibleH);
288
+
289
+ if (matchesKey(data, "up") || matchesKey(data, "k")) {
290
+ scrollOffset = Math.max(0, scrollOffset - 1);
291
+ if (opts.onToggleTask) {
292
+ let prev = -1;
293
+ for (let i = cursorEntryIndex - 1; i >= 0; i--) {
294
+ if (entries[i]?.type === "task") { prev = i; break; }
295
+ }
296
+ if (prev >= 0) cursorEntryIndex = prev;
297
+ }
298
+ tui.requestRender();
299
+ return;
300
+ }
301
+ if (matchesKey(data, "down") || matchesKey(data, "j")) {
302
+ scrollOffset = Math.min(maxO, scrollOffset + 1);
303
+ if (opts.onToggleTask) {
304
+ for (let i = cursorEntryIndex + 1; i < entries.length; i++) {
305
+ if (entries[i]?.type === "task") { cursorEntryIndex = i; break; }
306
+ }
307
+ }
308
+ tui.requestRender();
309
+ return;
310
+ }
311
+ if (matchesKey(data, "pageUp")) {
312
+ scrollOffset = Math.max(0, scrollOffset - visibleH);
313
+ tui.requestRender();
314
+ return;
315
+ }
316
+ if (matchesKey(data, "pageDown")) {
317
+ scrollOffset = Math.min(maxO, scrollOffset + visibleH);
318
+ tui.requestRender();
319
+ return;
320
+ }
321
+ if (matchesKey(data, "home")) {
322
+ scrollOffset = 0;
323
+ tui.requestRender();
324
+ return;
325
+ }
326
+ if (matchesKey(data, "end")) {
327
+ scrollOffset = maxO;
328
+ tui.requestRender();
329
+ return;
330
+ }
331
+ // Toggle view
332
+ if (matchesKey(data, "a")) {
333
+ showAllGoals = !showAllGoals;
334
+ rebuildEntries();
335
+ scrollOffset = 0;
336
+ tui.requestRender();
337
+ return;
338
+ }
339
+ if (matchesKey(data, "enter") && opts.onToggleTask) {
340
+ const cursorEntry = entries[cursorEntryIndex];
341
+ if (cursorEntry && cursorEntry.type === "task") {
342
+ void (async () => {
343
+ const result = await opts.onToggleTask!(cursorEntry.goalId, cursorEntry.taskId);
344
+ if (result.ok) {
345
+ rebuildEntries();
346
+ tui.requestRender();
347
+ } else {
348
+ cursorMessage = result.message ?? "Toggle failed.";
349
+ tui.requestRender();
350
+ setTimeout(() => {
351
+ cursorMessage = "";
352
+ tui.requestRender();
353
+ }, 2500);
354
+ }
355
+ })();
356
+ return;
357
+ }
358
+ done();
359
+ return;
360
+ }
361
+ if (matchesKey(data, "escape") || matchesKey(data, "enter")) {
362
+ done();
363
+ return;
364
+ }
365
+ },
366
+ };
367
+
368
+ return component;
369
+ },
370
+ {
371
+ overlay: true,
372
+ overlayOptions: {
373
+ anchor: "center",
374
+ width: "80%",
375
+ minWidth: 60,
376
+ maxHeight: "80%",
377
+ },
378
+ },
379
+ );
380
+ }
381
+
382
+ // ── Task counting ─────────────────────────────────────────────────────
383
+
384
+ // ── Tree entry collection ─────────────────────────────────────────────
385
+
386
+ const STATUS_ICONS = { complete: "✓", skipped: "—", pending: "◌" } as const;
387
+ const BRANCH = "├─";
388
+ const BRANCH_LAST = "└─";
389
+
390
+ function collectTaskEntries(
391
+ task: GoalTask,
392
+ depth: number,
393
+ isLast: boolean,
394
+ entries: LineEntry[],
395
+ goalId: string,
396
+ ): void {
397
+ const branch = isLast ? BRANCH_LAST : BRANCH;
398
+ const statusIcon = STATUS_ICONS[task.status] ?? STATUS_ICONS.pending;
399
+
400
+ const indent = " " + " ".repeat(depth - 1);
401
+ const prefix = `${indent} ${branch} ${statusIcon}`;
402
+ entries.push({ type: "task", goalId, taskId: task.id, status: task.status, prefix, title: task.title });
403
+
404
+ if (task.subtasks && task.subtasks.length > 0) {
405
+ for (let i = 0; i < task.subtasks.length; i++) {
406
+ collectTaskEntries(task.subtasks[i]!, depth + 1, i === task.subtasks.length - 1, entries, goalId);
407
+ }
408
+ }
409
+ }
@@ -0,0 +1,30 @@
1
+ import { truncateToWidth as sdkTruncate, wrapTextWithAnsi as sdkWrap } from "@earendil-works/pi-tui";
2
+
3
+ // Cache pure SDK text transforms, keyed by complete ANSI content and layout arguments.
4
+ // Theme changes naturally miss. visibleWidth already has a cache in the supported SDK.
5
+ const cache = new Map<string, {value: string | string[]; chars: number}>();
6
+ let retainedChars = 0;
7
+ function memo<T extends string | string[]>(key: string, build: () => T): T {
8
+ const cached = cache.get(key);
9
+ if (cached) return cached.value as T;
10
+ const value = build();
11
+ const chars = key.length + (typeof value === "string" ? value.length : value.reduce((n, line) => n + line.length, 0));
12
+ if (chars <= 500_000) {
13
+ while (cache.size >= 1024 || retainedChars + chars > 500_000) {
14
+ const oldest = cache.keys().next().value!;
15
+ retainedChars -= cache.get(oldest)!.chars;
16
+ cache.delete(oldest);
17
+ }
18
+ cache.set(key, {value, chars}); retainedChars += chars;
19
+ }
20
+ return value;
21
+ }
22
+
23
+ export function truncateToWidth(text: string, width: number, ellipsis = "...", pad = false): string {
24
+ return memo(`t:${width}:${pad}:${ellipsis.length}:${ellipsis}${text}`, () => sdkTruncate(text, width, ellipsis, pad));
25
+ }
26
+
27
+ export function wrapTextWithAnsi(text: string, width: number): string[] {
28
+ // Callers may edit the returned rows; cached rows stay privately owned.
29
+ return memo(`w:${width}:${text}`, () => sdkWrap(text, width)).slice();
30
+ }
package/package.json CHANGED
@@ -1,6 +1,119 @@
1
1
  {
2
2
  "name": "@quantmind-br/pi-goal-x",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.35.1",
4
+ "description": "Fork of pi-goal-x with a fixed, cache-stable goal tool profile. Adds /goal to pi: conversational goal planning, flexible or ordered goals, persistent progress, and an independent completion auditor.",
5
+ "license": "MIT",
6
+ "author": "pi-goal-x contributors",
7
+ "type": "module",
8
+ "bin": {
9
+ "pi-goal-x-recover": "scripts/recover-session-checkpoints.mjs"
10
+ },
11
+ "keywords": [
12
+ "pi-package",
13
+ "pi",
14
+ "pi-extension",
15
+ "goal",
16
+ "sisyphus",
17
+ "autoresearch",
18
+ "agent-loop",
19
+ "long-running"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/quantforge-br/pi-goal-x.git"
24
+ },
25
+ "homepage": "https://github.com/quantforge-br/pi-goal-x#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/quantforge-br/pi-goal-x/issues"
28
+ },
29
+ "files": [
30
+ "extensions",
31
+ "scripts/recover-session-checkpoints.mjs",
32
+ "README.md",
33
+ "LICENSE",
34
+ "docs/agent-flow-design.md",
35
+ "docs/agentic-runtime-prd.md",
36
+ "docs/architecture.md",
37
+ "docs/prompt-caching.md",
38
+ "docs/advanced-usage.md",
39
+ "docs/pi-autoresearch-survey.md",
40
+ "docs/pi-goal-x-unified-dashboard-plan.md",
41
+ "docs/unified-dashboard.md",
42
+ "assets/badge-light.svg",
43
+ "assets/badge-dark.svg",
44
+ "docs/controls.md"
45
+ ],
46
+ "pi": {
47
+ "extensions": [
48
+ "extensions/goal.ts"
49
+ ],
50
+ "image": "https://github.com/quantforge-br/pi-goal-x/raw/main/pi-goal-x.png"
51
+ },
52
+ "scripts": {
53
+ "check": "tsc --noEmit",
54
+ "test": "node scripts/run-unit-tests.mjs",
55
+ "test:serial": "node --experimental-strip-types --test --test-concurrency=1 tests/*.test.ts",
56
+ "test:unit": "node scripts/run-unit-tests.mjs",
57
+ "test:integration": "node scripts/run-unit-tests.mjs integration",
58
+ "test:all": "node scripts/run-unit-tests.mjs all",
59
+ "test:selfcheck": "node scripts/run-unit-tests.mjs --selfcheck",
60
+ "bench": "node --import ./experiments/bench/bench-adapter-hooks.mjs --experimental-strip-types ./experiments/bench/run-bench.mjs",
61
+ "bench:gate": "node --experimental-strip-types ./experiments/bench/b6-gate.mjs",
62
+ "bench:naf": "node --import ./experiments/bench/bench-adapter-hooks.mjs --experimental-strip-types ./experiments/bench/run-bench.mjs",
63
+ "bench:gate:naf": "node --experimental-strip-types ./experiments/bench/b6-gate.mjs naf",
64
+ "test:e2e": "node scripts/run-unit-tests.mjs e2e",
65
+ "test:checkpoint-recovery": "node --experimental-strip-types --test tests/session-checkpoint-growth.test.ts tests/session-checkpoint-recovery.test.ts tests/goal-session-health.test.ts",
66
+ "test:settings-race": "node --experimental-strip-types --test tests/goal-settings-race.test.ts",
67
+ "lint": "eslint .",
68
+ "context:measure": "node experiments/context/run-measure.mjs",
69
+ "context:gate": "node experiments/context/run-gate.mjs",
70
+ "context:provider-check": "node --experimental-strip-types experiments/context/provider-crosscheck.mjs",
71
+ "bench:runtime-token": "node --import ./experiments/bench/bench-adapter-hooks.mjs --expose-gc --experimental-strip-types experiments/bench/runtime-token.mjs",
72
+ "bench:gate:runtime-token": "node experiments/bench/runtime-token-gate.mjs",
73
+ "test:oracle": "node --experimental-strip-types --test tests/goal-oracle.test.ts",
74
+ "bench:comprehensive": "node --import ./experiments/bench/bench-adapter-hooks.mjs --expose-gc --experimental-strip-types experiments/bench/comprehensive.mjs",
75
+ "bench:gate:comprehensive": "node experiments/bench/comprehensive-gate.mjs",
76
+ "test:controls-cli": "node experiments/controls/cli-walkthrough.mjs",
77
+ "context:cache-report": "node --experimental-strip-types experiments/context/cache-report.mjs",
78
+ "context:cache-gate": "node --experimental-strip-types experiments/context/cache-gate.mjs"
79
+ },
80
+ "peerDependencies": {
81
+ "@earendil-works/pi-ai": ">=1.4.0 <2.0.0",
82
+ "@earendil-works/pi-coding-agent": ">=1.4.0 <2.0.0",
83
+ "@earendil-works/pi-tui": ">=1.4.0 <2.0.0",
84
+ "typebox": "^1.3.11"
85
+ },
86
+ "devDependencies": {
87
+ "@earendil-works/pi-ai": "1.4.0",
88
+ "@earendil-works/pi-coding-agent": "1.4.0",
89
+ "@earendil-works/pi-tui": "1.4.0",
90
+ "@eslint/js": "^9.39.5",
91
+ "@xterm/headless": "^6.0.0",
92
+ "eslint": "^9.39.5",
93
+ "globals": "^15.15.0",
94
+ "typebox": "^1.3.11",
95
+ "typescript": "^5.9.3",
96
+ "typescript-eslint": "^8.66.0"
97
+ },
98
+ "publishConfig": {
99
+ "access": "public"
100
+ },
101
+ "overrides": {
102
+ "@earendil-works/pi-coding-agent": {
103
+ "undici": "8.12.2",
104
+ "minimatch": {
105
+ ".": "10.2.6",
106
+ "brace-expansion": "5.0.12"
107
+ }
108
+ },
109
+ "@earendil-works/pi-ai": {
110
+ "undici": "8.12.2"
111
+ },
112
+ "minimatch": {
113
+ "brace-expansion": "5.0.12"
114
+ }
115
+ },
116
+ "engines": {
117
+ "node": ">=22.19.0"
118
+ }
119
+ }