@steipete/oracle 0.9.0 → 0.11.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.
- package/LICENSE +1 -1
- package/README.md +107 -49
- package/dist/bin/oracle-cli.js +551 -410
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/archiveConversation.js +224 -0
- package/dist/src/browser/actions/assistantResponse.js +175 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/deepResearch.js +662 -0
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +342 -119
- package/dist/src/browser/actions/navigation.js +183 -137
- package/dist/src/browser/actions/projectSources.js +491 -0
- package/dist/src/browser/actions/promptComposer.js +152 -91
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingStatus.js +391 -0
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/artifacts.js +150 -0
- package/dist/src/browser/attachRunning.js +31 -0
- package/dist/src/browser/chatgptImages.js +315 -0
- package/dist/src/browser/chromeLifecycle.js +276 -63
- package/dist/src/browser/config.js +59 -16
- package/dist/src/browser/constants.js +25 -12
- package/dist/src/browser/controlPlan.js +81 -0
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +250 -77
- package/dist/src/browser/domDebug.js +50 -1
- package/dist/src/browser/index.js +1559 -692
- package/dist/src/browser/liveTabs.js +434 -0
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +127 -42
- package/dist/src/browser/projectSourcesRunner.js +366 -0
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +1 -1
- package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
- package/dist/src/browser/providers/index.js +2 -2
- package/dist/src/browser/reattach.js +178 -73
- package/dist/src/browser/reattachHelpers.js +32 -27
- package/dist/src/browser/sessionRunner.js +89 -25
- package/dist/src/browser/tabLeaseRegistry.js +182 -0
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +24 -20
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +102 -48
- package/dist/src/cli/browserDefaults.js +51 -26
- package/dist/src/cli/browserTabs.js +228 -0
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +2 -2
- package/dist/src/cli/dryRun.js +62 -26
- package/dist/src/cli/duplicatePromptGuard.js +12 -4
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +3 -3
- package/dist/src/cli/format.js +2 -2
- package/dist/src/cli/help.js +28 -28
- package/dist/src/cli/hiddenAliases.js +3 -3
- package/dist/src/cli/markdownBundle.js +7 -7
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +131 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/projectSources.js +116 -0
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +32 -28
- package/dist/src/cli/sessionCommand.js +82 -21
- package/dist/src/cli/sessionDisplay.js +213 -87
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +149 -95
- package/dist/src/cli/sessionTable.js +26 -23
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +139 -128
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +19 -15
- package/dist/src/gemini-web/client.js +76 -70
- package/dist/src/gemini-web/executionMode.js +6 -8
- package/dist/src/gemini-web/executor.js +98 -93
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/consultPresets.js +19 -0
- package/dist/src/mcp/server.js +18 -12
- package/dist/src/mcp/tools/consult.js +246 -67
- package/dist/src/mcp/tools/projectSources.js +123 -0
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +12 -5
- package/dist/src/mcp/utils.js +21 -8
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +50 -41
- package/dist/src/oracle/config.js +96 -66
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +55 -46
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -33
- package/dist/src/oracle/logging.js +7 -7
- package/dist/src/oracle/markdown.js +28 -28
- package/dist/src/oracle/modelResolver.js +16 -16
- package/dist/src/oracle/multiModelRunner.js +12 -12
- package/dist/src/oracle/oscProgress.js +8 -8
- package/dist/src/oracle/promptAssembly.js +6 -3
- package/dist/src/oracle/request.js +16 -13
- package/dist/src/oracle/run.js +160 -135
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/projectSources/plan.js +27 -0
- package/dist/src/projectSources/url.js +23 -0
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +78 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +67 -62
- package/vendor/oracle-notifier/README.md +2 -0
- package/dist/markdansi/types/index.js +0 -4
- package/dist/oracle/bin/oracle-cli.js +0 -472
- package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
- package/dist/oracle/src/browser/actions/attachments.js +0 -82
- package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
- package/dist/oracle/src/browser/actions/navigation.js +0 -75
- package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
- package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
- package/dist/oracle/src/browser/config.js +0 -33
- package/dist/oracle/src/browser/constants.js +0 -40
- package/dist/oracle/src/browser/cookies.js +0 -210
- package/dist/oracle/src/browser/domDebug.js +0 -36
- package/dist/oracle/src/browser/index.js +0 -331
- package/dist/oracle/src/browser/pageActions.js +0 -5
- package/dist/oracle/src/browser/prompt.js +0 -88
- package/dist/oracle/src/browser/promptSummary.js +0 -20
- package/dist/oracle/src/browser/sessionRunner.js +0 -80
- package/dist/oracle/src/browser/utils.js +0 -62
- package/dist/oracle/src/browserMode.js +0 -1
- package/dist/oracle/src/cli/browserConfig.js +0 -44
- package/dist/oracle/src/cli/dryRun.js +0 -59
- package/dist/oracle/src/cli/engine.js +0 -17
- package/dist/oracle/src/cli/errorUtils.js +0 -9
- package/dist/oracle/src/cli/help.js +0 -70
- package/dist/oracle/src/cli/markdownRenderer.js +0 -15
- package/dist/oracle/src/cli/options.js +0 -103
- package/dist/oracle/src/cli/promptRequirement.js +0 -14
- package/dist/oracle/src/cli/rootAlias.js +0 -30
- package/dist/oracle/src/cli/sessionCommand.js +0 -77
- package/dist/oracle/src/cli/sessionDisplay.js +0 -270
- package/dist/oracle/src/cli/sessionRunner.js +0 -94
- package/dist/oracle/src/heartbeat.js +0 -43
- package/dist/oracle/src/oracle/client.js +0 -48
- package/dist/oracle/src/oracle/config.js +0 -29
- package/dist/oracle/src/oracle/errors.js +0 -101
- package/dist/oracle/src/oracle/files.js +0 -220
- package/dist/oracle/src/oracle/format.js +0 -33
- package/dist/oracle/src/oracle/fsAdapter.js +0 -7
- package/dist/oracle/src/oracle/oscProgress.js +0 -60
- package/dist/oracle/src/oracle/request.js +0 -48
- package/dist/oracle/src/oracle/run.js +0 -444
- package/dist/oracle/src/oracle/tokenStats.js +0 -39
- package/dist/oracle/src/oracle/types.js +0 -1
- package/dist/oracle/src/oracle.js +0 -9
- package/dist/oracle/src/sessionManager.js +0 -205
- package/dist/oracle/src/version.js +0 -39
- package/dist/scripts/chrome/browser-tools.js +0 -295
- package/dist/src/browser/profileSync.js +0 -141
- /package/dist/{oracle/src/browser → src/projectSources}/types.js +0 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { sessionStore } from "../sessionStore.js";
|
|
5
|
+
import { collectChatGptTabs, DEFAULT_REMOTE_CHROME_HOST, DEFAULT_REMOTE_CHROME_PORT, extractConversationIdFromUrl, formatBrowserTabState, harvestChatGptTab, sessionMatchesTab, } from "../browser/liveTabs.js";
|
|
6
|
+
import { resolveOutputPath } from "./writeOutputPath.js";
|
|
7
|
+
const LIVE_POLL_MS = 2000;
|
|
8
|
+
const DEFAULT_STALL_THRESHOLD_MS = 60_000;
|
|
9
|
+
function sessionBrowserEndpoint(meta) {
|
|
10
|
+
const runtime = meta?.browser?.runtime ?? {};
|
|
11
|
+
const remote = meta?.browser?.config?.remoteChrome ?? {};
|
|
12
|
+
const host = runtime.chromeHost ?? remote.host;
|
|
13
|
+
const port = runtime.chromePort ?? remote.port;
|
|
14
|
+
if (!host || !port) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return { host, port };
|
|
18
|
+
}
|
|
19
|
+
function collectUniqueEndpoints(metas) {
|
|
20
|
+
const entries = new Map();
|
|
21
|
+
entries.set(`${DEFAULT_REMOTE_CHROME_HOST}:${DEFAULT_REMOTE_CHROME_PORT}`, {
|
|
22
|
+
host: DEFAULT_REMOTE_CHROME_HOST,
|
|
23
|
+
port: DEFAULT_REMOTE_CHROME_PORT,
|
|
24
|
+
});
|
|
25
|
+
for (const meta of metas) {
|
|
26
|
+
const endpoint = sessionBrowserEndpoint(meta);
|
|
27
|
+
if (!endpoint) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
entries.set(`${endpoint.host}:${endpoint.port}`, endpoint);
|
|
31
|
+
}
|
|
32
|
+
return Array.from(entries.values());
|
|
33
|
+
}
|
|
34
|
+
function buildSessionIndex(metas) {
|
|
35
|
+
return metas
|
|
36
|
+
.filter((meta) => meta?.mode === "browser")
|
|
37
|
+
.sort((left, right) => String(right.createdAt ?? "").localeCompare(String(left.createdAt ?? "")));
|
|
38
|
+
}
|
|
39
|
+
function resolveLinkedSession(tab, metas) {
|
|
40
|
+
return buildSessionIndex(metas).find((meta) => sessionMatchesTab(meta, tab)) ?? null;
|
|
41
|
+
}
|
|
42
|
+
function snippet(text, max = 120) {
|
|
43
|
+
const normalized = String(text ?? "")
|
|
44
|
+
.replace(/\s+/g, " ")
|
|
45
|
+
.trim();
|
|
46
|
+
if (normalized.length <= max) {
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
return `${normalized.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
|
|
50
|
+
}
|
|
51
|
+
function resolveSessionTabRef(meta) {
|
|
52
|
+
const runtime = meta?.browser?.runtime ?? {};
|
|
53
|
+
const harvest = meta?.browser?.harvest ?? {};
|
|
54
|
+
return (harvest.url ??
|
|
55
|
+
runtime.tabUrl ??
|
|
56
|
+
harvest.conversationId ??
|
|
57
|
+
runtime.conversationId ??
|
|
58
|
+
harvest.targetId ??
|
|
59
|
+
runtime.chromeTargetId ??
|
|
60
|
+
"current");
|
|
61
|
+
}
|
|
62
|
+
export function resolveSessionTabRefForTest(meta) {
|
|
63
|
+
return resolveSessionTabRef(meta);
|
|
64
|
+
}
|
|
65
|
+
async function persistHarvest(sessionId, meta, harvested) {
|
|
66
|
+
const hash = createHash("sha1")
|
|
67
|
+
.update(harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "")
|
|
68
|
+
.digest("hex");
|
|
69
|
+
const browser = {
|
|
70
|
+
...(meta.browser ?? {}),
|
|
71
|
+
harvest: {
|
|
72
|
+
targetId: harvested.targetId,
|
|
73
|
+
url: harvested.url,
|
|
74
|
+
conversationId: harvested.conversationId ?? extractConversationIdFromUrl(harvested.url),
|
|
75
|
+
harvestedAt: new Date().toISOString(),
|
|
76
|
+
assistantHash: hash,
|
|
77
|
+
state: harvested.state,
|
|
78
|
+
stopExists: harvested.stopExists,
|
|
79
|
+
sendExists: harvested.sendExists,
|
|
80
|
+
assistantCount: harvested.assistantCount,
|
|
81
|
+
currentModelLabel: harvested.currentModelLabel,
|
|
82
|
+
lastAssistantSnippet: harvested.lastAssistantSnippet,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
await sessionStore.updateSession(sessionId, { browser });
|
|
86
|
+
}
|
|
87
|
+
function printHarvestSummary(sessionId, harvested) {
|
|
88
|
+
console.log(chalk.bold(`Session: ${sessionId}`));
|
|
89
|
+
console.log(`Target: ${harvested.targetId}`);
|
|
90
|
+
console.log(`State: ${formatBrowserTabState(harvested)}`);
|
|
91
|
+
console.log(`Model: ${harvested.currentModelLabel || "(unknown)"}`);
|
|
92
|
+
console.log(`URL: ${harvested.url}`);
|
|
93
|
+
console.log(`Assistant turns: ${harvested.assistantCount}`);
|
|
94
|
+
console.log(`Signals: stop=${harvested.stopExists ? "yes" : "no"} send=${harvested.sendExists ? "yes" : "no"}`);
|
|
95
|
+
if (harvested.lastUserSnippet) {
|
|
96
|
+
console.log(`Last user: ${harvested.lastUserSnippet}`);
|
|
97
|
+
}
|
|
98
|
+
console.log(chalk.dim("---"));
|
|
99
|
+
}
|
|
100
|
+
async function maybeWriteHarvestOutput(pathInput, cwd, content) {
|
|
101
|
+
const resolved = resolveOutputPath(pathInput, cwd);
|
|
102
|
+
if (!resolved) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const payload = content ?? "";
|
|
106
|
+
if (resolved === "-" || resolved === "/dev/stdout") {
|
|
107
|
+
process.stdout.write(`${payload}${payload.endsWith("\n") ? "" : "\n"}`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
await fs.writeFile(resolved, payload, "utf8");
|
|
111
|
+
console.log(chalk.dim(`Wrote harvested assistant output to ${resolved}`));
|
|
112
|
+
}
|
|
113
|
+
export async function showBrowserTabsStatus() {
|
|
114
|
+
const metas = await sessionStore.listSessions().catch(() => []);
|
|
115
|
+
const endpoints = collectUniqueEndpoints(metas);
|
|
116
|
+
let printedAny = false;
|
|
117
|
+
for (const endpoint of endpoints) {
|
|
118
|
+
let tabs;
|
|
119
|
+
try {
|
|
120
|
+
tabs = await collectChatGptTabs(endpoint);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (tabs.length === 0) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
printedAny = true;
|
|
129
|
+
console.log(chalk.bold(`Browser Tabs ${endpoint.host}:${endpoint.port}`));
|
|
130
|
+
for (const tab of tabs) {
|
|
131
|
+
const linkedSession = resolveLinkedSession({ ...tab, host: endpoint.host, port: endpoint.port }, metas);
|
|
132
|
+
console.log(`- ${tab.targetId} ${formatBrowserTabState(tab)} model=${tab.currentModelLabel || "(unknown)"} turns=${tab.assistantCount} stop=${tab.stopExists ? "yes" : "no"} send=${tab.sendExists ? "yes" : "no"}`);
|
|
133
|
+
console.log(` title=${tab.title || "(untitled)"}`);
|
|
134
|
+
console.log(` url=${tab.url}`);
|
|
135
|
+
if (linkedSession) {
|
|
136
|
+
console.log(` session=${linkedSession.id}`);
|
|
137
|
+
}
|
|
138
|
+
if (tab.lastAssistantSnippet) {
|
|
139
|
+
console.log(` last=${snippet(tab.lastAssistantSnippet)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!printedAny) {
|
|
144
|
+
console.log("No live ChatGPT tabs found on known Chrome DevTools endpoints.");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export async function harvestSessionBrowserOutput(sessionId, options = {}) {
|
|
148
|
+
const meta = await sessionStore.readSession(sessionId);
|
|
149
|
+
if (!meta) {
|
|
150
|
+
throw new Error(`No session found with ID ${sessionId}.`);
|
|
151
|
+
}
|
|
152
|
+
const endpoint = sessionBrowserEndpoint(meta) ?? {
|
|
153
|
+
host: DEFAULT_REMOTE_CHROME_HOST,
|
|
154
|
+
port: DEFAULT_REMOTE_CHROME_PORT,
|
|
155
|
+
};
|
|
156
|
+
const harvested = await harvestChatGptTab({
|
|
157
|
+
host: endpoint.host,
|
|
158
|
+
port: endpoint.port,
|
|
159
|
+
ref: options.browserTabRef ?? resolveSessionTabRef(meta),
|
|
160
|
+
stallWindowMs: options.stallWindowMs,
|
|
161
|
+
});
|
|
162
|
+
await persistHarvest(sessionId, meta, harvested);
|
|
163
|
+
printHarvestSummary(sessionId, harvested);
|
|
164
|
+
const output = harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "";
|
|
165
|
+
if (options.writeOutputPath) {
|
|
166
|
+
await maybeWriteHarvestOutput(options.writeOutputPath, meta.cwd ?? process.cwd(), output);
|
|
167
|
+
}
|
|
168
|
+
if (!options.quietOutput && output) {
|
|
169
|
+
process.stdout.write(`${output}${output.endsWith("\n") ? "" : "\n"}`);
|
|
170
|
+
}
|
|
171
|
+
return harvested;
|
|
172
|
+
}
|
|
173
|
+
export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
|
|
174
|
+
const meta = await sessionStore.readSession(sessionId);
|
|
175
|
+
if (!meta) {
|
|
176
|
+
throw new Error(`No session found with ID ${sessionId}.`);
|
|
177
|
+
}
|
|
178
|
+
const endpoint = sessionBrowserEndpoint(meta) ?? {
|
|
179
|
+
host: DEFAULT_REMOTE_CHROME_HOST,
|
|
180
|
+
port: DEFAULT_REMOTE_CHROME_PORT,
|
|
181
|
+
};
|
|
182
|
+
const browserTabRef = options.browserTabRef ?? resolveSessionTabRef(meta);
|
|
183
|
+
const stallThresholdMs = options.stallThresholdMs ?? DEFAULT_STALL_THRESHOLD_MS;
|
|
184
|
+
let lastHash = null;
|
|
185
|
+
let unchangedSince = Date.now();
|
|
186
|
+
while (true) {
|
|
187
|
+
const harvested = await harvestChatGptTab({
|
|
188
|
+
host: endpoint.host,
|
|
189
|
+
port: endpoint.port,
|
|
190
|
+
ref: browserTabRef,
|
|
191
|
+
});
|
|
192
|
+
const fullText = harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "";
|
|
193
|
+
const hash = createHash("sha1").update(fullText).digest("hex");
|
|
194
|
+
if (hash !== lastHash) {
|
|
195
|
+
lastHash = hash;
|
|
196
|
+
unchangedSince = Date.now();
|
|
197
|
+
const statusLine = `[${new Date().toISOString()}] state=${harvested.state} stop=${harvested.stopExists ? "yes" : "no"} ` +
|
|
198
|
+
`send=${harvested.sendExists ? "yes" : "no"} model=${harvested.currentModelLabel || "(unknown)"} ` +
|
|
199
|
+
`snippet=${snippet(harvested.lastAssistantSnippet || fullText, 160)}`;
|
|
200
|
+
console.log(statusLine);
|
|
201
|
+
await persistHarvest(sessionId, meta, harvested);
|
|
202
|
+
}
|
|
203
|
+
const derivedState = harvested.stopExists
|
|
204
|
+
? Date.now() - unchangedSince >= stallThresholdMs
|
|
205
|
+
? "stalled"
|
|
206
|
+
: "running"
|
|
207
|
+
: harvested.authenticated
|
|
208
|
+
? "completed"
|
|
209
|
+
: "detached";
|
|
210
|
+
if (derivedState === "completed" || derivedState === "stalled" || derivedState === "detached") {
|
|
211
|
+
const finalHarvest = {
|
|
212
|
+
...harvested,
|
|
213
|
+
state: derivedState,
|
|
214
|
+
};
|
|
215
|
+
await persistHarvest(sessionId, meta, finalHarvest);
|
|
216
|
+
printHarvestSummary(sessionId, finalHarvest);
|
|
217
|
+
const output = finalHarvest.lastAssistantMarkdown ?? finalHarvest.lastAssistantText ?? "";
|
|
218
|
+
if (options.writeOutputPath) {
|
|
219
|
+
await maybeWriteHarvestOutput(options.writeOutputPath, meta.cwd ?? process.cwd(), output);
|
|
220
|
+
}
|
|
221
|
+
if (output) {
|
|
222
|
+
process.stdout.write(`${output}${output.endsWith("\n") ? "" : "\n"}`);
|
|
223
|
+
}
|
|
224
|
+
return finalHarvest;
|
|
225
|
+
}
|
|
226
|
+
await new Promise((resolve) => setTimeout(resolve, LIVE_POLL_MS));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
async function loadClipboard() {
|
|
2
|
+
if (process.platform === "darwin" && process.arch === "x64") {
|
|
3
|
+
const paths = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
4
|
+
if (!paths.includes("/usr/sbin")) {
|
|
5
|
+
process.env.PATH = ["/usr/sbin", ...paths].join(":");
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return (await import("clipboardy")).default;
|
|
9
|
+
}
|
|
2
10
|
export async function copyToClipboard(text) {
|
|
3
11
|
try {
|
|
12
|
+
const clipboard = await loadClipboard();
|
|
4
13
|
await clipboard.write(text);
|
|
5
|
-
return { success: true, command:
|
|
14
|
+
return { success: true, command: "clipboardy" };
|
|
6
15
|
}
|
|
7
16
|
catch (error) {
|
|
8
17
|
return { success: false, error };
|
package/dist/src/cli/detach.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isProModel } from
|
|
1
|
+
import { isProModel } from "../oracle/modelResolver.js";
|
|
2
2
|
export function shouldDetachSession({
|
|
3
3
|
// Params kept for policy tweaks.
|
|
4
4
|
engine, model, waitPreference, disableDetachEnv, }) {
|
|
@@ -8,7 +8,7 @@ engine, model, waitPreference, disableDetachEnv, }) {
|
|
|
8
8
|
if (waitPreference)
|
|
9
9
|
return false;
|
|
10
10
|
// Only Pro-tier API runs should start detached by default; browser runs stay inline so failures surface.
|
|
11
|
-
if (isProModel(model) && engine ===
|
|
11
|
+
if (isProModel(model) && engine === "api")
|
|
12
12
|
return true;
|
|
13
13
|
return false;
|
|
14
14
|
}
|
package/dist/src/cli/dryRun.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import { MODEL_CONFIGS, TOKENIZER_OPTIONS, DEFAULT_SYSTEM_PROMPT, buildPrompt, readFiles, getFileTokenStats, printFileTokenStats, } from
|
|
3
|
-
import { isKnownModel } from
|
|
4
|
-
import { assembleBrowserPrompt } from
|
|
5
|
-
import { buildTokenEstimateSuffix, formatAttachmentLabel } from
|
|
6
|
-
import { buildCookiePlan } from
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { MODEL_CONFIGS, TOKENIZER_OPTIONS, DEFAULT_SYSTEM_PROMPT, buildPrompt, readFiles, getFileTokenStats, printFileTokenStats, } from "../oracle.js";
|
|
3
|
+
import { isKnownModel } from "../oracle/modelResolver.js";
|
|
4
|
+
import { assembleBrowserPrompt } from "../browser/prompt.js";
|
|
5
|
+
import { buildTokenEstimateSuffix, formatAttachmentLabel } from "../browser/promptSummary.js";
|
|
6
|
+
import { buildCookiePlan } from "../browser/policies.js";
|
|
7
|
+
import { describeBrowserControlPlan, formatBrowserControlPlan } from "../browser/controlPlan.js";
|
|
7
8
|
export async function runDryRunSummary({ engine, runOptions, cwd, version, log, browserConfig, }, deps = {}) {
|
|
8
|
-
if (engine ===
|
|
9
|
+
if (engine === "browser") {
|
|
9
10
|
await runBrowserDryRun({ runOptions, cwd, version, log, browserConfig }, deps);
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
@@ -15,17 +16,19 @@ async function runApiDryRun({ runOptions, cwd, version, log, }, deps) {
|
|
|
15
16
|
const readFilesImpl = deps.readFilesImpl ?? readFiles;
|
|
16
17
|
const files = await readFilesImpl(runOptions.file ?? [], { cwd });
|
|
17
18
|
const systemPrompt = runOptions.system?.trim() || DEFAULT_SYSTEM_PROMPT;
|
|
18
|
-
const combinedPrompt = buildPrompt(runOptions.prompt ??
|
|
19
|
-
const modelConfig = isKnownModel(runOptions.model)
|
|
19
|
+
const combinedPrompt = buildPrompt(runOptions.prompt ?? "", files, cwd);
|
|
20
|
+
const modelConfig = isKnownModel(runOptions.model)
|
|
21
|
+
? MODEL_CONFIGS[runOptions.model]
|
|
22
|
+
: MODEL_CONFIGS["gpt-5.1"];
|
|
20
23
|
const tokenizer = modelConfig.tokenizer;
|
|
21
24
|
const estimatedInputTokens = tokenizer([
|
|
22
|
-
{ role:
|
|
23
|
-
{ role:
|
|
25
|
+
{ role: "system", content: systemPrompt },
|
|
26
|
+
{ role: "user", content: combinedPrompt },
|
|
24
27
|
], TOKENIZER_OPTIONS);
|
|
25
28
|
const headerLine = `[dry-run] Oracle (${version}) would call ${runOptions.model} with ~${estimatedInputTokens.toLocaleString()} tokens and ${files.length} files.`;
|
|
26
29
|
log(chalk.cyan(headerLine));
|
|
27
30
|
if (files.length === 0) {
|
|
28
|
-
log(chalk.dim(
|
|
31
|
+
log(chalk.dim("[dry-run] No files matched the provided --file patterns."));
|
|
29
32
|
return;
|
|
30
33
|
}
|
|
31
34
|
const inputBudget = runOptions.maxInput ?? modelConfig.inputLimit;
|
|
@@ -38,13 +41,23 @@ async function runApiDryRun({ runOptions, cwd, version, log, }, deps) {
|
|
|
38
41
|
printFileTokenStats(stats, { inputTokenBudget: inputBudget, log });
|
|
39
42
|
}
|
|
40
43
|
async function runBrowserDryRun({ runOptions, cwd, version, log, browserConfig, }, deps) {
|
|
44
|
+
validateBrowserFollowUps(runOptions, browserConfig);
|
|
41
45
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
42
46
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
43
47
|
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
44
48
|
const headerLine = `[dry-run] Oracle (${version}) would launch browser mode (${runOptions.model}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
45
49
|
log(chalk.cyan(headerLine));
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
logBrowserControlPlan(browserConfig, log, "dry-run");
|
|
51
|
+
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "dry-run");
|
|
52
|
+
logBrowserCookieStrategy(browserConfig, log, "dry-run");
|
|
53
|
+
logBrowserArchivePolicy(browserConfig, log, "dry-run");
|
|
54
|
+
logBrowserFileSummary(artifacts, log, "dry-run");
|
|
55
|
+
}
|
|
56
|
+
function logBrowserControlPlan(browserConfig, log, label) {
|
|
57
|
+
const plan = describeBrowserControlPlan(browserConfig);
|
|
58
|
+
for (const line of formatBrowserControlPlan(plan, label)) {
|
|
59
|
+
log(chalk.dim(line));
|
|
60
|
+
}
|
|
48
61
|
}
|
|
49
62
|
function logBrowserCookieStrategy(browserConfig, log, label) {
|
|
50
63
|
if (!browserConfig)
|
|
@@ -52,9 +65,15 @@ function logBrowserCookieStrategy(browserConfig, log, label) {
|
|
|
52
65
|
const plan = buildCookiePlan(browserConfig);
|
|
53
66
|
log(chalk.bold(`[${label}] ${plan.description}`));
|
|
54
67
|
}
|
|
68
|
+
function logBrowserArchivePolicy(browserConfig, log, label) {
|
|
69
|
+
const mode = browserConfig?.archiveConversations ?? "auto";
|
|
70
|
+
log(chalk.dim(`[${label}] ChatGPT archive policy: ${mode}.`));
|
|
71
|
+
}
|
|
55
72
|
function logBrowserFileSummary(artifacts, log, label) {
|
|
56
73
|
if (artifacts.attachments.length > 0) {
|
|
57
|
-
const prefix = artifacts.bundled
|
|
74
|
+
const prefix = artifacts.bundled
|
|
75
|
+
? `[${label}] Bundled upload:`
|
|
76
|
+
: `[${label}] Attachments to upload:`;
|
|
58
77
|
log(chalk.bold(prefix));
|
|
59
78
|
artifacts.attachments.forEach((attachment) => {
|
|
60
79
|
log(` • ${formatAttachmentLabel(attachment)}`);
|
|
@@ -66,19 +85,22 @@ function logBrowserFileSummary(artifacts, log, label) {
|
|
|
66
85
|
}
|
|
67
86
|
if (artifacts.inlineFileCount > 0) {
|
|
68
87
|
log(chalk.bold(`[${label}] Inline file content:`));
|
|
69
|
-
log(` • ${artifacts.inlineFileCount} file${artifacts.inlineFileCount === 1 ?
|
|
88
|
+
log(` • ${artifacts.inlineFileCount} file${artifacts.inlineFileCount === 1 ? "" : "s"} pasted directly into the composer.`);
|
|
70
89
|
return;
|
|
71
90
|
}
|
|
72
91
|
log(chalk.dim(`[${label}] No files attached.`));
|
|
73
92
|
}
|
|
74
|
-
export async function runBrowserPreview({ runOptions, cwd, version, previewMode, log, }, deps = {}) {
|
|
93
|
+
export async function runBrowserPreview({ runOptions, cwd, version, previewMode, log, browserConfig, }, deps = {}) {
|
|
94
|
+
validateBrowserFollowUps(runOptions, browserConfig);
|
|
75
95
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
76
96
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
77
97
|
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
78
98
|
const headerLine = `[preview] Oracle (${version}) browser mode (${runOptions.model}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
79
99
|
log(chalk.cyan(headerLine));
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
logBrowserControlPlan(browserConfig, log, "preview");
|
|
101
|
+
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "preview");
|
|
102
|
+
logBrowserFileSummary(artifacts, log, "preview");
|
|
103
|
+
if (previewMode === "json" || previewMode === "full") {
|
|
82
104
|
const attachmentSummary = artifacts.attachments.map((attachment) => ({
|
|
83
105
|
path: attachment.path,
|
|
84
106
|
displayPath: attachment.displayPath,
|
|
@@ -86,20 +108,34 @@ export async function runBrowserPreview({ runOptions, cwd, version, previewMode,
|
|
|
86
108
|
}));
|
|
87
109
|
const previewPayload = {
|
|
88
110
|
model: runOptions.model,
|
|
89
|
-
engine:
|
|
111
|
+
engine: "browser",
|
|
90
112
|
composerText: artifacts.composerText,
|
|
91
113
|
attachments: attachmentSummary,
|
|
92
114
|
inlineFileCount: artifacts.inlineFileCount,
|
|
93
115
|
bundled: artifacts.bundled,
|
|
94
116
|
tokenEstimate: artifacts.estimatedInputTokens,
|
|
117
|
+
browserFollowUps: runOptions.browserFollowUps ?? [],
|
|
95
118
|
};
|
|
96
|
-
log(
|
|
97
|
-
log(chalk.bold(
|
|
119
|
+
log("");
|
|
120
|
+
log(chalk.bold("Preview JSON"));
|
|
98
121
|
log(JSON.stringify(previewPayload, null, 2));
|
|
99
122
|
}
|
|
100
|
-
if (previewMode ===
|
|
101
|
-
log(
|
|
102
|
-
log(chalk.bold(
|
|
103
|
-
log(artifacts.composerText || chalk.dim(
|
|
123
|
+
if (previewMode === "full") {
|
|
124
|
+
log("");
|
|
125
|
+
log(chalk.bold("Composer Text"));
|
|
126
|
+
log(artifacts.composerText || chalk.dim("(empty prompt)"));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function logBrowserFollowUpSummary(followUps, log, label) {
|
|
130
|
+
const count = followUps?.filter((entry) => entry.trim().length > 0).length ?? 0;
|
|
131
|
+
if (count > 0) {
|
|
132
|
+
log(chalk.bold(`[${label}] Browser follow-ups: ${count} additional prompt(s).`));
|
|
133
|
+
log(chalk.dim(`[${label}] Multi-turn is explicit only: Oracle will send these prompts in order, but it never invents follow-ups automatically.`));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function validateBrowserFollowUps(runOptions, browserConfig) {
|
|
137
|
+
const followUpCount = runOptions.browserFollowUps?.filter((entry) => entry.trim().length > 0).length ?? 0;
|
|
138
|
+
if (followUpCount > 0 && browserConfig?.researchMode === "deep") {
|
|
139
|
+
throw new Error("Browser follow-ups are not supported with Deep Research mode. Put the full research plan into the initial prompt or run a normal browser consult for multi-turn review.");
|
|
104
140
|
}
|
|
105
141
|
}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
function normalizeRunSignature(prompt, browserFollowUps) {
|
|
3
|
+
const followUps = (browserFollowUps ?? [])
|
|
4
|
+
.map((entry) => entry.trim())
|
|
5
|
+
.filter(Boolean)
|
|
6
|
+
.join("\n\n--- browser follow-up ---\n\n");
|
|
7
|
+
return [prompt.trim(), followUps].filter(Boolean).join("\n\n--- browser follow-ups ---\n\n");
|
|
8
|
+
}
|
|
9
|
+
export async function shouldBlockDuplicatePrompt({ prompt, browserFollowUps, force, sessionStore, log = console.log, }) {
|
|
3
10
|
if (force)
|
|
4
11
|
return false;
|
|
5
12
|
const normalized = prompt?.trim();
|
|
6
13
|
if (!normalized)
|
|
7
14
|
return false;
|
|
8
|
-
const
|
|
9
|
-
const
|
|
15
|
+
const signature = normalizeRunSignature(normalized, browserFollowUps);
|
|
16
|
+
const running = (await sessionStore.listSessions()).filter((entry) => entry.status === "running");
|
|
17
|
+
const duplicate = running.find((entry) => normalizeRunSignature(entry.options?.prompt?.trim?.() ?? "", entry.options?.browserFollowUps) === signature);
|
|
10
18
|
if (!duplicate)
|
|
11
19
|
return false;
|
|
12
20
|
log(chalk.yellow(`A session with the same prompt is already running (${duplicate.id}). Reattach with "oracle session ${duplicate.id}" or rerun with --force to start another run.`));
|
package/dist/src/cli/engine.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { isProModel } from
|
|
1
|
+
import { isProModel } from "../oracle/modelResolver.js";
|
|
2
2
|
export function defaultWaitPreference(model, engine) {
|
|
3
3
|
// Pro-class API runs can take a long time; prefer non-blocking unless explicitly overridden.
|
|
4
|
-
if (engine ===
|
|
4
|
+
if (engine === "api" && isProModel(model)) {
|
|
5
5
|
return false;
|
|
6
6
|
}
|
|
7
7
|
return true; // browser or non-pro models are fast enough to block by default
|
|
@@ -17,7 +17,7 @@ export function defaultWaitPreference(model, engine) {
|
|
|
17
17
|
*/
|
|
18
18
|
export function resolveEngine({ engine, browserFlag, env, }) {
|
|
19
19
|
if (browserFlag) {
|
|
20
|
-
return
|
|
20
|
+
return "browser";
|
|
21
21
|
}
|
|
22
22
|
if (engine) {
|
|
23
23
|
return engine;
|
|
@@ -26,16 +26,16 @@ export function resolveEngine({ engine, browserFlag, env, }) {
|
|
|
26
26
|
if (envEngine) {
|
|
27
27
|
return envEngine;
|
|
28
28
|
}
|
|
29
|
-
return env.OPENAI_API_KEY ?
|
|
29
|
+
return env.OPENAI_API_KEY ? "api" : "browser";
|
|
30
30
|
}
|
|
31
31
|
function normalizeEngineMode(raw) {
|
|
32
|
-
if (typeof raw !==
|
|
32
|
+
if (typeof raw !== "string") {
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
const normalized = raw.trim().toLowerCase();
|
|
36
|
-
if (normalized ===
|
|
37
|
-
return
|
|
38
|
-
if (normalized ===
|
|
39
|
-
return
|
|
36
|
+
if (normalized === "api")
|
|
37
|
+
return "api";
|
|
38
|
+
if (normalized === "browser")
|
|
39
|
+
return "browser";
|
|
40
40
|
return null;
|
|
41
41
|
}
|
package/dist/src/cli/fileSize.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { normalizeMaxFileSizeBytes } from
|
|
1
|
+
import { normalizeMaxFileSizeBytes } from "../oracle/files.js";
|
|
2
2
|
export function resolveConfiguredMaxFileSizeBytes(userConfig, env = process.env) {
|
|
3
3
|
const envValue = env.ORACLE_MAX_FILE_SIZE_BYTES?.trim();
|
|
4
4
|
if (envValue) {
|
|
5
|
-
return normalizeMaxFileSizeBytes(envValue,
|
|
5
|
+
return normalizeMaxFileSizeBytes(envValue, "ORACLE_MAX_FILE_SIZE_BYTES");
|
|
6
6
|
}
|
|
7
7
|
if (userConfig?.maxFileSizeBytes !== undefined) {
|
|
8
|
-
return normalizeMaxFileSizeBytes(userConfig.maxFileSizeBytes,
|
|
8
|
+
return normalizeMaxFileSizeBytes(userConfig.maxFileSizeBytes, "config.maxFileSizeBytes");
|
|
9
9
|
}
|
|
10
10
|
return undefined;
|
|
11
11
|
}
|
package/dist/src/cli/format.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export function formatCompactNumber(value) {
|
|
2
2
|
if (Number.isNaN(value) || !Number.isFinite(value))
|
|
3
|
-
return
|
|
3
|
+
return "0";
|
|
4
4
|
const abs = Math.abs(value);
|
|
5
|
-
const stripTrailingZero = (text) => text.replace(/\.0$/,
|
|
5
|
+
const stripTrailingZero = (text) => text.replace(/\.0$/, "");
|
|
6
6
|
if (abs >= 1_000_000) {
|
|
7
7
|
return `${stripTrailingZero((value / 1_000_000).toFixed(1))}m`;
|
|
8
8
|
}
|
package/dist/src/cli/help.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import kleur from
|
|
1
|
+
import kleur from "kleur";
|
|
2
2
|
const createColorWrapper = (isTty) => (styler) => (text) => isTty ? styler(text) : text;
|
|
3
3
|
export function applyHelpStyling(program, version, isTty) {
|
|
4
4
|
const wrap = createColorWrapper(isTty);
|
|
@@ -34,44 +34,44 @@ export function applyHelpStyling(program, version, isTty) {
|
|
|
34
34
|
return colors.argument(text);
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
|
-
program.addHelpText(
|
|
38
|
-
program.addHelpText(
|
|
37
|
+
program.addHelpText("beforeAll", () => renderHelpBanner(version, colors));
|
|
38
|
+
program.addHelpText("after", () => renderHelpFooter(program, colors));
|
|
39
39
|
}
|
|
40
40
|
function renderHelpBanner(version, colors) {
|
|
41
|
-
const subtitle =
|
|
41
|
+
const subtitle = "Prompt + files required — GPT-5.5 Pro/GPT-5.5 for tough questions with code/file context.";
|
|
42
42
|
return `${colors.banner(`Oracle CLI v${version}`)} ${colors.subtitle(`— ${subtitle}`)}\n`;
|
|
43
43
|
}
|
|
44
44
|
function renderHelpFooter(program, colors) {
|
|
45
45
|
const tips = [
|
|
46
|
-
`${colors.bullet(
|
|
47
|
-
`${colors.bullet(
|
|
48
|
-
`${colors.bullet(
|
|
49
|
-
`${colors.bullet(
|
|
50
|
-
`${colors.bullet(
|
|
51
|
-
`${colors.bullet(
|
|
52
|
-
`${colors.bullet(
|
|
53
|
-
`${colors.bullet(
|
|
54
|
-
`${colors.bullet(
|
|
55
|
-
`${colors.bullet(
|
|
56
|
-
`${colors.bullet(
|
|
57
|
-
`${colors.bullet(
|
|
58
|
-
`${colors.bullet(
|
|
59
|
-
`${colors.bullet(
|
|
60
|
-
].join(
|
|
46
|
+
`${colors.bullet("•")} Required: always pass a prompt AND ${colors.accent("--file …")} (directories/globs are fine); Oracle cannot see your project otherwise.`,
|
|
47
|
+
`${colors.bullet("•")} Attach lots of source (whole directories beat single files) and keep total input under ~196k tokens.`,
|
|
48
|
+
`${colors.bullet("•")} Oracle starts empty—open with a short project briefing (stack, services, build steps), spell out the question and prior attempts, and why it matters; the more explanation and context you provide, the better the response will be.`,
|
|
49
|
+
`${colors.bullet("•")} Spell out the project + platform + version requirements (repo name, target OS/toolchain versions, API dependencies) so Oracle doesn’t guess defaults.`,
|
|
50
|
+
`${colors.bullet("•")} When comparing multiple repos/files, spell out each repo + path + role (e.g., “Project A SettingsView → apps/project-a/Sources/SettingsView.swift; Project B SettingsView → ../project-b/mac/...”) so the model knows exactly which file is which.`,
|
|
51
|
+
`${colors.bullet("•")} Best results: 6–30 sentences plus key source files; very short prompts often yield generic answers.`,
|
|
52
|
+
`${colors.bullet("•")} Oracle is one-shot by default. API runs can continue with ${colors.accent("--followup <sessionId|responseId>")}; browser runs can ask sequential ChatGPT turns with repeated ${colors.accent("--browser-follow-up")}.`,
|
|
53
|
+
`${colors.bullet("•")} Run ${colors.accent("--files-report")} to inspect token spend before hitting the API.`,
|
|
54
|
+
`${colors.bullet("•")} Non-preview runs spawn detached sessions (especially gpt-5.5-pro API). If the CLI times out, do not re-run — reattach with ${colors.accent("oracle session <slug>")} to resume/inspect the existing run.`,
|
|
55
|
+
`${colors.bullet("•")} Set a memorable 3–5 word slug via ${colors.accent('--slug "<words>"')} to keep session IDs tidy.`,
|
|
56
|
+
`${colors.bullet("•")} Finished sessions auto-hide preamble logs when reattached; raw timestamps remain in the saved log file.`,
|
|
57
|
+
`${colors.bullet("•")} Need hidden flags? Run ${colors.accent(`${program.name()} --help --verbose`)} to list search/token/browser overrides.`,
|
|
58
|
+
`${colors.bullet("•")} If any Oracle session is already running, do not start new API runs. Attach to the existing browser session instead; only trigger API calls when you explicitly mean to.`,
|
|
59
|
+
`${colors.bullet("•")} Duplicate prompt guard: if the same prompt is already running, new runs are blocked unless you pass ${colors.accent("--force")}—prefer reattaching instead of spawning duplicates.`,
|
|
60
|
+
].join("\n");
|
|
61
61
|
const formatExample = (command, description) => `${colors.command(` ${command}`)}\n${colors.muted(` ${description}`)}`;
|
|
62
62
|
const examples = [
|
|
63
|
-
formatExample(`${program.name()} --render --copy --prompt "Review the TS data layer for schema drift" --file "src/**/*.ts,*/*.test.ts"`,
|
|
64
|
-
formatExample(`${program.name()} --prompt "Cross-check the data layer assumptions" --models gpt-5.2-pro,gemini-3-pro --file "src/**/*.ts"`,
|
|
65
|
-
formatExample(`${program.name()} status --hours 72 --limit 50`,
|
|
66
|
-
formatExample(`${program.name()} session <sessionId>`,
|
|
67
|
-
formatExample(`${program.name()} --prompt "Ship review" --slug "release-readiness-audit"`,
|
|
68
|
-
formatExample(`${program.name()} --prompt "Tabs frozen: compare Project A SettingsView (apps/project-a/Sources/SettingsView.swift) vs Project B SettingsView (../project-b/mac/App/Presentation/Views/SettingsView.swift)" --file apps/project-a/Sources/SettingsView.swift --file ../project-b/mac/App/Presentation/Views/SettingsView.swift`,
|
|
69
|
-
].join(
|
|
63
|
+
formatExample(`${program.name()} --render --copy --prompt "Review the TS data layer for schema drift" --file "src/**/*.ts,*/*.test.ts"`, "Build the bundle, print it, and copy it for manual paste into ChatGPT."),
|
|
64
|
+
formatExample(`${program.name()} --prompt "Cross-check the data layer assumptions" --models gpt-5.2-pro,gemini-3-pro --file "src/**/*.ts"`, "Run multiple API models in one go and aggregate cost/usage."),
|
|
65
|
+
formatExample(`${program.name()} status --hours 72 --limit 50`, "Show sessions from the last 72h (capped at 50 entries)."),
|
|
66
|
+
formatExample(`${program.name()} session <sessionId>`, "Attach to a running/completed session and stream the saved transcript."),
|
|
67
|
+
formatExample(`${program.name()} --prompt "Ship review" --slug "release-readiness-audit"`, "Encourage the model to hand you a 3–5 word slug and pass it along with --slug."),
|
|
68
|
+
formatExample(`${program.name()} --prompt "Tabs frozen: compare Project A SettingsView (apps/project-a/Sources/SettingsView.swift) vs Project B SettingsView (../project-b/mac/App/Presentation/Views/SettingsView.swift)" --file apps/project-a/Sources/SettingsView.swift --file ../project-b/mac/App/Presentation/Views/SettingsView.swift`, "Spell out what each attached file is (repo + path + role) before asking for comparisons so the model knows exactly what it is reading."),
|
|
69
|
+
].join("\n\n");
|
|
70
70
|
return `
|
|
71
|
-
${colors.section(
|
|
71
|
+
${colors.section("Tips")}
|
|
72
72
|
${tips}
|
|
73
73
|
|
|
74
|
-
${colors.section(
|
|
74
|
+
${colors.section("Examples")}
|
|
75
75
|
${examples}
|
|
76
76
|
`;
|
|
77
77
|
}
|
|
@@ -9,14 +9,14 @@ export function applyHiddenAliases(options, setOptionValue) {
|
|
|
9
9
|
if (options.include && options.include.length > 0) {
|
|
10
10
|
const mergedFiles = [...(options.file ?? []), ...options.include];
|
|
11
11
|
options.file = mergedFiles;
|
|
12
|
-
setOptionValue?.(
|
|
12
|
+
setOptionValue?.("file", mergedFiles);
|
|
13
13
|
}
|
|
14
14
|
if (!options.prompt && options.message) {
|
|
15
15
|
options.prompt = options.message;
|
|
16
|
-
setOptionValue?.(
|
|
16
|
+
setOptionValue?.("prompt", options.message);
|
|
17
17
|
}
|
|
18
18
|
if (!options.engine && options.mode) {
|
|
19
19
|
options.engine = options.mode;
|
|
20
|
-
setOptionValue?.(
|
|
20
|
+
setOptionValue?.("engine", options.mode);
|
|
21
21
|
}
|
|
22
22
|
}
|