shariq-pi-extensions 0.1.0

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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/THIRD_PARTY_NOTICES.md +11 -0
  4. package/docs/ARCHITECTURE.md +54 -0
  5. package/docs/DEVELOPMENT.md +88 -0
  6. package/docs/EXTENSIONS.md +89 -0
  7. package/extensions/antigravity-provider/LICENSE +21 -0
  8. package/extensions/antigravity-provider/README.md +23 -0
  9. package/extensions/antigravity-provider/antigravity/cloud-code-assist.ts +519 -0
  10. package/extensions/antigravity-provider/antigravity/index.ts +67 -0
  11. package/extensions/antigravity-provider/antigravity/models.ts +184 -0
  12. package/extensions/antigravity-provider/antigravity/oauth.ts +499 -0
  13. package/extensions/antigravity-provider/index.ts +3 -0
  14. package/extensions/ask-user/README.md +21 -0
  15. package/extensions/ask-user/index.ts +417 -0
  16. package/extensions/background-terminals/README.md +43 -0
  17. package/extensions/background-terminals/index.ts +400 -0
  18. package/extensions/background-terminals/src/manager.ts +480 -0
  19. package/extensions/background-terminals/src/output-buffer.ts +110 -0
  20. package/extensions/background-terminals/src/presentation.ts +74 -0
  21. package/extensions/background-terminals/src/types.ts +47 -0
  22. package/extensions/background-terminals/src/ui.ts +478 -0
  23. package/extensions/context-usage/LICENSE +21 -0
  24. package/extensions/context-usage/README.md +20 -0
  25. package/extensions/context-usage/index.ts +2006 -0
  26. package/extensions/context-usage/lib/ansi.ts +60 -0
  27. package/extensions/context-usage/lib/boundary.ts +27 -0
  28. package/extensions/context-usage/lib/chat.ts +190 -0
  29. package/extensions/context-usage/lib/config.ts +30 -0
  30. package/extensions/context-usage/lib/fmt.ts +59 -0
  31. package/extensions/context-usage/lib/style.ts +260 -0
  32. package/extensions/copy-all/README.md +15 -0
  33. package/extensions/copy-all/index.ts +72 -0
  34. package/extensions/factory-provider/API_KEYS.md +17 -0
  35. package/extensions/factory-provider/AUTH.md +15 -0
  36. package/extensions/factory-provider/README.md +90 -0
  37. package/extensions/factory-provider/REFRESH_MODELS.md +185 -0
  38. package/extensions/factory-provider/UPDATING.md +97 -0
  39. package/extensions/factory-provider/factory/api-keys.ts +407 -0
  40. package/extensions/factory-provider/factory/auth.ts +132 -0
  41. package/extensions/factory-provider/factory/constants.ts +15 -0
  42. package/extensions/factory-provider/factory/dashboard.ts +363 -0
  43. package/extensions/factory-provider/factory/droid.ts +84 -0
  44. package/extensions/factory-provider/factory/limits.ts +342 -0
  45. package/extensions/factory-provider/factory/models.ts +283 -0
  46. package/extensions/factory-provider/factory/responses.ts +331 -0
  47. package/extensions/factory-provider/factory/warm-theme.ts +53 -0
  48. package/extensions/factory-provider/factory/websocket.ts +130 -0
  49. package/extensions/factory-provider/index.ts +204 -0
  50. package/extensions/factory-provider/scripts/audit-reasoning-efforts.py +84 -0
  51. package/extensions/firecrawl-web/README.md +31 -0
  52. package/extensions/firecrawl-web/auth.ts +92 -0
  53. package/extensions/firecrawl-web/client.ts +238 -0
  54. package/extensions/firecrawl-web/index.ts +96 -0
  55. package/extensions/firecrawl-web/output.ts +125 -0
  56. package/extensions/git-info/README.md +25 -0
  57. package/extensions/git-info/index.ts +162 -0
  58. package/extensions/git-info/src/git.ts +206 -0
  59. package/extensions/git-info/src/ui.ts +291 -0
  60. package/extensions/goal/README.md +44 -0
  61. package/extensions/goal/constants.ts +4 -0
  62. package/extensions/goal/extension.ts +1064 -0
  63. package/extensions/goal/index.ts +2 -0
  64. package/extensions/goal/types.ts +56 -0
  65. package/extensions/goal/ui.ts +334 -0
  66. package/extensions/orchestration/README.md +58 -0
  67. package/extensions/orchestration/dashboard.ts +192 -0
  68. package/extensions/orchestration/engine.ts +706 -0
  69. package/extensions/orchestration/index.ts +243 -0
  70. package/extensions/orchestration/prompts.ts +124 -0
  71. package/extensions/orchestration/protocol.ts +116 -0
  72. package/extensions/orchestration/settings-ui.ts +41 -0
  73. package/extensions/orchestration/settings.ts +59 -0
  74. package/extensions/orchestration/storage.ts +49 -0
  75. package/extensions/orchestration/types.ts +105 -0
  76. package/extensions/performance-status/README.md +5 -0
  77. package/extensions/performance-status/index.ts +171 -0
  78. package/extensions/pi-memory/README.md +53 -0
  79. package/extensions/pi-memory/index.ts +326 -0
  80. package/extensions/pi-memory/src/config.ts +75 -0
  81. package/extensions/pi-memory/src/database.ts +617 -0
  82. package/extensions/pi-memory/src/extraction.ts +178 -0
  83. package/extensions/pi-memory/src/project.ts +80 -0
  84. package/extensions/pi-memory/src/projection.ts +59 -0
  85. package/extensions/pi-memory/src/redaction.ts +60 -0
  86. package/extensions/pi-memory/src/retrieval.ts +30 -0
  87. package/extensions/pi-memory/src/service.ts +194 -0
  88. package/extensions/pi-memory/src/session.ts +146 -0
  89. package/extensions/pi-memory/src/types.ts +82 -0
  90. package/extensions/shared/README.md +18 -0
  91. package/extensions/shared/activity-status.ts +31 -0
  92. package/extensions/shared/child-session.ts +150 -0
  93. package/extensions/shared/context-utilization.ts +47 -0
  94. package/extensions/shared/dashboard-state.ts +99 -0
  95. package/extensions/shared/tool-call-timeout.ts +104 -0
  96. package/extensions/shared/tui-dashboard.ts +139 -0
  97. package/extensions/shell-shortcuts/README.md +17 -0
  98. package/extensions/shell-shortcuts/features/exit-alias.ts +10 -0
  99. package/extensions/shell-shortcuts/index.ts +6 -0
  100. package/extensions/subagents/README.md +89 -0
  101. package/extensions/subagents/index.ts +1386 -0
  102. package/extensions/subagents/src/backend.ts +69 -0
  103. package/extensions/subagents/src/backends/pi.ts +781 -0
  104. package/extensions/subagents/src/catalog.ts +85 -0
  105. package/extensions/subagents/src/config.ts +241 -0
  106. package/extensions/subagents/src/context.ts +57 -0
  107. package/extensions/subagents/src/coordinator.ts +64 -0
  108. package/extensions/subagents/src/domain.ts +294 -0
  109. package/extensions/subagents/src/format.ts +74 -0
  110. package/extensions/subagents/src/manager.ts +813 -0
  111. package/extensions/subagents/src/prompt.ts +94 -0
  112. package/extensions/subagents/src/result-delivery.ts +20 -0
  113. package/extensions/subagents/src/runtime.ts +48 -0
  114. package/extensions/subagents/src/ui/takeover.ts +695 -0
  115. package/extensions/subagents/src/ui/transcript.ts +182 -0
  116. package/extensions/subagents/src/worktree.ts +175 -0
  117. package/extensions/web-fetch/README.md +13 -0
  118. package/extensions/web-fetch/index.ts +441 -0
  119. package/package.json +117 -0
  120. package/skills/background-terminals/SKILL.md +42 -0
  121. package/skills/orchestration/SKILL.md +33 -0
  122. package/skills/subagents/SKILL.md +89 -0
  123. package/themes/ember-warm-dark.json +85 -0
