ask-pro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.codex-plugin/plugin.json +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +231 -0
  4. package/assets/ask-pro_logo.png +0 -0
  5. package/dist/bin/ask-pro-cli.js +507 -0
  6. package/dist/scripts/run-cli.js +27 -0
  7. package/dist/src/ask-pro/atomicWrite.js +26 -0
  8. package/dist/src/ask-pro/browserRunner.js +796 -0
  9. package/dist/src/ask-pro/responseZip.js +349 -0
  10. package/dist/src/ask-pro/session.js +662 -0
  11. package/dist/src/ask-pro/sessionControllerLease.js +64 -0
  12. package/dist/src/ask-pro/toon.js +26 -0
  13. package/dist/src/ask-pro/zip.js +85 -0
  14. package/dist/src/browser/actions/assistantResponse.js +1245 -0
  15. package/dist/src/browser/actions/attachmentDataTransfer.js +140 -0
  16. package/dist/src/browser/actions/attachments.js +1720 -0
  17. package/dist/src/browser/actions/composerSendReadiness.js +369 -0
  18. package/dist/src/browser/actions/domEvents.js +31 -0
  19. package/dist/src/browser/actions/inputGuard.js +52 -0
  20. package/dist/src/browser/actions/modelPickerDom.js +68 -0
  21. package/dist/src/browser/actions/modelSelection.js +576 -0
  22. package/dist/src/browser/actions/navigation.js +510 -0
  23. package/dist/src/browser/actions/promptComposer.js +824 -0
  24. package/dist/src/browser/actions/remoteFileTransfer.js +37 -0
  25. package/dist/src/browser/actions/thinkingStatus.js +408 -0
  26. package/dist/src/browser/actions/thinkingTime.js +635 -0
  27. package/dist/src/browser/actions/windowState.js +47 -0
  28. package/dist/src/browser/attachRunning.js +31 -0
  29. package/dist/src/browser/chatgptModelCatalog.js +321 -0
  30. package/dist/src/browser/chromeLifecycle.js +807 -0
  31. package/dist/src/browser/config.js +110 -0
  32. package/dist/src/browser/constants.js +85 -0
  33. package/dist/src/browser/cookies.js +191 -0
  34. package/dist/src/browser/detect.js +337 -0
  35. package/dist/src/browser/domDebug.js +72 -0
  36. package/dist/src/browser/errors.js +20 -0
  37. package/dist/src/browser/format.js +16 -0
  38. package/dist/src/browser/index.js +2631 -0
  39. package/dist/src/browser/language.js +97 -0
  40. package/dist/src/browser/liveTabs.js +434 -0
  41. package/dist/src/browser/modelStrategy.js +13 -0
  42. package/dist/src/browser/pageActions.js +5 -0
  43. package/dist/src/browser/profilePaths.js +282 -0
  44. package/dist/src/browser/profileState.js +413 -0
  45. package/dist/src/browser/providerDomFlow.js +17 -0
  46. package/dist/src/browser/providers/chatgptDomProvider.js +50 -0
  47. package/dist/src/browser/reattach.js +534 -0
  48. package/dist/src/browser/reattachHelpers.js +387 -0
  49. package/dist/src/browser/utils.js +122 -0
  50. package/dist/src/browserMode.js +1 -0
  51. package/dist/src/version.js +39 -0
  52. package/package.json +114 -0
  53. package/scripts/refresh-local-plugin.mjs +179 -0
  54. package/scripts/refresh-local-plugin.ps1 +93 -0
  55. package/skills/ask-pro/SKILL.md +181 -0
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const args = parseArgs(process.argv.slice(2));
8
+ const pluginName = args.pluginName ?? "ask-pro";
9
+ const codexHome = args.codexHome ?? process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
10
+ const marketplacePath = args.marketplacePath ?? (await findCodexMarketplace(codexHome, pluginName));
11
+
12
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
13
+ const repoRoot = path.resolve(scriptDir, "..");
14
+ const marketplaceFile = path.resolve(marketplacePath);
15
+ const marketplace = JSON.parse(await fs.readFile(marketplaceFile, "utf8"));
16
+ const marketplaceName = String(marketplace.name ?? "").trim();
17
+ if (!marketplaceName) {
18
+ throw new Error("Marketplace file must include a top-level name.");
19
+ }
20
+
21
+ const plugin = Array.isArray(marketplace.plugins)
22
+ ? marketplace.plugins.find((entry) => entry?.name === pluginName)
23
+ : null;
24
+ if (!plugin) {
25
+ throw new Error(`Plugin '${pluginName}' was not found in ${marketplaceFile}.`);
26
+ }
27
+ if (plugin.source?.source === "local") {
28
+ const configuredSourcePath = plugin.source?.path;
29
+ if (typeof configuredSourcePath !== "string" || !configuredSourcePath.trim()) {
30
+ throw new Error(`Plugin '${pluginName}' local source must include a path.`);
31
+ }
32
+ const configuredSourceRoot = resolveConfiguredSourceRoot(configuredSourcePath, marketplaceFile);
33
+ if (!samePath(configuredSourceRoot, repoRoot)) {
34
+ throw new Error(
35
+ `Plugin '${pluginName}' marketplace path resolves to ${configuredSourceRoot}, not this checkout ${repoRoot}.`,
36
+ );
37
+ }
38
+ } else if (plugin.source?.source !== "url") {
39
+ throw new Error(`Plugin '${pluginName}' must come from a local or URL marketplace source.`);
40
+ }
41
+
42
+ const manifestPath = path.join(repoRoot, ".codex-plugin", "plugin.json");
43
+ const manifest = JSON.parse(await fs.readFile(manifestPath, "utf8"));
44
+ if (manifest.name !== pluginName) {
45
+ throw new Error(
46
+ `Plugin manifest name '${manifest.name}' does not match requested plugin '${pluginName}'.`,
47
+ );
48
+ }
49
+
50
+ const cacheRoot = path.resolve(codexHome, "plugins", "cache");
51
+ const pluginCacheRoot = path.resolve(cacheRoot, marketplaceName, pluginName);
52
+ const cacheVersion = plugin.source?.source === "local" ? "local" : manifest.version;
53
+ if (typeof cacheVersion !== "string" || !cacheVersion.trim()) {
54
+ throw new Error(`Plugin '${pluginName}' manifest must include a version.`);
55
+ }
56
+ const targetRoot = path.resolve(pluginCacheRoot, cacheVersion);
57
+ assertInside(
58
+ pluginCacheRoot,
59
+ cacheRoot,
60
+ "Resolved plugin cache path is outside Codex plugin cache",
61
+ );
62
+
63
+ const requiredDistEntry = path.join(repoRoot, "dist", "bin", "ask-pro-cli.js");
64
+ if (!(await exists(requiredDistEntry))) {
65
+ throw new Error("dist is missing. Run `pnpm run build` before refreshing the plugin cache.");
66
+ }
67
+ const repoNodeModules = path.join(repoRoot, "node_modules");
68
+ if (!(await exists(repoNodeModules))) {
69
+ throw new Error(
70
+ "node_modules is missing. Run `pnpm install` before refreshing the plugin cache.",
71
+ );
72
+ }
73
+
74
+ await removeNodeModulesLink(path.join(targetRoot, "node_modules"));
75
+ await fs.rm(targetRoot, { recursive: true, force: true });
76
+ await fs.mkdir(targetRoot, { recursive: true });
77
+
78
+ for (const item of [
79
+ ".codex-plugin",
80
+ "assets",
81
+ "skills",
82
+ "references",
83
+ "README.md",
84
+ "LICENSE",
85
+ "package.json",
86
+ "pnpm-lock.yaml",
87
+ "pnpm-workspace.yaml",
88
+ "dist",
89
+ ]) {
90
+ const source = path.join(repoRoot, item);
91
+ if (!(await exists(source))) continue;
92
+ await fs.cp(source, path.join(targetRoot, item), { recursive: true, force: true });
93
+ }
94
+
95
+ await fs.mkdir(path.join(targetRoot, "scripts"), { recursive: true });
96
+ await fs.cp(
97
+ path.join(repoRoot, "scripts", "run-cached-cli.mjs"),
98
+ path.join(targetRoot, "scripts", "run-cached-cli.mjs"),
99
+ { force: true },
100
+ );
101
+ await fs.symlink(repoNodeModules, path.join(targetRoot, "node_modules"), "junction");
102
+
103
+ console.log("Refreshed local Codex plugin cache:");
104
+ console.log(` source: ${repoRoot}`);
105
+ console.log(` target: ${targetRoot}`);
106
+ console.log("");
107
+ console.log("Restart or reload Codex to pick up refreshed plugin skills.");
108
+
109
+ function parseArgs(argv) {
110
+ const result = {};
111
+ for (let i = 0; i < argv.length; i += 1) {
112
+ const arg = argv[i];
113
+ const value = argv[i + 1];
114
+ if (!arg.startsWith("--") || !value || value.startsWith("--")) {
115
+ throw new Error(`Expected --name value argument, got '${arg}'.`);
116
+ }
117
+ i += 1;
118
+ if (arg === "--marketplace-path") result.marketplacePath = value;
119
+ else if (arg === "--codex-home") result.codexHome = value;
120
+ else if (arg === "--plugin-name") result.pluginName = value;
121
+ else throw new Error(`Unknown argument '${arg}'.`);
122
+ }
123
+ return result;
124
+ }
125
+
126
+ function assertInside(child, parent, message) {
127
+ const relative = path.relative(parent, child);
128
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
129
+ throw new Error(`${message}: ${child}`);
130
+ }
131
+ }
132
+
133
+ async function exists(filePath) {
134
+ try {
135
+ await fs.access(filePath);
136
+ return true;
137
+ } catch {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ async function removeNodeModulesLink(filePath) {
143
+ try {
144
+ const stat = await fs.lstat(filePath);
145
+ if (stat.isSymbolicLink()) {
146
+ await fs.unlink(filePath);
147
+ }
148
+ } catch {
149
+ // Missing or non-removable links are handled by the full target cleanup.
150
+ }
151
+ }
152
+
153
+ async function findCodexMarketplace(codexHome, pluginName) {
154
+ const codexMarketplace = path.join(
155
+ codexHome,
156
+ ".tmp",
157
+ "marketplaces",
158
+ pluginName,
159
+ ".agents",
160
+ "plugins",
161
+ "marketplace.json",
162
+ );
163
+ if (await exists(codexMarketplace)) return codexMarketplace;
164
+ return path.join(os.homedir(), ".agents", "plugins", "marketplace.json");
165
+ }
166
+
167
+ function resolveConfiguredSourceRoot(sourcePath, marketplaceFile) {
168
+ const candidates = [
169
+ path.resolve(path.dirname(marketplaceFile), sourcePath),
170
+ path.resolve(os.homedir(), sourcePath),
171
+ ];
172
+ return candidates.find((candidate) => samePath(candidate, repoRoot)) ?? candidates[0];
173
+ }
174
+
175
+ function samePath(left, right) {
176
+ const a = path.resolve(left);
177
+ const b = path.resolve(right);
178
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
179
+ }
@@ -0,0 +1,93 @@
1
+ param(
2
+ [string]$MarketplacePath = "",
3
+ [string]$CodexHome = $(if ($env:CODEX_HOME) { $env:CODEX_HOME } else { "$HOME/.codex" }),
4
+ [string]$PluginName = "ask-pro"
5
+ )
6
+
7
+ $ErrorActionPreference = "Stop"
8
+
9
+ function Resolve-StrictPath([string]$Path) {
10
+ return [System.IO.Path]::GetFullPath((Resolve-Path -LiteralPath $Path).Path)
11
+ }
12
+
13
+ function Join-And-Normalize([string]$Base, [string[]]$Parts) {
14
+ return [System.IO.Path]::GetFullPath((Join-Path -Path $Base -ChildPath ([System.IO.Path]::Combine($Parts))))
15
+ }
16
+
17
+ $repoRoot = Resolve-StrictPath (Join-Path $PSScriptRoot "..")
18
+ if ([string]::IsNullOrWhiteSpace($MarketplacePath)) {
19
+ $codexMarketplace = Join-And-Normalize $CodexHome @(".tmp", "marketplaces", $PluginName, ".agents", "plugins", "marketplace.json")
20
+ if (Test-Path -LiteralPath $codexMarketplace) {
21
+ $MarketplacePath = $codexMarketplace
22
+ } else {
23
+ $MarketplacePath = "$HOME/.agents/plugins/marketplace.json"
24
+ }
25
+ }
26
+ $marketplaceFile = Resolve-StrictPath $MarketplacePath
27
+ $marketplace = Get-Content -LiteralPath $marketplaceFile -Raw | ConvertFrom-Json
28
+ $marketplaceName = [string]$marketplace.name
29
+ if ([string]::IsNullOrWhiteSpace($marketplaceName)) {
30
+ throw "Marketplace file must include a top-level name."
31
+ }
32
+
33
+ $plugin = @($marketplace.plugins) | Where-Object { $_.name -eq $PluginName } | Select-Object -First 1
34
+ if (-not $plugin) {
35
+ throw "Plugin '$PluginName' was not found in $marketplaceFile."
36
+ }
37
+ if (($plugin.source.source -ne "local") -and ($plugin.source.source -ne "url")) {
38
+ throw "Plugin '$PluginName' must come from a local or URL marketplace source."
39
+ }
40
+
41
+ $sourceRoot = Resolve-StrictPath $repoRoot
42
+ $manifestPath = Join-Path $sourceRoot ".codex-plugin/plugin.json"
43
+ if (-not (Test-Path -LiteralPath $manifestPath)) {
44
+ throw "Missing plugin manifest at $manifestPath."
45
+ }
46
+
47
+ $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
48
+ if ($manifest.name -ne $PluginName) {
49
+ throw "Plugin manifest name '$($manifest.name)' does not match requested plugin '$PluginName'."
50
+ }
51
+
52
+ $codexHomePath = [System.IO.Path]::GetFullPath($CodexHome)
53
+ $cacheRoot = Join-And-Normalize $codexHomePath @("plugins", "cache")
54
+ $pluginCacheRoot = Join-And-Normalize $cacheRoot @($marketplaceName, $PluginName)
55
+ $cacheVersion = $(if ($plugin.source.source -eq "local") { "local" } else { [string]$manifest.version })
56
+ if ([string]::IsNullOrWhiteSpace($cacheVersion)) {
57
+ throw "Plugin '$PluginName' manifest must include a version."
58
+ }
59
+ $targetRoot = Join-And-Normalize $pluginCacheRoot @($cacheVersion)
60
+
61
+ $cacheRootWithSeparator = $cacheRoot.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + [System.IO.Path]::DirectorySeparatorChar
62
+ if (-not $pluginCacheRoot.StartsWith($cacheRootWithSeparator, [System.StringComparison]::OrdinalIgnoreCase)) {
63
+ throw "Resolved plugin cache path is outside Codex plugin cache: $pluginCacheRoot"
64
+ }
65
+
66
+ if (Test-Path -LiteralPath $targetRoot) {
67
+ Remove-Item -LiteralPath $targetRoot -Recurse -Force
68
+ }
69
+ New-Item -ItemType Directory -Path $targetRoot -Force | Out-Null
70
+
71
+ $itemsToCopy = @(
72
+ ".codex-plugin",
73
+ "skills",
74
+ "references",
75
+ "README.md",
76
+ "LICENSE",
77
+ "package.json",
78
+ "dist"
79
+ )
80
+
81
+ foreach ($item in $itemsToCopy) {
82
+ $source = Join-Path $sourceRoot $item
83
+ if (-not (Test-Path -LiteralPath $source)) {
84
+ continue
85
+ }
86
+ Copy-Item -LiteralPath $source -Destination $targetRoot -Recurse -Force
87
+ }
88
+
89
+ Write-Host "Refreshed local Codex plugin cache:"
90
+ Write-Host " source: $sourceRoot"
91
+ Write-Host " target: $targetRoot"
92
+ Write-Host ""
93
+ Write-Host "Restart or reload Codex to pick up refreshed plugin skills."
@@ -0,0 +1,181 @@
1
+ ---
2
+ name: ask-pro
3
+ description: Escalate hard engineering questions to ChatGPT Pro through browser automation with focused repo context. Use when an agent needs a stronger external review, architecture plan, migration strategy, production-debugging second opinion, or when the user explicitly asks to use $ask-pro.
4
+ ---
5
+
6
+ # $ask-pro
7
+
8
+ Use `$ask-pro` to ask ChatGPT Pro for a focused second opinion on hard engineering work.
9
+
10
+ The calling agent still owns the work. Use Pro for judgment, architecture, risk review, or implementation planning when the decision is consequential enough to justify a browser run.
11
+
12
+ ## Trigger
13
+
14
+ Use this skill when the user explicitly asks for `$ask-pro`, or when a second opinion would materially reduce risk for:
15
+
16
+ - backend architecture
17
+ - schema or data migrations
18
+ - auth, sessions, permissions, or billing
19
+ - queues, workers, idempotency, caching, scaling, or latency
20
+ - production debugging and observability
21
+ - ambiguous implementation paths where a second opinion would reduce risk
22
+
23
+ Do not use it for trivial syntax fixes, formatting, obvious dependency updates, or small bugs with a clear cause.
24
+
25
+ ## Critical Reminders
26
+
27
+ - Long waits are normal. ask-pro can take a super long time to run; do not
28
+ close or kill the browser window/run until at least 3 hours have passed unless
29
+ the CLI has completed, failed, or explicitly asks for human action.
30
+
31
+ ## Workflow
32
+
33
+ When invoked:
34
+
35
+ 1. Inspect the repo and the relevant files.
36
+ 2. Identify the exact decision Pro should answer.
37
+ 3. Choose a small, high-signal file bundle with `--files`.
38
+ 4. Write the prompt yourself using the Prompt Shape below.
39
+ 5. Run the smallest useful command, usually
40
+ `ask-pro --no-temporary --files "<glob>" "<prompt>"` for repo advisories.
41
+ For multiline prompts, write a temporary prompt file and use
42
+ `ask-pro --no-temporary --prompt-file <path> --files "<glob>"`; do not rely
43
+ on shell multiline quoting.
44
+ If installed through the skills CLI and `ask-pro` is not on `PATH`, install
45
+ the standalone CLI from the [README](https://github.com/JJLiebig/ask-pro#skills-cli-codex-and-other-supported-agents)
46
+ first. For Codex plugin installs without `ask-pro` on `PATH`, use the cached
47
+ plugin runner. Locate it under the installed plugin cache,
48
+ usually
49
+ `~/.codex/plugins/cache/<marketplace-name>/ask-pro/<version>/scripts/run-cached-cli.mjs`,
50
+ then call it with:
51
+ `node <cached-runner> -- --cwd <target-repo-root> --no-temporary --prompt-file <path> --files "<repo-relative-glob>"`.
52
+ On Git marketplace installs, the first cached-runner call may bootstrap the
53
+ content-addressed runtime under `$CODEX_HOME/plugin-runtimes/ask-pro/` by
54
+ installing dependencies and building `dist`; wait for that to finish. The
55
+ installed plugin cache stays immutable.
56
+ 6. If auth is required, stop and ask the human to log in in the opened browser.
57
+ 7. Read the CLI's compact `ask_pro` record and run the emitted `resume` or
58
+ `harvest` command when that is the next action.
59
+ 8. Treat the answer as advisory; turn it into your own plan before editing code.
60
+
61
+ `ask-pro` selects `Latest`, then `Pro` intelligence. Do not require a
62
+ different dated model label or a separate thinking-effort control in the prompt
63
+ or workflow.
64
+
65
+ Fresh runs try ChatGPT Temporary Chat by default and automatically fall back to
66
+ normal ChatGPT if the current account/UI does not expose Pro there. For repo
67
+ advisories, large bundles, review rounds, or anything where recovery matters,
68
+ prefer `--no-temporary` from the start. Add `--temporary` only when Temporary
69
+ Chat is required and falling back would be wrong. Temporary Chat is less
70
+ recoverable after browser/tab loss.
71
+
72
+ On Windows, ordinary managed Chrome runs start minimized. First login,
73
+ resume/recovery, and stale-auth paths stay visible or are restored for human
74
+ action. Local managed Chrome guards browser input while Pro is answering.
75
+ If login, MFA, a browser challenge, or incomplete-answer debugging needs human
76
+ attention, ask-pro should restore or retain the browser and emit the next
77
+ action.
78
+
79
+ Do not set `ASK_PRO_AGENT_ID` for ordinary or concurrent use; the shared
80
+ `ask-pro` browser profile under `$CODEX_HOME/state/ask-pro/` handles concurrent
81
+ agents automatically. Set `ASK_PRO_AGENT_ID` only when explicitly testing an
82
+ isolated profile or when the human requests a separate browser login. Use a
83
+ stable reusable lowercase id like `review-t1`, not a one-off task slug, because
84
+ each new id creates a new Chrome profile and may require the human to log in
85
+ again. Example:
86
+ `ASK_PRO_AGENT_ID=review-t1 ask-pro ...`.
87
+
88
+ ## Prompt Shape
89
+
90
+ Assume Pro has no caller or repository context. Include each material fact and
91
+ instruction once: the goal, current state, hard constraints, evidence, success
92
+ criteria, and required output. Do not rely on the agent's conversation context, repo
93
+ folklore, prior ask-pro runs, branch names, or unstated user preferences.
94
+ Keep advisory design consults as plain answer requests. Start advisory prompts
95
+ with:
96
+
97
+ ```text
98
+ Return final Markdown only, with no preamble or implementation package.
99
+ ```
100
+
101
+ The CLI wrapper already tells Pro to read `CONTEXT.zip` and call out missing or
102
+ conflicting context. Do not repeat that instruction. Add task-specific output
103
+ requirements only when needed. For risk or review work, ask Pro to rank
104
+ findings by severity. For architecture or design consults, ask directly for the
105
+ recommendation and tradeoffs.
106
+
107
+ Use `--artifacts` only when the standard implementation package is needed. The
108
+ wrapper supplies the zip name, standard files, and markdown fallback; add only
109
+ task-specific deliverables not covered by that package. Keep advisory consults
110
+ inline by default.
111
+ Keep bundles focused: source files under review, focused tests, relevant docs,
112
+ known recent changes, and validation status. Avoid whole-repo bundles unless the
113
+ question is explicitly architectural.
114
+
115
+ ## Output
116
+
117
+ Normal `ask-pro` stdout is compact TOON-style telemetry. Use `state`, `action`,
118
+ `resume`, and `harvest` to decide the next command. Browser progress may appear
119
+ on stderr and can be ignored unless diagnosing a stuck run.
120
+
121
+ When present, use `profile`, `profile_path`, `chrome`, and `language` only as
122
+ diagnostic hints. They tell you whether the run used the shared profile, an
123
+ isolated agent profile, saved DevTools state, and English browser steering.
124
+ When present, `conversation_url` is a recoverable non-temporary ChatGPT
125
+ conversation URL.
126
+
127
+ `ask-pro --harvest <session-id>` prints the raw markdown answer only for
128
+ answer-bearing states such as `COMPLETED` or `HARVESTED`.
129
+ For pending/incomplete sessions it prints compact status/action instead. For
130
+ sessions run with `--artifacts`, any provided `ask-pro-response.zip` is
131
+ extracted under the session's `pro-output/` directory and described in
132
+ `PRO_OUTPUT_MANIFEST.json`. Inline-default sessions should not expect a zip.
133
+
134
+ `INCOMPLETE_ANSWER` / `stopped_without_answer` means ChatGPT stopped without
135
+ an answer after ask-pro attempted one automatic `continue`. Present the caller
136
+ with the emitted resume command or a full retry of the original request in a new
137
+ chat, and wait for their choice. Do not automatically resume or retry this state.
138
+ An explicit `--resume` allows one more continuation attempt.
139
+
140
+ `COMPLETED` means harvest now; the run browser may already be closed. If the
141
+ state is `INCOMPLETE_ANSWER` / `preamble_without_artifacts`, do not treat
142
+ `ANSWER.md` as final. Try resume/harvest if recoverable; otherwise rerun with
143
+ `--no-temporary`, a tighter bundle, and a more direct prompt.
144
+
145
+ ## Commands
146
+
147
+ ```bash
148
+ ask-pro "Review the async billing webhook migration plan and return an implementation plan."
149
+ ask-pro --no-temporary --prompt-file question.md --files src --files tests
150
+ ask-pro --temporary "Review this sensitive migration plan, and fail if Temporary Chat cannot use Pro."
151
+ ask-pro --no-temporary "Review this in normal ChatGPT instead of Temporary Chat."
152
+ ask-pro --prompt-file question.md --files .\src
153
+ ask-pro --artifacts --prompt-file implementation-plan.md --files src
154
+ ask-pro --files "src/api/stripe/**" --files "prisma/**" --files "src/lib/billing/**" \
155
+ "Review whether this Stripe webhook flow should use a queue or transactional outbox."
156
+ ask-pro --dry-run "Prepare the Pro handoff but do not open the browser."
157
+ ask-pro --resume <session-id>
158
+ ask-pro --harvest <session-id>
159
+ ```
160
+
161
+ If the binary is not on `PATH`, skills CLI installs need the standalone CLI from
162
+ the README. Codex plugin installs can use the cached plugin runner. Do not run
163
+ the plugin from a mutable development checkout; it may contain in-flight changes
164
+ that have not been synced for agents.
165
+
166
+ ```bash
167
+ node <cached-runner> -- --cwd /path/to/repo --no-temporary --prompt-file question.md --files src
168
+ ```
169
+
170
+ In cached-runner fallback mode, `--files` must be inside `--cwd`. Use
171
+ repo-relative `--files`; do not point at files outside the target repo cwd.
172
+
173
+ ## Safety
174
+
175
+ Never ask for, read, store, type, or log passwords, MFA codes, recovery codes, session cookies, or raw auth tokens.
176
+
177
+ Browser auth is human-controlled. Continue only after the human says the ChatGPT composer is visible.
178
+
179
+ After submit, avoid interacting with any retained Chrome run window while Pro is
180
+ thinking. ask-pro guards input after submit, but the safest agent behavior is to
181
+ let the run finish or resume/harvest from CLI telemetry.