@steipete/oracle 0.17.3 → 0.19.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 (161) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/oracle-cli.js +75 -64
  3. package/dist/bin/oracle-mcp.js +0 -0
  4. package/dist/src/browser/actions/archiveConversation.js +31 -7
  5. package/dist/src/browser/actions/assistantResponse.js +43 -1
  6. package/dist/src/browser/actions/attachmentContext.js +239 -0
  7. package/dist/src/browser/actions/attachmentDataTransfer.js +19 -1
  8. package/dist/src/browser/actions/attachmentEvidence.js +78 -0
  9. package/dist/src/browser/actions/attachmentProgress.js +41 -0
  10. package/dist/src/browser/actions/attachmentPrompt.js +130 -0
  11. package/dist/src/browser/actions/attachments.js +306 -355
  12. package/dist/src/browser/actions/deepResearch.js +2 -1
  13. package/dist/src/browser/actions/modelSelection.js +13 -3
  14. package/dist/src/browser/actions/navigation.js +1 -1
  15. package/dist/src/browser/actions/promptComposer.js +361 -186
  16. package/dist/src/browser/actions/remoteFileTransfer.js +4 -2
  17. package/dist/src/browser/actions/thinkingStatus.js +2 -5
  18. package/dist/src/browser/actions/thinkingTime.js +267 -15
  19. package/dist/src/browser/attachRunning.js +46 -2
  20. package/dist/src/browser/attachmentValidation.js +53 -0
  21. package/dist/src/browser/cancellation.js +105 -0
  22. package/dist/src/browser/chatgptImages.js +2 -0
  23. package/dist/src/browser/chatgptThrottle.js +104 -0
  24. package/dist/src/browser/chromeLifecycle.js +267 -43
  25. package/dist/src/browser/config.js +15 -3
  26. package/dist/src/browser/detect.js +1 -1
  27. package/dist/src/browser/index.js +447 -263
  28. package/dist/src/browser/modelDisplay.js +9 -0
  29. package/dist/src/browser/pageActions.js +2 -2
  30. package/dist/src/browser/policies.js +6 -2
  31. package/dist/src/browser/profileState.js +114 -9
  32. package/dist/src/browser/projectSourcesRunner.js +88 -17
  33. package/dist/src/browser/prompt.js +272 -113
  34. package/dist/src/browser/providers/chatgptDomProvider.js +2 -0
  35. package/dist/src/browser/reattach.js +14 -4
  36. package/dist/src/browser/sessionRunner.js +71 -11
  37. package/dist/src/browser/tabLeaseRegistry.js +414 -38
  38. package/dist/src/browser/utils.js +20 -2
  39. package/dist/src/cli/browserConfig.js +40 -15
  40. package/dist/src/cli/browserDefaults.js +13 -0
  41. package/dist/src/cli/browserTabs.js +12 -29
  42. package/dist/src/cli/detachedSession.js +69 -0
  43. package/dist/src/cli/dryRun.js +58 -47
  44. package/dist/src/cli/harvestIntegrity.js +140 -0
  45. package/dist/src/cli/projectSources.js +5 -3
  46. package/dist/src/cli/serveBrowserConfig.js +24 -0
  47. package/dist/src/cli/sessionDisplay.js +9 -2
  48. package/dist/src/cli/sessionRunner.js +91 -7
  49. package/dist/src/config.js +1 -0
  50. package/dist/src/gemini-web/client.js +67 -21
  51. package/dist/src/gemini-web/executor.js +4 -0
  52. package/dist/src/gemini-web/http.js +15 -0
  53. package/dist/src/gemini-web/models.js +15 -11
  54. package/dist/src/mcp/server.js +11 -14
  55. package/dist/src/mcp/tools/chatgptImage.js +4 -4
  56. package/dist/src/mcp/tools/consult.js +97 -26
  57. package/dist/src/mcp/tools/projectSources.js +4 -6
  58. package/dist/src/mcp/tools/sessionResources.js +1 -1
  59. package/dist/src/mcp/tools/sessions.js +2 -2
  60. package/dist/src/mcp/tools/wait.js +182 -0
  61. package/dist/src/mcp/types.js +7 -0
  62. package/dist/src/oracle/errors.js +11 -0
  63. package/dist/src/oracle/providerRoutePlan.js +3 -2
  64. package/dist/src/remote/client.js +70 -6
  65. package/dist/src/remote/health.js +6 -2
  66. package/dist/src/remote/runSlots.js +83 -0
  67. package/dist/src/remote/server.js +351 -72
  68. package/dist/src/sessionManager.js +36 -1
  69. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  70. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  71. package/package.json +25 -23
  72. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  73. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  74. package/dist/bin/oracle.js +0 -683
  75. package/dist/docs-site/.nojekyll +0 -0
  76. package/dist/docs-site/CNAME +0 -1
  77. package/dist/docs-site/RELEASING.html +0 -410
  78. package/dist/docs-site/agents.html +0 -374
  79. package/dist/docs-site/anthropic.html +0 -368
  80. package/dist/docs-site/bridge.html +0 -400
  81. package/dist/docs-site/browser-mode.html +0 -588
  82. package/dist/docs-site/chromium-forks.html +0 -347
  83. package/dist/docs-site/cli-reference.html +0 -346
  84. package/dist/docs-site/configuration.html +0 -436
  85. package/dist/docs-site/favicon.svg +0 -14
  86. package/dist/docs-site/followup.html +0 -368
  87. package/dist/docs-site/gemini.html +0 -379
  88. package/dist/docs-site/grok.html +0 -325
  89. package/dist/docs-site/index.html +0 -360
  90. package/dist/docs-site/install.html +0 -335
  91. package/dist/docs-site/linux.html +0 -321
  92. package/dist/docs-site/llms.txt +0 -43
  93. package/dist/docs-site/manual-tests.html +0 -595
  94. package/dist/docs-site/mcp.html +0 -368
  95. package/dist/docs-site/multimodel.html +0 -364
  96. package/dist/docs-site/mythical-pro-agents.html +0 -360
  97. package/dist/docs-site/notifier.html +0 -338
  98. package/dist/docs-site/openai-endpoints.html +0 -387
  99. package/dist/docs-site/openrouter.html +0 -344
  100. package/dist/docs-site/quickstart.html +0 -369
  101. package/dist/docs-site/refactor/ux.html +0 -532
  102. package/dist/docs-site/sessions.html +0 -388
  103. package/dist/docs-site/social-card.png +0 -0
  104. package/dist/docs-site/social-card.svg +0 -79
  105. package/dist/docs-site/spec.html +0 -363
  106. package/dist/docs-site/testing.html +0 -320
  107. package/dist/docs-site/tui-debug.html +0 -326
  108. package/dist/docs-site/windows-work.html +0 -323
  109. package/dist/docs-site/windows.html +0 -320
  110. package/dist/markdansi/types/index.js +0 -4
  111. package/dist/oracle/bin/oracle-cli.js +0 -472
  112. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  113. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  114. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  115. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  116. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  117. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  118. package/dist/oracle/src/browser/config.js +0 -33
  119. package/dist/oracle/src/browser/constants.js +0 -40
  120. package/dist/oracle/src/browser/cookies.js +0 -210
  121. package/dist/oracle/src/browser/domDebug.js +0 -36
  122. package/dist/oracle/src/browser/index.js +0 -331
  123. package/dist/oracle/src/browser/pageActions.js +0 -5
  124. package/dist/oracle/src/browser/prompt.js +0 -88
  125. package/dist/oracle/src/browser/promptSummary.js +0 -20
  126. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  127. package/dist/oracle/src/browser/types.js +0 -1
  128. package/dist/oracle/src/browser/utils.js +0 -62
  129. package/dist/oracle/src/browserMode.js +0 -1
  130. package/dist/oracle/src/cli/browserConfig.js +0 -44
  131. package/dist/oracle/src/cli/dryRun.js +0 -59
  132. package/dist/oracle/src/cli/engine.js +0 -17
  133. package/dist/oracle/src/cli/errorUtils.js +0 -9
  134. package/dist/oracle/src/cli/help.js +0 -70
  135. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  136. package/dist/oracle/src/cli/options.js +0 -103
  137. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  138. package/dist/oracle/src/cli/rootAlias.js +0 -30
  139. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  140. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  141. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  142. package/dist/oracle/src/heartbeat.js +0 -43
  143. package/dist/oracle/src/oracle/client.js +0 -48
  144. package/dist/oracle/src/oracle/config.js +0 -29
  145. package/dist/oracle/src/oracle/errors.js +0 -101
  146. package/dist/oracle/src/oracle/files.js +0 -220
  147. package/dist/oracle/src/oracle/format.js +0 -33
  148. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  149. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  150. package/dist/oracle/src/oracle/request.js +0 -48
  151. package/dist/oracle/src/oracle/run.js +0 -444
  152. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  153. package/dist/oracle/src/oracle/types.js +0 -1
  154. package/dist/oracle/src/oracle.js +0 -9
  155. package/dist/oracle/src/sessionManager.js +0 -205
  156. package/dist/oracle/src/version.js +0 -39
  157. package/dist/scripts/chrome/browser-tools.js +0 -295
  158. package/dist/src/browser/chromeCookies.js +0 -312
  159. package/dist/src/browser/keytarShim.js +0 -56
  160. package/dist/src/browser/profileSync.js +0 -141
  161. package/dist/src/browser/windowsCookies.js +0 -219
