@jx-grxf/patchpilot 0.4.0 → 1.2.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 (180) hide show
  1. package/.env.example +17 -1
  2. package/README.md +113 -23
  3. package/SECURITY.md +7 -1
  4. package/dist/cli.js +103 -14
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/agent.d.ts +47 -1
  7. package/dist/core/agent.js +667 -76
  8. package/dist/core/agent.js.map +1 -1
  9. package/dist/core/cleanup.d.ts +3 -0
  10. package/dist/core/cleanup.js +29 -0
  11. package/dist/core/cleanup.js.map +1 -0
  12. package/dist/core/clipboard.d.ts +14 -0
  13. package/dist/core/clipboard.js +134 -0
  14. package/dist/core/clipboard.js.map +1 -0
  15. package/dist/core/codex.d.ts +8 -0
  16. package/dist/core/codex.js +28 -2
  17. package/dist/core/codex.js.map +1 -1
  18. package/dist/core/compaction.d.ts +23 -0
  19. package/dist/core/compaction.js +145 -0
  20. package/dist/core/compaction.js.map +1 -0
  21. package/dist/core/contextFormat.d.ts +21 -0
  22. package/dist/core/contextFormat.js +87 -0
  23. package/dist/core/contextFormat.js.map +1 -0
  24. package/dist/core/contextItem.d.ts +41 -0
  25. package/dist/core/contextItem.js +93 -0
  26. package/dist/core/contextItem.js.map +1 -0
  27. package/dist/core/contextStore.d.ts +48 -0
  28. package/dist/core/contextStore.js +306 -0
  29. package/dist/core/contextStore.js.map +1 -0
  30. package/dist/core/doctor.d.ts +4 -1
  31. package/dist/core/doctor.js +122 -3
  32. package/dist/core/doctor.js.map +1 -1
  33. package/dist/core/gemini.js +10 -4
  34. package/dist/core/gemini.js.map +1 -1
  35. package/dist/core/geminiWrapper.d.ts +92 -0
  36. package/dist/core/geminiWrapper.js +1258 -0
  37. package/dist/core/geminiWrapper.js.map +1 -0
  38. package/dist/core/http.js +70 -6
  39. package/dist/core/http.js.map +1 -1
  40. package/dist/core/json.d.ts +1 -1
  41. package/dist/core/json.js +81 -19
  42. package/dist/core/json.js.map +1 -1
  43. package/dist/core/memory.d.ts +16 -0
  44. package/dist/core/memory.js +108 -0
  45. package/dist/core/memory.js.map +1 -0
  46. package/dist/core/modelClient.js +7 -0
  47. package/dist/core/modelClient.js.map +1 -1
  48. package/dist/core/nvidia.d.ts +1 -1
  49. package/dist/core/nvidia.js +13 -4
  50. package/dist/core/nvidia.js.map +1 -1
  51. package/dist/core/ollama.js +13 -3
  52. package/dist/core/ollama.js.map +1 -1
  53. package/dist/core/openrouter.js +15 -6
  54. package/dist/core/openrouter.js.map +1 -1
  55. package/dist/core/projectInit.d.ts +6 -0
  56. package/dist/core/projectInit.js +44 -0
  57. package/dist/core/projectInit.js.map +1 -0
  58. package/dist/core/reasoning.js +6 -0
  59. package/dist/core/reasoning.js.map +1 -1
  60. package/dist/core/session.d.ts +1 -0
  61. package/dist/core/session.js +55 -3
  62. package/dist/core/session.js.map +1 -1
  63. package/dist/core/tokenAccounting.d.ts +4 -0
  64. package/dist/core/tokenAccounting.js +75 -13
  65. package/dist/core/tokenAccounting.js.map +1 -1
  66. package/dist/core/types.d.ts +65 -5
  67. package/dist/core/types.js +30 -1
  68. package/dist/core/types.js.map +1 -1
  69. package/dist/core/updateCheck.d.ts +19 -0
  70. package/dist/core/updateCheck.js +103 -0
  71. package/dist/core/updateCheck.js.map +1 -0
  72. package/dist/core/workspace.d.ts +37 -0
  73. package/dist/core/workspace.js +1535 -84
  74. package/dist/core/workspace.js.map +1 -1
  75. package/dist/tui/App.d.ts +1 -0
  76. package/dist/tui/App.js +1841 -140
  77. package/dist/tui/App.js.map +1 -1
  78. package/dist/tui/commands.js +141 -9
  79. package/dist/tui/commands.js.map +1 -1
  80. package/dist/tui/components/ApprovalPanel.js +16 -1
  81. package/dist/tui/components/ApprovalPanel.js.map +1 -1
  82. package/dist/tui/components/CommandSuggestions.js +33 -5
  83. package/dist/tui/components/CommandSuggestions.js.map +1 -1
  84. package/dist/tui/components/Composer.d.ts +3 -0
  85. package/dist/tui/components/Composer.js +57 -5
  86. package/dist/tui/components/Composer.js.map +1 -1
  87. package/dist/tui/components/ExperimentalPanel.d.ts +10 -0
  88. package/dist/tui/components/ExperimentalPanel.js +38 -0
  89. package/dist/tui/components/ExperimentalPanel.js.map +1 -0
  90. package/dist/tui/components/Header.js +3 -3
  91. package/dist/tui/components/Header.js.map +1 -1
  92. package/dist/tui/components/OnboardingPanel.d.ts +25 -1
  93. package/dist/tui/components/OnboardingPanel.js +87 -25
  94. package/dist/tui/components/OnboardingPanel.js.map +1 -1
  95. package/dist/tui/components/Sidebar.js +17 -13
  96. package/dist/tui/components/Sidebar.js.map +1 -1
  97. package/dist/tui/components/StartupBanner.d.ts +4 -0
  98. package/dist/tui/components/StartupBanner.js +9 -0
  99. package/dist/tui/components/StartupBanner.js.map +1 -0
  100. package/dist/tui/components/Transcript.d.ts +7 -0
  101. package/dist/tui/components/Transcript.js +87 -17
  102. package/dist/tui/components/Transcript.js.map +1 -1
  103. package/dist/tui/contextCommands.d.ts +8 -0
  104. package/dist/tui/contextCommands.js +205 -0
  105. package/dist/tui/contextCommands.js.map +1 -0
  106. package/dist/tui/experimental/AnimatedText.d.ts +38 -0
  107. package/dist/tui/experimental/AnimatedText.js +55 -0
  108. package/dist/tui/experimental/AnimatedText.js.map +1 -0
  109. package/dist/tui/experimental/Banner.d.ts +10 -0
  110. package/dist/tui/experimental/Banner.js +33 -0
  111. package/dist/tui/experimental/Banner.js.map +1 -0
  112. package/dist/tui/experimental/CommandPalette.d.ts +11 -0
  113. package/dist/tui/experimental/CommandPalette.js +25 -0
  114. package/dist/tui/experimental/CommandPalette.js.map +1 -0
  115. package/dist/tui/experimental/ExperimentalShell.d.ts +58 -0
  116. package/dist/tui/experimental/ExperimentalShell.js +366 -0
  117. package/dist/tui/experimental/ExperimentalShell.js.map +1 -0
  118. package/dist/tui/experimental/ThemePicker.d.ts +13 -0
  119. package/dist/tui/experimental/ThemePicker.js +12 -0
  120. package/dist/tui/experimental/ThemePicker.js.map +1 -0
  121. package/dist/tui/experimental/attachments.d.ts +35 -0
  122. package/dist/tui/experimental/attachments.js +244 -0
  123. package/dist/tui/experimental/attachments.js.map +1 -0
  124. package/dist/tui/experimental/composer.d.ts +24 -0
  125. package/dist/tui/experimental/composer.js +84 -0
  126. package/dist/tui/experimental/composer.js.map +1 -0
  127. package/dist/tui/experimental/geminiPricing.d.ts +16 -0
  128. package/dist/tui/experimental/geminiPricing.js +39 -0
  129. package/dist/tui/experimental/geminiPricing.js.map +1 -0
  130. package/dist/tui/experimental/layout.d.ts +46 -0
  131. package/dist/tui/experimental/layout.js +112 -0
  132. package/dist/tui/experimental/layout.js.map +1 -0
  133. package/dist/tui/experimental/theme.d.ts +35 -0
  134. package/dist/tui/experimental/theme.js +86 -0
  135. package/dist/tui/experimental/theme.js.map +1 -0
  136. package/dist/tui/experimental/transcriptRows.d.ts +20 -0
  137. package/dist/tui/experimental/transcriptRows.js +169 -0
  138. package/dist/tui/experimental/transcriptRows.js.map +1 -0
  139. package/dist/tui/experimental/ultraModes.d.ts +46 -0
  140. package/dist/tui/experimental/ultraModes.js +95 -0
  141. package/dist/tui/experimental/ultraModes.js.map +1 -0
  142. package/dist/tui/experimental/ultramaxx.d.ts +19 -0
  143. package/dist/tui/experimental/ultramaxx.js +43 -0
  144. package/dist/tui/experimental/ultramaxx.js.map +1 -0
  145. package/dist/tui/format.d.ts +4 -2
  146. package/dist/tui/format.js +21 -7
  147. package/dist/tui/format.js.map +1 -1
  148. package/dist/tui/hosts.js +7 -1
  149. package/dist/tui/hosts.js.map +1 -1
  150. package/dist/tui/layout.d.ts +26 -0
  151. package/dist/tui/layout.js +66 -0
  152. package/dist/tui/layout.js.map +1 -0
  153. package/dist/tui/modelSelection.d.ts +1 -1
  154. package/dist/tui/modelSelection.js +8 -6
  155. package/dist/tui/modelSelection.js.map +1 -1
  156. package/dist/tui/modes.d.ts +8 -1
  157. package/dist/tui/modes.js +20 -2
  158. package/dist/tui/modes.js.map +1 -1
  159. package/dist/tui/onboardingPreferences.d.ts +37 -0
  160. package/dist/tui/onboardingPreferences.js +118 -0
  161. package/dist/tui/onboardingPreferences.js.map +1 -0
  162. package/dist/tui/runStatus.d.ts +50 -0
  163. package/dist/tui/runStatus.js +164 -0
  164. package/dist/tui/runStatus.js.map +1 -0
  165. package/dist/tui/types.d.ts +8 -0
  166. package/dist/tui/types.js.map +1 -1
  167. package/docs/architecture.md +115 -0
  168. package/docs/gemini-wrapper.md +110 -0
  169. package/docs/product-context.md +43 -0
  170. package/docs/releases/v0.1.1-beta.md +18 -0
  171. package/docs/releases/v0.2.1.md +1 -1
  172. package/docs/releases/v0.3.1-beta.md +4 -0
  173. package/docs/releases/v0.4.0.md +1 -1
  174. package/docs/releases/v1.0.0.md +28 -0
  175. package/docs/releases/v1.0.1.md +25 -0
  176. package/docs/releases/v1.1.0.md +30 -0
  177. package/docs/releases/v1.2.0.md +28 -0
  178. package/docs/showcase/patchpilot-banner.png +0 -0
  179. package/docs/showcase/patchpilot-logo.png +0 -0
  180. package/package.json +8 -3
