@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,148 @@
1
+ import { goalDialogControls } from "../goal-dialog-controls.ts";
2
+ import { truncateToWidth } from "./text-cache.ts";
3
+ import type { Component, TUI } from "@earendil-works/pi-tui";
4
+ import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
5
+ import { borderedLine, dialogInnerWidth, horizontalRule } from "./dialog-scaffold.ts";
6
+
7
+ /**
8
+ * Result of the Escape dialog during audit.
9
+ */
10
+ export type EscapeDialogResult = "complete_without_audit" | "continue_working";
11
+
12
+ /**
13
+ * Show a TUI confirmation dialog when the user presses Escape during a completion audit.
14
+ *
15
+ * Presents two choices:
16
+ * - Mark complete without audit (skips auditor, marks goal complete immediately)
17
+ * - Continue working (returns to agent, goal stays active)
18
+ *
19
+ * Returns the user's choice. Escape or Enter on a focused option submits the selection.
20
+ */
21
+ export async function showEscapeDialog(
22
+ ctx: ExtensionContext,
23
+ goalObjective: string,
24
+ ): Promise<EscapeDialogResult> {
25
+ if (!ctx.hasUI) {
26
+ // Fallback for headless/RPC mode — return "continue" as the safe default
27
+ return "continue_working";
28
+ }
29
+
30
+ const controls = goalDialogControls(typeof ctx.cwd === "string" ? ctx.cwd : process.cwd());
31
+ return await ctx.ui.custom<EscapeDialogResult>(
32
+ (tui: TUI, theme: Theme, _keybindings: unknown, done: (result: EscapeDialogResult) => void): Component => {
33
+ const wasHardwareCursorShown = tui.getShowHardwareCursor();
34
+ tui.setShowHardwareCursor(false);
35
+ // Pause pi's working spinner for the dialog duration: its ~80ms re-renders
36
+ // write output that snaps a scrolled-up user back to the terminal bottom.
37
+ ctx.ui.setWorkingVisible(false);
38
+
39
+ let selectedIndex = 1; // Default: "Continue working" (index 1)
40
+ let cancelled = false;
41
+
42
+ const OPTIONS: Array<{ label: string; value: EscapeDialogResult; description: string }> = [
43
+ {
44
+ label: "Mark complete without audit",
45
+ value: "complete_without_audit",
46
+ description: "Bypass the auditor and mark the goal complete now.",
47
+ },
48
+ {
49
+ label: "Continue working",
50
+ value: "continue_working",
51
+ description: "Resume work on the goal. It stays active; the audit will not run this turn.",
52
+ },
53
+ ];
54
+
55
+ const accent = (s: string) => theme.fg("accent", s);
56
+ const dim = (s: string) => theme.fg("dim", s);
57
+ const warning = (s: string) => theme.fg("warning", s);
58
+
59
+ // ── Component ────────────────────────────────────────────────────
60
+ const component: Component & { dispose?(): void } = {
61
+ dispose() {
62
+ tui.setShowHardwareCursor(wasHardwareCursorShown);
63
+ ctx.ui.setWorkingVisible(true);
64
+ },
65
+
66
+ invalidate(): void {
67
+ // No cached state to invalidate
68
+ },
69
+
70
+ render(width: number): string[] {
71
+ const termWidth = Math.min(width, 80);
72
+ const innerWidth = dialogInnerWidth(termWidth);
73
+
74
+ const line = (leftContent: string): string => borderedLine(accent, innerWidth, leftContent);
75
+
76
+ const horizLine = horizontalRule(innerWidth);
77
+ const lines: string[] = [];
78
+ const p = " "; // left padding inside the border
79
+
80
+ // ── Header ────────────────────────────────────────────────
81
+ lines.push(accent(`┌${horizLine}┐`));
82
+ const headerContent = p + theme.bold("Audit interrupted") + dim(" (continue = default)");
83
+ lines.push(line(truncateToWidth(headerContent, innerWidth, "…")));
84
+ const truncatedObjective = truncateToWidth(goalObjective, innerWidth - 14, "…");
85
+ lines.push(line(p + dim("Goal: ") + dim(truncatedObjective)));
86
+ lines.push(accent(`├${horizLine}┤`));
87
+
88
+ // ── Options ─────────────────────────────────────────────
89
+ OPTIONS.forEach((opt, i) => {
90
+ const isSelected = i === selectedIndex && !cancelled;
91
+ const marker = isSelected ? "▸ " : " ";
92
+ const label = isSelected ? warning(opt.label) : opt.label;
93
+ const truncLabel = truncateToWidth(label, innerWidth - 6, "…");
94
+ lines.push(line(p + marker + truncLabel));
95
+ if (isSelected && opt.description) {
96
+ const desc = dim(opt.description);
97
+ const truncDesc = truncateToWidth(desc, innerWidth - 10, "…");
98
+ lines.push(line(p + " ".repeat(4) + truncDesc));
99
+ }
100
+ });
101
+
102
+ // ── Footer ───────────────────────────────────────────────
103
+ lines.push(accent(`├${horizLine}┤`));
104
+ const footerText = dim(`${controls.hint("confirm")} to select · ${controls.navigationHint()} to navigate · ${controls.hint("cancel")} = continue working`);
105
+ const truncFooter = truncateToWidth(footerText, innerWidth - 2, "…");
106
+ lines.push(line(p + truncFooter));
107
+ lines.push(accent(`└${horizLine}┘`));
108
+
109
+ return lines;
110
+ },
111
+
112
+ handleInput(data: string): void {
113
+ if (controls.matches(data, "up")) {
114
+ selectedIndex = (selectedIndex - 1 + OPTIONS.length) % OPTIONS.length;
115
+ tui.requestRender();
116
+ return;
117
+ }
118
+ if (controls.matches(data, "down")) {
119
+ selectedIndex = (selectedIndex + 1) % OPTIONS.length;
120
+ tui.requestRender();
121
+ return;
122
+ }
123
+ if (controls.matches(data, "confirm")) {
124
+ cancelled = false;
125
+ done(OPTIONS[selectedIndex]!.value);
126
+ return;
127
+ }
128
+ if (controls.matches(data, "cancel")) {
129
+ cancelled = true;
130
+ done("continue_working");
131
+ return;
132
+ }
133
+ },
134
+ };
135
+
136
+ return component;
137
+ },
138
+ {
139
+ overlay: true,
140
+ overlayOptions: {
141
+ anchor: "center",
142
+ width: "70%",
143
+ minWidth: 50,
144
+ maxHeight: "50%",
145
+ },
146
+ },
147
+ );
148
+ }
@@ -0,0 +1,9 @@
1
+ import { displayObjectiveTitle, truncateText } from "../goal-core.ts";
2
+
3
+ export function buildGoalRunningNotification(args: { objective: string; sisyphus: boolean; autoContinue: boolean }): string {
4
+ const icon = args.sisyphus ? "◆" : "●";
5
+ const mode = args.sisyphus ? "Sisyphus" : "Goal";
6
+ const title = truncateText(displayObjectiveTitle(args.objective), 92);
7
+ const drive = args.autoContinue ? "auto-continue on" : "manual mode";
8
+ return [`${icon} ${mode} running`, `├─ ⟡ ${title}`, `└─ ${drive}`].join("\n");
9
+ }