@@ -1,220 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import fg from 'fast-glob';
4
- import { FileValidationError } from './errors.js';
5
- const MAX_FILE_SIZE_BYTES = 1 * 1024 * 1024; // 1 MB
6
- const DEFAULT_FS = fs;
7
- export async function readFiles(filePaths, { cwd = process.cwd(), fsModule = DEFAULT_FS, maxFileSizeBytes = MAX_FILE_SIZE_BYTES } = {}) {
8
- if (!filePaths || filePaths.length === 0) {
9
- return [];
10
- }
11
- const partitioned = await partitionFileInputs(filePaths, cwd, fsModule);
12
- const useNativeFilesystem = fsModule === DEFAULT_FS;
13
- let candidatePaths = [];
14
- if (useNativeFilesystem) {
15
- candidatePaths = await expandWithNativeGlob(partitioned, cwd);
16
- }
17
- else {
18
- if (partitioned.globPatterns.length > 0 || partitioned.excludePatterns.length > 0) {
19
- throw new Error('Glob patterns and exclusions are only supported for on-disk files.');
20
- }
21
- candidatePaths = await expandWithCustomFs(partitioned, fsModule);
22
- }
23
- if (candidatePaths.length === 0) {
24
- throw new FileValidationError('No files matched the provided --file patterns.', {
25
- patterns: partitioned.globPatterns,
26
- excludes: partitioned.excludePatterns,
27
- });
28
- }
29
- const oversized = [];
30
- const accepted = [];
31
- for (const filePath of candidatePaths) {
32
- let stats;
33
- try {
34
- stats = await fsModule.stat(filePath);
35
- }
36
- catch (error) {
37
- throw new FileValidationError(`Missing file or directory: ${relativePath(filePath, cwd)}`, { path: filePath }, error);
38
- }
39
- if (!stats.isFile()) {
40
- continue;
41
- }
42
- if (maxFileSizeBytes && typeof stats.size === 'number' && stats.size > maxFileSizeBytes) {
43
- const relative = path.relative(cwd, filePath) || filePath;
44
- oversized.push(`${relative} (${formatBytes(stats.size)})`);
45
- continue;
46
- }
47
- accepted.push(filePath);
48
- }
49
- if (oversized.length > 0) {
50
- throw new FileValidationError(`The following files exceed the 1 MB limit:\n- ${oversized.join('\n- ')}`, {
51
- files: oversized,
52
- limitBytes: maxFileSizeBytes,
53
- });
54
- }
55
- const files = [];
56
- for (const filePath of accepted) {
57
- const content = await fsModule.readFile(filePath, 'utf8');
58
- files.push({ path: filePath, content });
59
- }
60
- return files;
61
- }
62
- async function partitionFileInputs(rawPaths, cwd, fsModule) {
63
- const result = {
64
- globPatterns: [],
65
- excludePatterns: [],
66
- literalFiles: [],
67
- literalDirectories: [],
68
- };
69
- for (const entry of rawPaths) {
70
- const raw = entry?.trim();
71
- if (!raw) {
72
- continue;
73
- }
74
- if (raw.startsWith('!')) {
75
- const normalized = normalizeGlob(raw.slice(1), cwd);
76
- if (normalized) {
77
- result.excludePatterns.push(normalized);
78
- }
79
- continue;
80
- }
81
- if (fg.isDynamicPattern(raw)) {
82
- result.globPatterns.push(normalizeGlob(raw, cwd));
83
- continue;
84
- }
85
- const absolutePath = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
86
- let stats;
87
- try {
88
- stats = await fsModule.stat(absolutePath);
89
- }
90
- catch (error) {
91
- throw new FileValidationError(`Missing file or directory: ${raw}`, { path: absolutePath }, error);
92
- }
93
- if (stats.isDirectory()) {
94
- result.literalDirectories.push(absolutePath);
95
- }
96
- else if (stats.isFile()) {
97
- result.literalFiles.push(absolutePath);
98
- }
99
- else {
100
- throw new FileValidationError(`Not a file or directory: ${raw}`, { path: absolutePath });
101
- }
102
- }
103
- return result;
104
- }
105
- async function expandWithNativeGlob(partitioned, cwd) {
106
- const patterns = [
107
- ...partitioned.globPatterns,
108
- ...partitioned.literalFiles.map((absPath) => toPosixRelativeOrBasename(absPath, cwd)),
109
- ...partitioned.literalDirectories.map((absDir) => makeDirectoryPattern(toPosixRelative(absDir, cwd))),
110
- ].filter(Boolean);
111
- if (patterns.length === 0) {
112
- return [];
113
- }
114
- const matches = await fg(patterns, {
115
- cwd,
116
- absolute: true,
117
- dot: true,
118
- ignore: partitioned.excludePatterns,
119
- onlyFiles: true,
120
- followSymbolicLinks: false,
121
- });
122
- return Array.from(new Set(matches.map((match) => path.resolve(match))));
123
- }
124
- async function expandWithCustomFs(partitioned, fsModule) {
125
- const paths = new Set();
126
- partitioned.literalFiles.forEach((file) => {
127
- paths.add(file);
128
- });
129
- for (const directory of partitioned.literalDirectories) {
130
- const nested = await expandDirectoryRecursive(directory, fsModule);
131
- nested.forEach((entry) => {
132
- paths.add(entry);
133
- });
134
- }
135
- return Array.from(paths);
136
- }
137
- async function expandDirectoryRecursive(directory, fsModule) {
138
- const entries = await fsModule.readdir(directory);
139
- const results = [];
140
- for (const entry of entries) {
141
- const childPath = path.join(directory, entry);
142
- const stats = await fsModule.stat(childPath);
143
- if (stats.isDirectory()) {
144
- results.push(...(await expandDirectoryRecursive(childPath, fsModule)));
145
- }
146
- else if (stats.isFile()) {
147
- results.push(childPath);
148
- }
149
- }
150
- return results;
151
- }
152
- function makeDirectoryPattern(relative) {
153
- if (relative === '.' || relative === '') {
154
- return '**/*';
155
- }
156
- return `${stripTrailingSlashes(relative)}/**/*`;
157
- }
158
- function normalizeGlob(pattern, cwd) {
159
- if (!pattern) {
160
- return '';
161
- }
162
- let normalized = pattern;
163
- if (path.isAbsolute(normalized)) {
164
- normalized = path.relative(cwd, normalized);
165
- }
166
- normalized = toPosix(normalized);
167
- if (normalized.startsWith('./')) {
168
- normalized = normalized.slice(2);
169
- }
170
- return normalized;
171
- }
172
- function toPosix(value) {
173
- return value.replace(/\\/g, '/');
174
- }
175
- function toPosixRelative(absPath, cwd) {
176
- const relative = path.relative(cwd, absPath);
177
- if (!relative) {
178
- return '.';
179
- }
180
- return toPosix(relative);
181
- }
182
- function toPosixRelativeOrBasename(absPath, cwd) {
183
- const relative = path.relative(cwd, absPath);
184
- return toPosix(relative || path.basename(absPath));
185
- }
186
- function stripTrailingSlashes(value) {
187
- const normalized = toPosix(value);
188
- return normalized.replace(/\/+$/g, '');
189
- }
190
- function formatBytes(size) {
191
- if (size >= 1024 * 1024) {
192
- return `${(size / (1024 * 1024)).toFixed(1)} MB`;
193
- }
194
- if (size >= 1024) {
195
- return `${(size / 1024).toFixed(1)} KB`;
196
- }
197
- return `${size} B`;
198
- }
199
- function relativePath(targetPath, cwd) {
200
- const relative = path.relative(cwd, targetPath);
201
- return relative || targetPath;
202
- }
203
- export function createFileSections(files, cwd = process.cwd()) {
204
- return files.map((file, index) => {
205
- const relative = path.relative(cwd, file.path) || file.path;
206
- const sectionText = [
207
- `### File ${index + 1}: ${relative}`,
208
- '```',
209
- file.content.trimEnd(),
210
- '```',
211
- ].join('\n');
212
- return {
213
- index: index + 1,
214
- absolutePath: file.path,
215
- displayPath: relative,
216
- sectionText,
217
- content: file.content,
218
- };
219
- });
220
- }
@@ -1,33 +0,0 @@
1
- export function formatUSD(value) {
2
- if (!Number.isFinite(value)) {
3
- return 'n/a';
4
- }
5
- if (value >= 0.1) {
6
- return `$${value.toFixed(2)}`;
7
- }
8
- if (value >= 0.01) {
9
- return `$${value.toFixed(3)}`;
10
- }
11
- return `$${value.toFixed(6)}`;
12
- }
13
- export function formatNumber(value, { estimated = false } = {}) {
14
- if (value == null) {
15
- return 'n/a';
16
- }
17
- const suffix = estimated ? ' (est.)' : '';
18
- return `${value.toLocaleString()}${suffix}`;
19
- }
20
- export function formatElapsed(ms) {
21
- const totalSeconds = ms / 1000;
22
- if (totalSeconds < 60) {
23
- return `${totalSeconds.toFixed(2)}s`;
24
- }
25
- const minutes = Math.floor(totalSeconds / 60);
26
- let seconds = Math.round(totalSeconds - minutes * 60);
27
- let adjustedMinutes = minutes;
28
- if (seconds === 60) {
29
- adjustedMinutes += 1;
30
- seconds = 0;
31
- }
32
- return `${adjustedMinutes}m ${seconds}s`;
33
- }
@@ -1,7 +0,0 @@
1
- export function createFsAdapter(fsModule) {
2
- return {
3
- stat: (targetPath) => fsModule.stat(targetPath),
4
- readdir: (targetPath) => fsModule.readdir(targetPath),
5
- readFile: (targetPath, encoding) => fsModule.readFile(targetPath, encoding),
6
- };
7
- }
@@ -1,60 +0,0 @@
1
- import process from 'node:process';
2
- const OSC = '\u001b]9;4;';
3
- const ST = '\u001b\\';
4
- function sanitizeLabel(label) {
5
- const withoutEscape = label.split('\u001b').join('');
6
- const withoutBellAndSt = withoutEscape.replaceAll('\u0007', '').replaceAll('\u009c', '');
7
- return withoutBellAndSt.replaceAll(']', '').trim();
8
- }
9
- export function supportsOscProgress(env = process.env, isTty = process.stdout.isTTY) {
10
- if (!isTty) {
11
- return false;
12
- }
13
- if (env.ORACLE_NO_OSC_PROGRESS === '1') {
14
- return false;
15
- }
16
- if (env.ORACLE_FORCE_OSC_PROGRESS === '1') {
17
- return true;
18
- }
19
- const termProgram = (env.TERM_PROGRAM ?? '').toLowerCase();
20
- if (termProgram.includes('ghostty')) {
21
- return true;
22
- }
23
- if (termProgram.includes('wezterm')) {
24
- return true;
25
- }
26
- if (env.WT_SESSION) {
27
- return true; // Windows Terminal exposes this
28
- }
29
- return false;
30
- }
31
- export function startOscProgress(options = {}) {
32
- const { label = 'Waiting for OpenAI', targetMs = 10 * 60_000, write = (text) => process.stdout.write(text) } = options;
33
- if (!supportsOscProgress(options.env, options.isTty)) {
34
- return () => { };
35
- }
36
- const cleanLabel = sanitizeLabel(label);
37
- const target = Math.max(targetMs, 1_000);
38
- const send = (state, percent) => {
39
- const clamped = Math.max(0, Math.min(100, Math.round(percent)));
40
- write(`${OSC}${state};${clamped};${cleanLabel}${ST}`);
41
- };
42
- const startedAt = Date.now();
43
- send(1, 0); // activate progress bar
44
- const timer = setInterval(() => {
45
- const elapsed = Date.now() - startedAt;
46
- const percent = Math.min(99, (elapsed / target) * 100);
47
- send(1, percent);
48
- }, 900);
49
- timer.unref?.();
50
- let stopped = false;
51
- return () => {
52
- // biome-ignore lint/nursery/noUnnecessaryConditions: multiple callers may try to stop
53
- if (stopped) {
54
- return;
55
- }
56
- stopped = true;
57
- clearInterval(timer);
58
- send(0, 0); // clear the progress bar
59
- };
60
- }
@@ -1,48 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import { DEFAULT_SYSTEM_PROMPT } from './config.js';
3
- import { createFileSections, readFiles } from './files.js';
4
- import { createFsAdapter } from './fsAdapter.js';
5
- export function buildPrompt(basePrompt, files, cwd = process.cwd()) {
6
- if (!files.length) {
7
- return basePrompt;
8
- }
9
- const sections = createFileSections(files, cwd);
10
- const sectionText = sections.map((section) => section.sectionText).join('\n\n');
11
- return `${basePrompt.trim()}\n\n${sectionText}`;
12
- }
13
- export function buildRequestBody({ modelConfig, systemPrompt, userPrompt, searchEnabled, maxOutputTokens, background, storeResponse, }) {
14
- return {
15
- model: modelConfig.model,
16
- instructions: systemPrompt,
17
- input: [
18
- {
19
- role: 'user',
20
- content: [
21
- {
22
- type: 'input_text',
23
- text: userPrompt,
24
- },
25
- ],
26
- },
27
- ],
28
- tools: searchEnabled ? [{ type: 'web_search_preview' }] : undefined,
29
- reasoning: modelConfig.reasoning || undefined,
30
- max_output_tokens: maxOutputTokens,
31
- background: background ? true : undefined,
32
- store: storeResponse ? true : undefined,
33
- };
34
- }
35
- export async function renderPromptMarkdown(options, deps = {}) {
36
- const cwd = deps.cwd ?? process.cwd();
37
- const fsModule = deps.fs ?? createFsAdapter(fs);
38
- const files = await readFiles(options.file ?? [], { cwd, fsModule });
39
- const sections = createFileSections(files, cwd);
40
- const systemPrompt = options.system?.trim() || DEFAULT_SYSTEM_PROMPT;
41
- const userPrompt = (options.prompt ?? '').trim();
42
- const lines = ['[SYSTEM]', systemPrompt, ''];
43
- lines.push('[USER]', userPrompt, '');
44
- sections.forEach((section) => {
45
- lines.push(`[FILE: ${section.displayPath}]`, section.content.trimEnd(), '');
46
- });
47
- return lines.join('\n');
48
- }