@@ -0,0 +1,291 @@
1
+ import type {
2
+ ExtensionContext,
3
+ Theme,
4
+ } from "@earendil-works/pi-coding-agent";
5
+ import {
6
+ Key,
7
+ matchesKey,
8
+ truncateToWidth,
9
+ } from "@earendil-works/pi-tui";
10
+ import {
11
+ frameBottom,
12
+ frameTop,
13
+ joinSides,
14
+ oneLine,
15
+ padLine,
16
+ stateLabel,
17
+ viewportSlice,
18
+ } from "../../shared/tui-dashboard.ts";
19
+ import type { ChangedFile } from "./git.ts";
20
+
21
+ function statusState(status: string): "success" | "warning" | "error" {
22
+ if (status === "??" || status.includes("A")) return "success";
23
+ if (status.includes("D")) return "error";
24
+ return "warning";
25
+ }
26
+
27
+ function statText(file: ChangedFile): string {
28
+ if (file.additions === null || file.deletions === null) return "binary";
29
+ return `+${file.additions} -${file.deletions}`;
30
+ }
31
+
32
+ function styleDiff(theme: Theme, line: string): string {
33
+ if (line.startsWith("diff --git") || line.startsWith("index ")) {
34
+ return theme.fg("accent", theme.bold(line));
35
+ }
36
+ if (line.startsWith("@@")) return theme.fg("mdHeading", line);
37
+ if (line.startsWith("---") || line.startsWith("+++")) return theme.fg("muted", line);
38
+ if (line.startsWith("+")) return theme.fg("success", line);
39
+ if (line.startsWith("-")) return theme.fg("error", line);
40
+ if (line.startsWith("…")) return theme.fg("warning", line);
41
+ return theme.fg("text", line);
42
+ }
43
+
44
+ function combine(left: string, right: string, width: number): string {
45
+ return truncateToWidth(`${left}${right}`, width, "");
46
+ }
47
+
48
+ export class GitChangesView {
49
+ private selected = 0;
50
+ private focus: "files" | "diff" = "files";
51
+ private diffOffset = 0;
52
+ private cachedWidth?: number;
53
+ private cachedHeight?: number;
54
+ private cachedLines?: string[];
55
+ private readonly files: ReadonlyArray<ChangedFile>;
56
+ private readonly root: string;
57
+ private readonly omitted: number;
58
+ private readonly theme: Theme;
59
+ private readonly rows: () => number;
60
+ private readonly requestRender: () => void;
61
+ private readonly close: () => void;
62
+
63
+ constructor(
64
+ files: ReadonlyArray<ChangedFile>,
65
+ root: string,
66
+ omitted: number,
67
+ theme: Theme,
68
+ rows: () => number,
69
+ requestRender: () => void,
70
+ close: () => void,
71
+ ) {
72
+ this.files = files;
73
+ this.root = root;
74
+ this.omitted = omitted;
75
+ this.theme = theme;
76
+ this.rows = rows;
77
+ this.requestRender = requestRender;
78
+ this.close = close;
79
+ }
80
+
81
+ private current(): ChangedFile {
82
+ return this.files[this.selected]!;
83
+ }
84
+
85
+ private invalidateRender(): void {
86
+ this.cachedWidth = undefined;
87
+ this.cachedHeight = undefined;
88
+ this.cachedLines = undefined;
89
+ this.requestRender();
90
+ }
91
+
92
+ private moveFile(amount: number): void {
93
+ this.selected = (this.selected + amount + this.files.length) % this.files.length;
94
+ this.diffOffset = 0;
95
+ this.invalidateRender();
96
+ }
97
+
98
+ private moveDiff(amount: number): void {
99
+ const bodyHeight = Math.max(1, this.rows() - 8);
100
+ const max = Math.max(0, this.current().diff.length - bodyHeight);
101
+ this.diffOffset = Math.max(0, Math.min(max, this.diffOffset + amount));
102
+ this.invalidateRender();
103
+ }
104
+
105
+ handleInput(data: string): void {
106
+ if (this.focus === "files") {
107
+ if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
108
+ this.close();
109
+ return;
110
+ }
111
+ if (matchesKey(data, Key.down) || data === "j") this.moveFile(1);
112
+ else if (matchesKey(data, Key.up) || data === "k") this.moveFile(-1);
113
+ else if (matchesKey(data, Key.home) || data === "g") {
114
+ this.selected = 0;
115
+ this.diffOffset = 0;
116
+ this.invalidateRender();
117
+ } else if (matchesKey(data, Key.end) || data === "G") {
118
+ this.selected = this.files.length - 1;
119
+ this.diffOffset = 0;
120
+ this.invalidateRender();
121
+ } else if (
122
+ matchesKey(data, Key.enter) ||
123
+ matchesKey(data, Key.tab) ||
124
+ matchesKey(data, Key.right) ||
125
+ data === "l"
126
+ ) {
127
+ this.focus = "diff";
128
+ this.invalidateRender();
129
+ }
130
+ return;
131
+ }
132
+
133
+ if (
134
+ matchesKey(data, Key.escape) ||
135
+ matchesKey(data, Key.tab) ||
136
+ matchesKey(data, Key.left) ||
137
+ data === "h"
138
+ ) {
139
+ this.focus = "files";
140
+ this.invalidateRender();
141
+ return;
142
+ }
143
+ if (matchesKey(data, Key.down) || data === "j") this.moveDiff(4);
144
+ else if (matchesKey(data, Key.up) || data === "k") this.moveDiff(-4);
145
+ else if (matchesKey(data, Key.ctrl("d")) || matchesKey(data, Key.pageDown)) {
146
+ this.moveDiff(Math.max(1, Math.floor((this.rows() - 8) / 2)));
147
+ } else if (matchesKey(data, Key.ctrl("u")) || matchesKey(data, Key.pageUp)) {
148
+ this.moveDiff(-Math.max(1, Math.floor((this.rows() - 8) / 2)));
149
+ } else if (matchesKey(data, Key.home) || data === "g") {
150
+ this.diffOffset = 0;
151
+ this.invalidateRender();
152
+ } else if (matchesKey(data, Key.end) || data === "G") {
153
+ this.diffOffset = Math.max(0, this.current().diff.length - Math.max(1, this.rows() - 8));
154
+ this.invalidateRender();
155
+ } else if (matchesKey(data, Key.ctrl("c"))) {
156
+ this.close();
157
+ }
158
+ }
159
+
160
+ private renderList(width: number, height: number): string[] {
161
+ const inner = Math.max(1, width - 2);
162
+ const viewport = viewportSlice(this.files, this.selected, height);
163
+ const lines: string[] = [];
164
+ for (let row = 0; row < height; row++) {
165
+ const file = viewport.items[row];
166
+ if (!file) {
167
+ lines.push(`${this.theme.fg("border", "│")}${padLine("", inner)}${this.theme.fg("border", "│")}`);
168
+ continue;
169
+ }
170
+ const index = viewport.start + row;
171
+ const selected = index === this.selected;
172
+ const prefix = selected ? "❯" : " ";
173
+ const left = `${prefix} ${file.status.padEnd(2)} ${oneLine(file.path)}`;
174
+ const rowText = joinSides(left, statText(file), inner);
175
+ const styled = selected
176
+ ? this.theme.bg("selectedBg", this.theme.fg("accent", rowText))
177
+ : this.theme.fg(statusState(file.status), rowText);
178
+ lines.push(`${this.theme.fg(selected ? "borderAccent" : "border", "│")}${padLine(styled, inner)}${this.theme.fg(selected ? "borderAccent" : "border", "│")}`);
179
+ }
180
+ return lines;
181
+ }
182
+
183
+ private renderDiff(width: number, height: number): string[] {
184
+ const file = this.current();
185
+ const inner = Math.max(1, width - 2);
186
+ const lines: string[] = [];
187
+ const max = Math.max(0, file.diff.length - height);
188
+ this.diffOffset = Math.min(this.diffOffset, max);
189
+ for (let row = 0; row < height; row++) {
190
+ const source = file.diff[this.diffOffset + row];
191
+ const content = source === undefined ? "" : styleDiff(this.theme, source.replaceAll("\t", " "));
192
+ lines.push(`${this.theme.fg(this.focus === "diff" ? "borderAccent" : "border", "│")}${padLine(content, inner)}${this.theme.fg(this.focus === "diff" ? "borderAccent" : "border", "│")}`);
193
+ }
194
+ return lines;
195
+ }
196
+
197
+ render(width: number): string[] {
198
+ const terminalRows = this.rows();
199
+ if (
200
+ this.cachedLines &&
201
+ this.cachedWidth === width &&
202
+ this.cachedHeight === terminalRows
203
+ ) {
204
+ return this.cachedLines;
205
+ }
206
+ const safeWidth = Math.max(1, width);
207
+ const bodyHeight = Math.max(4, terminalRows - 7);
208
+ const current = this.current();
209
+ const totals = this.files.reduce(
210
+ (sum, file) => ({
211
+ additions: sum.additions + (file.additions ?? 0),
212
+ deletions: sum.deletions + (file.deletions ?? 0),
213
+ }),
214
+ { additions: 0, deletions: 0 },
215
+ );
216
+ const loaded = this.omitted > 0
217
+ ? `${this.files.length} shown · ${this.omitted} omitted`
218
+ : `${this.files.length} changed`;
219
+ const header = joinSides(
220
+ ` ${this.theme.bold("Git changes")} ${this.theme.fg("muted", oneLine(this.root))}`,
221
+ `${stateLabel(this.theme, "success", `+${totals.additions}`)} ${stateLabel(this.theme, "error", `-${totals.deletions}`)}`,
222
+ safeWidth,
223
+ );
224
+ const lines = [header];
225
+
226
+ if (safeWidth >= 88) {
227
+ const leftWidth = Math.max(34, Math.floor(safeWidth * 0.4));
228
+ const rightWidth = safeWidth - leftWidth;
229
+ const listTitle = `${loaded}${this.focus === "files" ? " · active" : ""}`;
230
+ const diffTitle = `${oneLine(current.path)} · ${statText(current)}${this.focus === "diff" ? " · active" : ""}`;
231
+ lines.push(combine(frameTop(this.theme, leftWidth, listTitle), frameTop(this.theme, rightWidth, diffTitle), safeWidth));
232
+ const list = this.renderList(leftWidth, bodyHeight);
233
+ const diff = this.renderDiff(rightWidth, bodyHeight);
234
+ for (let row = 0; row < bodyHeight; row++) {
235
+ lines.push(combine(list[row]!, diff[row]!, safeWidth));
236
+ }
237
+ lines.push(combine(frameBottom(this.theme, leftWidth), frameBottom(this.theme, rightWidth), safeWidth));
238
+ } else if (this.focus === "files") {
239
+ lines.push(frameTop(this.theme, safeWidth, loaded));
240
+ lines.push(...this.renderList(safeWidth, bodyHeight));
241
+ lines.push(frameBottom(this.theme, safeWidth));
242
+ } else {
243
+ lines.push(frameTop(this.theme, safeWidth, `${oneLine(current.path)} · ${statText(current)}`));
244
+ lines.push(...this.renderDiff(safeWidth, bodyHeight));
245
+ lines.push(frameBottom(this.theme, safeWidth));
246
+ }
247
+
248
+ const hint = this.focus === "files"
249
+ ? " up/down/jk select · enter/tab inspect · g/G first/last · escape close"
250
+ : " up/down/jk scroll · ctrl+u/d page · tab/h files · ctrl+c close";
251
+ lines.push(padLine(this.theme.fg("dim", hint), safeWidth));
252
+ this.cachedWidth = width;
253
+ this.cachedHeight = terminalRows;
254
+ this.cachedLines = lines.map((line) => truncateToWidth(line, safeWidth, ""));
255
+ return this.cachedLines;
256
+ }
257
+
258
+ invalidate(): void {
259
+ this.cachedWidth = undefined;
260
+ this.cachedHeight = undefined;
261
+ this.cachedLines = undefined;
262
+ }
263
+ }
264
+
265
+ export async function openGitChanges(
266
+ ctx: ExtensionContext,
267
+ root: string,
268
+ files: ReadonlyArray<ChangedFile>,
269
+ omitted: number,
270
+ ): Promise<void> {
271
+ await ctx.ui.custom<void>(
272
+ (tui, theme, _keybindings, done) =>
273
+ new GitChangesView(
274
+ files,
275
+ root,
276
+ omitted,
277
+ theme,
278
+ () => tui.terminal.rows || 30,
279
+ () => tui.requestRender(),
280
+ () => done(undefined),
281
+ ),
282
+ {
283
+ overlay: true,
284
+ overlayOptions: {
285
+ width: "100%",
286
+ maxHeight: "100%",
287
+ minWidth: 40,
288
+ },
289
+ },
290
+ );
291
+ }
@@ -0,0 +1,44 @@
1
+ # Pi Goal extension
2
+
3
+ A Pi-native persisted-goal system using Pi session entries and lifecycle hooks, with evidence checklists, bounded continuation, and explicit user controls.
4
+
5
+ ## Tools
6
+
7
+ - `create_goal` — explicitly create a goal; replaces an existing goal only when it is complete.
8
+ - `get_goal` — inspect objective, checklist, continuation state, status, budget, token usage, elapsed time, and remaining budget.
9
+ - `update_goal_progress` — maintain a structured checklist and evidence ledger.
10
+ - `update_goal` — mark a goal `complete` or `blocked` under strict, runtime-enforced completion and blocker rules.
11
+
12
+ Goal tools are active only for persisted Pi sessions. Temporary/`--no-session` children do not receive them.
13
+
14
+ ## Statuses
15
+
16
+ - `active`
17
+ - `paused`
18
+ - `blocked`
19
+ - `usage_limited`
20
+ - `budget_limited`
21
+ - `complete`
22
+
23
+ Use `/goal [<objective>|status|clear|edit|pause|resume]` for host-side control. Bare `/goal` and `/goal status` open the fullscreen control center with progress and budget meters, objective and checklist panels, selected-item evidence, live waiting/blocker state, and keyboard actions for pause/resume, edit, and guarded clear. Completed goals can be replaced without confirmation; unfinished goals require confirmation. Paused, blocked, and usage-limited goals can be resumed.
24
+
25
+ ## Lifecycle and accounting
26
+
27
+ - State is branch-safe and persisted as Pi custom session entries.
28
+ - Uncached input plus output usage is counted only while the goal is active.
29
+ - Usage is checkpointed after each model/tool turn, including the request that calls `update_goal`.
30
+ - Token limits stop work between tool turns rather than waiting for the outer agent run to finish.
31
+ - Interrupted runs and ordinary provider errors pause the goal without bypassing the blocker gate; exhausted usage/rate limits become `usage_limited`.
32
+ - Narration-only runs suppress further automatic continuation until the user resumes or steers the goal.
33
+ - Tool-backed active goals continue automatically when Pi becomes idle, after resume, and after `/reload`.
34
+ - Completed checklist items require concrete evidence. A goal with unfinished items cannot be marked complete.
35
+ - Blocking requires a specific blocked checklist item to persist for three goal turns.
36
+ - Continuation, completion-audit, blocked-audit, objective-update, and budget-limit steering follows current Codex behavior.
37
+
38
+ ## Pi-specific policy
39
+
40
+ Pi allows goal objectives up to **20,000 Unicode characters**. Current Codex uses 4,000; this larger limit is intentional.
41
+
42
+ ## Validation
43
+
44
+ From the repository root, run `npm run validate`.
@@ -0,0 +1,4 @@
1
+ export const STATE_ENTRY = "goal-state";
2
+ export const CONTEXT_ENTRY = "goal-context";
3
+ export const MAX_OBJECTIVE_CHARS = 20_000;
4
+ export const GOAL_TOOL_NAMES = ["get_goal", "create_goal", "update_goal_progress", "update_goal"] as const;