@@ -0,0 +1,244 @@
1
+ const extensionKinds = {
2
+ ".png": "image",
3
+ ".jpg": "image",
4
+ ".jpeg": "image",
5
+ ".gif": "image",
6
+ ".webp": "image",
7
+ ".heic": "image",
8
+ ".heif": "image",
9
+ ".pdf": "pdf",
10
+ ".docx": "docx",
11
+ ".doc": "docx",
12
+ ".svg": "text",
13
+ ".md": "text",
14
+ ".txt": "text",
15
+ ".rtf": "text",
16
+ ".json": "text",
17
+ ".jsonl": "text",
18
+ ".csv": "text",
19
+ ".yaml": "text",
20
+ ".yml": "text",
21
+ ".toml": "text",
22
+ ".xml": "text",
23
+ ".html": "text",
24
+ ".htm": "text",
25
+ ".css": "text",
26
+ ".scss": "text",
27
+ ".js": "text",
28
+ ".jsx": "text",
29
+ ".ts": "text",
30
+ ".tsx": "text",
31
+ ".mjs": "text",
32
+ ".mts": "text",
33
+ ".cjs": "text",
34
+ ".ini": "text",
35
+ ".tsv": "text",
36
+ ".py": "text",
37
+ ".java": "text",
38
+ ".kt": "text",
39
+ ".go": "text",
40
+ ".rs": "text",
41
+ ".php": "text",
42
+ ".rb": "text",
43
+ ".swift": "text",
44
+ ".c": "text",
45
+ ".cc": "text",
46
+ ".cpp": "text",
47
+ ".h": "text",
48
+ ".hpp": "text",
49
+ ".sh": "text",
50
+ ".zsh": "text",
51
+ ".bash": "text",
52
+ ".sql": "text",
53
+ ".log": "text",
54
+ ".diff": "text",
55
+ ".patch": "text",
56
+ };
57
+ /** Classify a path by extension; null if it is not an attachable document. */
58
+ export function attachmentKindForPath(path) {
59
+ const match = /\.[A-Za-z0-9]+$/.exec(path.trim());
60
+ if (!match) {
61
+ return null;
62
+ }
63
+ return extensionKinds[match[0].toLowerCase()] ?? null;
64
+ }
65
+ /** Remove a single layer of surrounding single/double quotes. */
66
+ export function stripQuotes(value) {
67
+ if (value.length >= 2 && ((value[0] === '"' && value.at(-1) === '"') || (value[0] === "'" && value.at(-1) === "'"))) {
68
+ return value.slice(1, -1);
69
+ }
70
+ return value;
71
+ }
72
+ /** Does this value look like a Windows path (drive-letter or UNC)? */
73
+ function looksLikeWindowsPath(value) {
74
+ return /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
75
+ }
76
+ /**
77
+ * Strip POSIX shell-escape backslashes (e.g. `\ ` for a space) without
78
+ * destroying Windows path separators. A backslash only acts as an escape when
79
+ * it precedes whitespace or a quote; a backslash before any other character is
80
+ * treated as a literal Windows path separator and kept.
81
+ */
82
+ function stripShellEscapes(value) {
83
+ return value.replace(/\\([\s'"])/g, "$1");
84
+ }
85
+ function stripFileUrlPrefix(value) {
86
+ // file:///C:/path -> C:/path ; file:///home/x -> /home/x ; file://host/share -> //host/share
87
+ const withoutScheme = value.replace(/^file:\/\//i, "");
88
+ if (/^\/[A-Za-z]:[\\/]/.test(withoutScheme)) {
89
+ return withoutScheme.slice(1);
90
+ }
91
+ return withoutScheme;
92
+ }
93
+ export function normalizeAttachmentPath(value) {
94
+ const unquoted = stripQuotes(value.trim());
95
+ if (/^file:\/\//i.test(unquoted)) {
96
+ try {
97
+ return stripFileUrlPrefix(decodeURIComponent(unquoted));
98
+ }
99
+ catch {
100
+ return stripFileUrlPrefix(unquoted);
101
+ }
102
+ }
103
+ return stripShellEscapes(unquoted);
104
+ }
105
+ function looksLikeAttachablePath(value, options) {
106
+ const trimmed = normalizeAttachmentPath(value);
107
+ if (!trimmed || attachmentKindForPath(trimmed) === null) {
108
+ return false;
109
+ }
110
+ const pathLike = trimmed.startsWith("/") ||
111
+ trimmed.startsWith("~") ||
112
+ trimmed.startsWith("./") ||
113
+ trimmed.startsWith(".\\") ||
114
+ trimmed.includes("/") ||
115
+ looksLikeWindowsPath(trimmed) ||
116
+ trimmed.includes("\\");
117
+ const raw = value.trim();
118
+ const wasQuoted = raw !== stripQuotes(raw);
119
+ const hadEscapedSpace = /\\\s/.test(raw);
120
+ const hasInnerSpace = /\s/.test(stripQuotes(raw));
121
+ return pathLike && (options.allowSpaces || wasQuoted || hadEscapedSpace || !hasInnerSpace);
122
+ }
123
+ /**
124
+ * Heuristic: does this pasted/typed value look like a single filesystem path
125
+ * to an attachable document? Accepts absolute, home-relative, and quoted
126
+ * paths; rejects free-form prose.
127
+ */
128
+ export function looksLikeAttachmentPath(value) {
129
+ return looksLikeAttachablePath(value, { allowSpaces: false });
130
+ }
131
+ export function attachmentTypeForPath(path) {
132
+ const match = /\.[A-Za-z0-9]+$/.exec(normalizeAttachmentPath(path));
133
+ if (!match) {
134
+ return "FILE";
135
+ }
136
+ const extension = match[0].slice(1).toUpperCase();
137
+ return extension === "JPEG" ? "JPG" : extension;
138
+ }
139
+ /** Human label for an attachment chip, e.g. "[PNG #2]". */
140
+ export function attachmentLabel(kind, index, path) {
141
+ const type = path ? attachmentTypeForPath(path) : kind.toUpperCase();
142
+ return `[${type} #${index}]`;
143
+ }
144
+ export function extractAttachmentPaths(value) {
145
+ const sanitized = sanitizePastedText(value);
146
+ const lines = sanitized
147
+ .split("\n")
148
+ .map((line) => line.trim())
149
+ .filter(Boolean);
150
+ if (lines.length > 1 && lines.every((line) => looksLikeAttachablePath(line, { allowSpaces: true }))) {
151
+ return lines.map(normalizeAttachmentPath);
152
+ }
153
+ const tokens = splitShellLikePaths(sanitized);
154
+ if (tokens.length > 0 && tokens.every((token) => looksLikeAttachablePath(token, { allowSpaces: false }))) {
155
+ return tokens.map(normalizeAttachmentPath);
156
+ }
157
+ return null;
158
+ }
159
+ function splitShellLikePaths(value) {
160
+ const tokens = [];
161
+ let current = "";
162
+ let quote = null;
163
+ let escaping = false;
164
+ // A backslash only acts as a POSIX shell escape when it precedes whitespace
165
+ // or a quote. Otherwise (e.g. inside a Windows path) it is a literal char.
166
+ const chars = [...value.trim()];
167
+ for (let index = 0; index < chars.length; index += 1) {
168
+ const char = chars[index];
169
+ if (escaping) {
170
+ current += `\\${char}`;
171
+ escaping = false;
172
+ continue;
173
+ }
174
+ if (char === "\\") {
175
+ const next = chars[index + 1];
176
+ if (next === undefined || /\s/.test(next) || next === "'" || next === '"') {
177
+ escaping = true;
178
+ continue;
179
+ }
180
+ current += char;
181
+ continue;
182
+ }
183
+ if ((char === "\"" || char === "'") && !quote) {
184
+ quote = char;
185
+ current += char;
186
+ continue;
187
+ }
188
+ if (quote === char) {
189
+ quote = null;
190
+ current += char;
191
+ continue;
192
+ }
193
+ if (!quote && /\s/.test(char)) {
194
+ if (current) {
195
+ tokens.push(current);
196
+ current = "";
197
+ }
198
+ continue;
199
+ }
200
+ current += char;
201
+ }
202
+ if (escaping) {
203
+ current += "\\";
204
+ }
205
+ if (current) {
206
+ tokens.push(current);
207
+ }
208
+ return tokens;
209
+ }
210
+ const kindSymbols = {
211
+ image: "▣",
212
+ pdf: "▤",
213
+ docx: "▥",
214
+ text: "▦",
215
+ file: "▧",
216
+ };
217
+ export function attachmentSymbol(kind) {
218
+ return kindSymbols[kind];
219
+ }
220
+ export function formatSessionArtifactContext(artifacts, limit = 12) {
221
+ const visibleArtifacts = artifacts.slice(-limit);
222
+ if (visibleArtifacts.length === 0) {
223
+ return "";
224
+ }
225
+ return [
226
+ "Known session attachments and artifacts (most recent last).",
227
+ "If the user asks about a prior uploaded file or artifact, use these paths and inspect the document before answering; provider chat state may not retain file inputs across PatchPilot runs.",
228
+ ...visibleArtifacts.map((artifact) => {
229
+ const type = attachmentTypeForPath(artifact.path);
230
+ return `- ${artifact.label} origin=${artifact.origin} type=${type} path=${JSON.stringify(artifact.path)}`;
231
+ })
232
+ ].join("\n");
233
+ }
234
+ // Control characters except tab (\t) and newline (\n) — built via escapes so
235
+ // no raw control bytes live in this source file.
236
+ const controlCharsPattern = new RegExp("[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]", "g");
237
+ /**
238
+ * Normalise pasted text: drop carriage returns and stray control characters
239
+ * (keeping tabs/newlines) so a multi-line paste does not corrupt the editor.
240
+ */
241
+ export function sanitizePastedText(value) {
242
+ return value.replace(/\r\n?/g, "\n").replace(controlCharsPattern, "");
243
+ }
244
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/tui/experimental/attachments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Cursor-aware view model for the experimental composer. The composer keeps a
3
+ * cursor position so the user can navigate inside the draft with the arrow
4
+ * keys, not just append at the end.
5
+ */
6
+ export type ComposerView = {
7
+ rows: string[];
8
+ cursorRow: number;
9
+ cursorCol: number;
10
+ hiddenAbove: number;
11
+ totalRows: number;
12
+ };
13
+ export type ComposerEditResult = {
14
+ input: string;
15
+ cursor: number;
16
+ };
17
+ export declare function insertComposerText(input: string, cursor: number, text: string): ComposerEditResult;
18
+ export declare function deleteComposerText(input: string, cursor: number, direction: "backward" | "forward"): ComposerEditResult;
19
+ /**
20
+ * Build the visible composer view for a given draft, cursor index and width.
21
+ * The window of `editorRows` rows always includes the cursor row, so moving
22
+ * the cursor up into earlier text scrolls the editor to follow it.
23
+ */
24
+ export declare function composerView(input: string, cursor: number, width: number, editorRows: number): ComposerView;
@@ -0,0 +1,84 @@
1
+ export function insertComposerText(input, cursor, text) {
2
+ const safeCursor = clampCursor(input, cursor);
3
+ return {
4
+ input: `${input.slice(0, safeCursor)}${text}${input.slice(safeCursor)}`,
5
+ cursor: safeCursor + text.length,
6
+ };
7
+ }
8
+ export function deleteComposerText(input, cursor, direction) {
9
+ const safeCursor = clampCursor(input, cursor);
10
+ if (direction === "backward") {
11
+ if (safeCursor === 0) {
12
+ return { input, cursor: safeCursor };
13
+ }
14
+ return {
15
+ input: `${input.slice(0, safeCursor - 1)}${input.slice(safeCursor)}`,
16
+ cursor: safeCursor - 1,
17
+ };
18
+ }
19
+ if (safeCursor >= input.length) {
20
+ return { input, cursor: safeCursor };
21
+ }
22
+ return {
23
+ input: `${input.slice(0, safeCursor)}${input.slice(safeCursor + 1)}`,
24
+ cursor: safeCursor,
25
+ };
26
+ }
27
+ /** Wrap a draft into rows, preserving explicit newlines, hard-chunking long lines. */
28
+ function wrapRows(input, width) {
29
+ const safeWidth = Math.max(1, width);
30
+ const rows = [];
31
+ const paragraphs = input.length > 0 ? input.split("\n") : [""];
32
+ for (const paragraph of paragraphs) {
33
+ if (paragraph.length === 0) {
34
+ rows.push("");
35
+ continue;
36
+ }
37
+ for (let index = 0; index < paragraph.length; index += safeWidth) {
38
+ rows.push(paragraph.slice(index, index + safeWidth));
39
+ }
40
+ }
41
+ return rows.length > 0 ? rows : [""];
42
+ }
43
+ /**
44
+ * Build the visible composer view for a given draft, cursor index and width.
45
+ * The window of `editorRows` rows always includes the cursor row, so moving
46
+ * the cursor up into earlier text scrolls the editor to follow it.
47
+ */
48
+ export function composerView(input, cursor, width, editorRows) {
49
+ const safeWidth = Math.max(1, width);
50
+ const visibleRows = Math.max(1, editorRows);
51
+ const clampedCursor = clampCursor(input, cursor);
52
+ const allRows = wrapRows(input, safeWidth);
53
+ // Locate the cursor by walking the paragraphs the same way wrapRows does.
54
+ const paragraphs = input.length > 0 ? input.split("\n") : [""];
55
+ let rowBase = 0;
56
+ let consumed = 0;
57
+ let cursorRow = 0;
58
+ let cursorCol = 0;
59
+ for (const paragraph of paragraphs) {
60
+ const rowsInParagraph = paragraph.length === 0 ? 1 : Math.ceil(paragraph.length / safeWidth);
61
+ if (clampedCursor >= consumed && clampedCursor <= consumed + paragraph.length) {
62
+ const offset = clampedCursor - consumed;
63
+ const rowWithin = Math.min(rowsInParagraph - 1, Math.floor(offset / safeWidth));
64
+ cursorRow = rowBase + rowWithin;
65
+ cursorCol = offset - rowWithin * safeWidth;
66
+ }
67
+ rowBase += rowsInParagraph;
68
+ consumed += paragraph.length + 1; // +1 for the newline
69
+ }
70
+ const maxStart = Math.max(0, allRows.length - visibleRows);
71
+ const start = Math.max(0, Math.min(cursorRow - visibleRows + 1, maxStart, cursorRow));
72
+ const windowed = allRows.slice(start, start + visibleRows);
73
+ return {
74
+ rows: windowed,
75
+ cursorRow: cursorRow - start,
76
+ cursorCol,
77
+ hiddenAbove: start,
78
+ totalRows: allRows.length,
79
+ };
80
+ }
81
+ function clampCursor(input, cursor) {
82
+ return Math.max(0, Math.min(Math.trunc(cursor), input.length));
83
+ }
84
+ //# sourceMappingURL=composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../../../src/tui/experimental/composer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Public Gemini API token pricing (USD per 1M tokens), used to show how much
3
+ * a gemini-wrapper session *would* have cost on the paid API — i.e. what the
4
+ * free Gemini Web route saved. Prices verified May 2026 from
5
+ * ai.google.dev/gemini-api/docs/pricing.
6
+ */
7
+ export type TokenPrice = {
8
+ inputPerMillion: number;
9
+ outputPerMillion: number;
10
+ };
11
+ /** Resolve the public API price for a Gemini model id. */
12
+ export declare function geminiPriceFor(model: string): TokenPrice;
13
+ /** Estimate the paid-API cost (USD) of the given token counts for a model. */
14
+ export declare function estimateGeminiCost(promptTokens: number, responseTokens: number, model: string): number;
15
+ /** Format a saved-cost figure for the header counter. */
16
+ export declare function formatSavedCost(usd: number): string;
@@ -0,0 +1,39 @@
1
+ const pricingTable = [
2
+ // Gemini 3.x Pro tier.
3
+ { pattern: /(gemini-?3|3\.\d).*pro|gemini-?3-pro/i, price: { inputPerMillion: 2, outputPerMillion: 12 } },
4
+ // Gemini 2.5 Pro.
5
+ { pattern: /2[._-]?5.*pro|gemini-pro|^pro$/i, price: { inputPerMillion: 1.25, outputPerMillion: 10 } },
6
+ // Flash-Lite.
7
+ { pattern: /flash[-_ ]?lite/i, price: { inputPerMillion: 0.1, outputPerMillion: 0.4 } },
8
+ // Gemini 2.5 / 2.0 Flash.
9
+ { pattern: /flash/i, price: { inputPerMillion: 0.3, outputPerMillion: 2.5 } },
10
+ ];
11
+ // "auto" and unknown models: assume the Flash tier (the common default route).
12
+ const fallbackPrice = { inputPerMillion: 0.3, outputPerMillion: 2.5 };
13
+ /** Resolve the public API price for a Gemini model id. */
14
+ export function geminiPriceFor(model) {
15
+ for (const entry of pricingTable) {
16
+ if (entry.pattern.test(model)) {
17
+ return entry.price;
18
+ }
19
+ }
20
+ return fallbackPrice;
21
+ }
22
+ /** Estimate the paid-API cost (USD) of the given token counts for a model. */
23
+ export function estimateGeminiCost(promptTokens, responseTokens, model) {
24
+ const price = geminiPriceFor(model);
25
+ const safePrompt = Math.max(0, promptTokens || 0);
26
+ const safeResponse = Math.max(0, responseTokens || 0);
27
+ return (safePrompt / 1_000_000) * price.inputPerMillion + (safeResponse / 1_000_000) * price.outputPerMillion;
28
+ }
29
+ /** Format a saved-cost figure for the header counter. */
30
+ export function formatSavedCost(usd) {
31
+ if (!Number.isFinite(usd) || usd <= 0) {
32
+ return "$0.00";
33
+ }
34
+ if (usd < 0.01) {
35
+ return "<$0.01";
36
+ }
37
+ return `$${usd.toFixed(2)}`;
38
+ }
39
+ //# sourceMappingURL=geminiPricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geminiPricing.js","sourceRoot":"","sources":["../../../src/tui/experimental/geminiPricing.ts"],"names":[],"mappings":"AAWA,MAAM,YAAY,GAAkD;IAClE,uBAAuB;IACvB,EAAE,OAAO,EAAE,uCAAuC,EAAE,KAAK,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,gBAAgB,EAAE,EAAE,EAAE,EAAE;IACzG,kBAAkB;IAClB,EAAE,OAAO,EAAE,iCAAiC,EAAE,KAAK,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,EAAE;IACtG,cAAc;IACd,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,EAAE;IACvF,0BAA0B;IAC1B,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,EAAE;CAC9E,CAAC;AAEF,+EAA+E;AAC/E,MAAM,aAAa,GAAe,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;AAElF,0DAA0D;AAC1D,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,kBAAkB,CAAC,YAAoB,EAAE,cAAsB,EAAE,KAAa;IAC5F,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,IAAI,CAAC,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,IAAI,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,eAAe,GAAG,CAAC,YAAY,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC;AAChH,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,GAAG,GAAG,IAAI,EAAE,CAAC;QACf,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Layout math for the experimental fullscreen shell. Kept as pure functions so
3
+ * the 80x24 / 120x40 / 207x47 acceptance cases can be covered without
4
+ * rendering Ink.
5
+ */
6
+ export type ExperimentalLayout = {
7
+ rootHeight: number;
8
+ rootWidth: number;
9
+ headerHeight: number;
10
+ bodyHeight: number;
11
+ transcriptHeight: number;
12
+ transcriptWidth: number;
13
+ composerHeight: number;
14
+ approvalHeight: number;
15
+ paletteHeight: number;
16
+ todoDockHeight: number;
17
+ artifactsHeight: number;
18
+ footerHeight: number;
19
+ };
20
+ /** Height of the todo dock panel for a given number of todos (0 = hidden). */
21
+ export declare function todoDockHeightFor(todoCount: number): number;
22
+ export declare function computeExperimentalLayout(options: {
23
+ rows: number;
24
+ columns: number;
25
+ composerInput: string;
26
+ paletteItemCount: number;
27
+ approvalActive: boolean;
28
+ todoCount?: number;
29
+ hasArtifacts?: boolean;
30
+ }): ExperimentalLayout;
31
+ export type RowWindow = {
32
+ start: number;
33
+ end: number;
34
+ anchored: "top" | "bottom";
35
+ clampedOffset: number;
36
+ hasOverflow: boolean;
37
+ };
38
+ /**
39
+ * Window a list of rows into a viewport.
40
+ *
41
+ * - When the content fits, it is anchored to the TOP (short outputs start at
42
+ * the top, no artificial blank lines — the caller pads with flexGrow).
43
+ * - When it overflows, it is anchored to the BOTTOM and `scrollOffset` walks
44
+ * the window upward.
45
+ */
46
+ export declare function windowRows(totalRows: number, viewportHeight: number, scrollOffset: number): RowWindow;
@@ -0,0 +1,112 @@
1
+ import { computeComposerLayout } from "../layout.js";
2
+ const MIN_ROWS = 24;
3
+ const MIN_COLUMNS = 80;
4
+ const HEADER_HEIGHT = 4;
5
+ const FOOTER_HEIGHT = 1;
6
+ const APPROVAL_HEIGHT = 8;
7
+ const MIN_TRANSCRIPT_HEIGHT = 3;
8
+ const MAX_PALETTE_ROWS = 8;
9
+ const MAX_TODO_DOCK_ROWS = 6;
10
+ /** Height of the todo dock panel for a given number of todos (0 = hidden). */
11
+ export function todoDockHeightFor(todoCount) {
12
+ const count = Math.max(0, Math.floor(todoCount));
13
+ if (count === 0) {
14
+ return 0;
15
+ }
16
+ // header row + up to MAX_TODO_DOCK_ROWS items + rounded border (2).
17
+ return Math.min(count, MAX_TODO_DOCK_ROWS) + 1 + 2;
18
+ }
19
+ // Minimum height the todo dock may be folded down to: border (2) + header +
20
+ // one visible item. Below this it is dropped entirely.
21
+ const MIN_TODO_DOCK_HEIGHT = 4;
22
+ export function computeExperimentalLayout(options) {
23
+ const rootHeight = Math.max(MIN_ROWS, Math.floor(options.rows) || MIN_ROWS);
24
+ const rootWidth = Math.max(MIN_COLUMNS, Math.floor(options.columns) || MIN_COLUMNS);
25
+ // Inner content width: 1 cell of horizontal padding on each side.
26
+ const contentWidth = Math.max(40, rootWidth - 2);
27
+ // Transcript and composer sit inside a rounded border (2 columns).
28
+ const transcriptWidth = Math.max(32, contentWidth - 2);
29
+ const composerEditor = computeComposerLayout({
30
+ input: options.composerInput,
31
+ width: transcriptWidth,
32
+ promptWidth: 2,
33
+ minHeight: 3,
34
+ maxHeight: 7,
35
+ });
36
+ // composer editor rows + status row + rounded border.
37
+ const composerHeight = composerEditor.height + 2;
38
+ const approvalHeight = options.approvalActive ? APPROVAL_HEIGHT : 0;
39
+ // Priority allocator: header, the blocking prompt, the composer and the
40
+ // footer are critical safety surfaces — they get their full height first and
41
+ // are never clipped. Whatever is left is shared between the transcript and
42
+ // the secondary panels (palette, todos, artifacts), which fold before any
43
+ // critical UI is touched.
44
+ const critical = HEADER_HEIGHT + FOOTER_HEIGHT + composerHeight + approvalHeight;
45
+ const paletteRows = Math.min(MAX_PALETTE_ROWS, Math.max(0, options.paletteItemCount));
46
+ // palette: header row + items + preview block (3) + border.
47
+ const paletteDesired = paletteRows > 0 ? paletteRows + 6 : 0;
48
+ const todoDesired = todoDockHeightFor(options.todoCount ?? 0);
49
+ // Artifacts bar: rounded border (2) + one chip row.
50
+ const artifactsDesired = options.hasArtifacts ? 3 : 0;
51
+ // Budget for the secondary panels, after reserving the transcript minimum.
52
+ let budget = Math.max(0, rootHeight - critical - MIN_TRANSCRIPT_HEIGHT);
53
+ // Palette is highest priority while open — the user is actively navigating
54
+ // it — so it gets its full height or nothing.
55
+ const paletteHeight = paletteDesired > 0 && budget >= paletteDesired ? paletteDesired : 0;
56
+ budget -= paletteHeight;
57
+ // Todos may fold: full height if it fits, otherwise shrunk to whatever space
58
+ // is left down to MIN_TODO_DOCK_HEIGHT, else dropped.
59
+ let todoDockHeight = 0;
60
+ if (todoDesired > 0) {
61
+ if (budget >= todoDesired) {
62
+ todoDockHeight = todoDesired;
63
+ }
64
+ else if (budget >= MIN_TODO_DOCK_HEIGHT) {
65
+ todoDockHeight = budget;
66
+ }
67
+ }
68
+ budget -= todoDockHeight;
69
+ // Artifacts bar is lowest priority — shown only if it still fits whole.
70
+ const artifactsHeight = artifactsDesired > 0 && budget >= artifactsDesired ? artifactsDesired : 0;
71
+ const secondary = paletteHeight + todoDockHeight + artifactsHeight;
72
+ // The transcript flexes into the remainder. It keeps its minimum whenever the
73
+ // critical UI fits; on extreme terminals it yields below the minimum so the
74
+ // composer and prompts always stay fully visible.
75
+ const transcriptHeight = Math.max(1, rootHeight - critical - secondary);
76
+ const bodyHeight = rootHeight - HEADER_HEIGHT;
77
+ return {
78
+ rootHeight,
79
+ rootWidth,
80
+ headerHeight: HEADER_HEIGHT,
81
+ bodyHeight,
82
+ transcriptHeight,
83
+ transcriptWidth,
84
+ composerHeight,
85
+ approvalHeight,
86
+ paletteHeight,
87
+ todoDockHeight,
88
+ artifactsHeight,
89
+ footerHeight: FOOTER_HEIGHT,
90
+ };
91
+ }
92
+ /**
93
+ * Window a list of rows into a viewport.
94
+ *
95
+ * - When the content fits, it is anchored to the TOP (short outputs start at
96
+ * the top, no artificial blank lines — the caller pads with flexGrow).
97
+ * - When it overflows, it is anchored to the BOTTOM and `scrollOffset` walks
98
+ * the window upward.
99
+ */
100
+ export function windowRows(totalRows, viewportHeight, scrollOffset) {
101
+ const total = Math.max(0, Math.floor(totalRows));
102
+ const viewport = Math.max(1, Math.floor(viewportHeight));
103
+ if (total <= viewport) {
104
+ return { start: 0, end: total, anchored: "top", clampedOffset: 0, hasOverflow: false };
105
+ }
106
+ const maxOffset = total - viewport;
107
+ const clampedOffset = Math.max(0, Math.min(Math.floor(scrollOffset), maxOffset));
108
+ const end = total - clampedOffset;
109
+ const start = Math.max(0, end - viewport);
110
+ return { start, end, anchored: "bottom", clampedOffset, hasOverflow: true };
111
+ }
112
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/tui/experimental/layout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { InkColor } from "../format.js";
2
+ import type { TranscriptBlockKind } from "../types.js";
3
+ /**
4
+ * Shared visual language for the experimental shell: consistent symbols and
5
+ * colors so tools, approvals, and run state read at a glance.
6
+ */
7
+ export declare const symbols: {
8
+ readonly user: "›";
9
+ readonly assistant: "◆";
10
+ readonly tool: "⚡";
11
+ readonly diff: "±";
12
+ readonly approval: "⚠";
13
+ readonly update: "⬆";
14
+ readonly reauth: "↻";
15
+ readonly info: "ℹ";
16
+ readonly error: "✗";
17
+ readonly final: "✓";
18
+ readonly status: "•";
19
+ readonly arrow: "→";
20
+ readonly bullet: "·";
21
+ readonly pin: "📌";
22
+ readonly todoDone: "✓";
23
+ readonly todoActive: "▸";
24
+ readonly todoPending: "○";
25
+ readonly caret: "▏";
26
+ readonly barFilled: "█";
27
+ readonly barEmpty: "░";
28
+ };
29
+ export type ToneColor = InkColor;
30
+ /** Marker glyph for a transcript block kind. */
31
+ export declare function blockSymbol(kind: TranscriptBlockKind): string;
32
+ /** Accent color for a transcript block kind. */
33
+ export declare function blockColor(kind: TranscriptBlockKind, danger: boolean): InkColor;
34
+ /** Border / accent color that reflects the current work state. */
35
+ export declare function workStateColor(workState: string): InkColor;