Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ ask_pro-specific notes:
- Keep the CLI small: `ask-pro "<question>"`, `--files`, `--prompt-file`,
`--artifacts` / `--response-zip`, `--dry-run`, `--resume`, `--status`,
`--harvest`, `--copy`, `--temporary`, `--no-temporary`, and
`--verbose`.
`--verbose`, and `--github`.
- Browser auth is human-controlled. Never ask for, type, read, or log passwords,
MFA codes, recovery codes, session cookies, or raw auth tokens.
- Browser “Pro thinking” gate: never click or auto-click ChatGPT's `Answer now`
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

### Added

- Add `--github` with a connection check before upload, human setup in the
managed browser, and a resumable handoff. Request read-only repository evidence
and citations while retaining local-file context. The flag does not restrict
the GitHub plugin's actual permissions.
- Add skills CLI installation for Codex and other supported agents using the
standalone `ask-pro` npm CLI.
- Publish `ask-pro` to npm after each passing main-branch CI run.
Expand Down
43 changes: 39 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# ask-pro

`ask-pro` gives coding agents a focused ChatGPT Pro second opinion through a
human-logged-in browser. The calling agent supplies the context and owns the
result; ask-pro never applies generated code.
human-logged-in browser. The calling agent supplies the question and evidence,
and owns the result; ask-pro never applies generated code.

## Install

Expand Down Expand Up @@ -72,8 +72,9 @@ ask-pro --no-temporary --prompt-file question.md --files src --files tests
```

ask-pro selects `Latest` when available, otherwise the highest numbered available
GPT model, with `Pro` intelligence. It has no repo or conversation
context unless you provide it, so keep the prompt and file bundle focused.
GPT model, with `Pro` intelligence. Supply the caller's context explicitly.
Use a focused file bundle for local code; connected GitHub can provide remote
repository context when available (see below).

Request generated files only when needed; treat them as data and never execute
them automatically:
Expand All @@ -87,6 +88,40 @@ Use `ask-pro --help` for the full CLI. For multiline prompts, use
`--prompt-file`. Prefer `--no-temporary` when recovery matters; `--temporary`
requires Temporary Chat. The [skill](skills/ask-pro/SKILL.md) has agent guidance.

## Optional GitHub context

Use `--github` to check the GitHub connection in ask-pro's signed-in ChatGPT
account before sending any prompt or files. It defaults to normal ChatGPT for
recovery; an explicit `--temporary` still requires Temporary Chat.

If GitHub is disconnected or its connection cannot be confirmed, ask-pro opens
GitHub's ChatGPT page and returns `connect_github_then_resume`. Install the
plugin if needed, connect your account yourself, and review repository access.
Select only the repositories you want available; an organization may require
approval. Then run the emitted `--resume` command. It checks the connection again
before submitting. ChatGPT login alone does not pass this GitHub check.

Then explicitly request repository evidence in the prompt:

```powershell
ask-pro --github "Read owner/repo and explain its request authentication flow."
```

Use `--files` for local changes or a precise snapshot that GitHub cannot retrieve.
For committed repository code, prefer `--github` with the repository, ref, paths,
and question; source-file uploads are usually unnecessary when retrieval works.
The flag requests read-only work and citations, but **does not enforce read-only
GitHub permissions**. The live GitHub plugin advertises write capabilities;
review the actual grant even though the [Help Center](https://help.openai.com/en/articles/11145903-connecting-github-to-chatgpt)
describes a read-only app. [Allow read actions](https://help.openai.com/en/articles/20001495-managing-app-permissions-in-chatgpt)
controls approval prompts, not the underlying access grant.

A connected account does not prove access to a particular repository, revision,
or Pro experience. ask-pro does not select an app in the composer, approve app
actions, or verify the answer's sources. The opt-in [GitHub smoke](docs/manual-tests.md#connected-github-context)
checks actual retrieval. GitHub authorization stays with ChatGPT; ask-pro needs
no GitHub token.

## Development

```powershell
Expand Down
34 changes: 33 additions & 1 deletion bin/ask-pro-cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env node
import "dotenv/config";
import { AssistantStoppedError } from "../src/browser/errors.js";
import { GITHUB_SETUP_URL, GitHubConnectionRequiredError } from "../src/browser/actions/github.js";
import fs from "node:fs/promises";
import path from "node:path";
import { Command, Option } from "commander";
Expand Down Expand Up @@ -32,6 +33,7 @@ interface AskProOptions {
files?: string[];
promptFile?: string;
artifacts?: boolean;
github?: boolean;
responseZip?: boolean;
resume?: string | boolean;
status?: string | boolean;
Expand All @@ -54,6 +56,14 @@ program
.option("--prompt-file <path>", "read the question from a UTF-8 file; use - for stdin")
.option("--artifacts", "ask Pro for ask-pro-response.zip plus markdown fallback")
.option("--response-zip", "alias for --artifacts")
.addOption(
new Option("--github", "use connected GitHub; pause for setup before sending").conflicts([
"resume",
"status",
"harvest",
"copy",
]),
)
.option("--resume [session-id]", "resume a prepared or waiting ask-pro session")
.option("--status [session-id]", "show ask-pro session status")
.option("--harvest [session-id]", "print harvested ANSWER.md for a session")
Expand Down Expand Up @@ -157,6 +167,7 @@ async function runAskPro(question: string, options: AskProOptions): Promise<void
}

const dryRun = options.dryRun === true;
if (options.github && options.temporary === undefined) options.temporary = false;
const resolvedQuestion = await resolveQuestion(question, options, cwd);
const artifacts = options.artifacts === true || options.responseZip === true;
const session = await createAskProSession({
Expand All @@ -165,6 +176,7 @@ async function runAskPro(question: string, options: AskProOptions): Promise<void
filePatterns: options.files ?? [],
dryRun,
artifacts,
github: options.github,
});
const resumeCommand = buildResumeCommand(session.id, options, cwd);
const harvestCommand = buildHarvestCommand(session.id, cwd);
Expand Down Expand Up @@ -241,7 +253,8 @@ async function submitOrResumeBrowserSession(
status.status === "WAITING" ||
status.status === "WAIT_TIMED_OUT" ||
status.status === "INCOMPLETE_ANSWER" ||
status.status === "NEEDS_USER_AUTH"
status.status === "NEEDS_USER_AUTH" ||
status.status === "NEEDS_GITHUB_CONNECTION"
) {
try {
await resumeAskProBrowserSession({
Expand All @@ -251,6 +264,11 @@ async function submitOrResumeBrowserSession(
verbose: options.verbose,
});
} catch (error) {
if (error instanceof GitHubConnectionRequiredError) {
const { status: paused } = await readAskProStatus({ cwd, sessionId });
printStatusRecord(paused);
return;
}
if (error instanceof AssistantStoppedError) {
const { status: stopped } = await readAskProStatus({ cwd, sessionId });
printStatusRecord(stopped, await readBrowserPreflight(cwd, stopped));
Expand Down Expand Up @@ -282,6 +300,11 @@ async function submitOrResumeBrowserSession(
...answerExtraForStatus(completed, sessionId),
});
} catch (error) {
if (error instanceof GitHubConnectionRequiredError) {
const { status: paused } = await readAskProStatus({ cwd, sessionId });
printStatusRecord(paused);
return;
}
if (error instanceof AssistantStoppedError) {
const { status: stopped } = await readAskProStatus({ cwd, sessionId });
printStatusRecord(stopped, await readBrowserPreflight(cwd, stopped));
Expand Down Expand Up @@ -386,6 +409,12 @@ function printStatusRecord(status: AskProStatusFile, extra: AskProToonFields = {
state: normalizeState(status.status),
reason: status.reason,
temporary: normalizeTemporary(status.temporary),
github: status.github || undefined,
setup_url: status.status === "NEEDS_GITHUB_CONNECTION" ? GITHUB_SETUP_URL : undefined,
help:
status.status === "NEEDS_GITHUB_CONNECTION"
? "In the opened ChatGPT page, install GitHub if needed, connect your account, review repository access, then resume."
: undefined,
action: actionForStatus(status),
resume: shouldPrintResume(status) ? status.resumeCommand : undefined,
harvest: shouldPrintHarvest(status) ? status.harvestCommand : undefined,
Expand Down Expand Up @@ -427,6 +456,8 @@ function normalizeTemporary(temporary: boolean | undefined): string {
function actionForStatus(status: AskProStatusFile): string {
if (status.reason === "stopped_without_answer") return "choose_resume_or_retry";
switch (status.status) {
case "NEEDS_GITHUB_CONNECTION":
return "connect_github_then_resume";
case "DRY_RUN_COMPLETE":
case "INCOMPLETE_ANSWER":
case "READY_TO_SUBMIT":
Expand All @@ -453,6 +484,7 @@ function shouldPrintResume(status: AskProStatusFile): boolean {
"WAITING",
"READY_TO_SUBMIT",
"NEEDS_USER_AUTH",
"NEEDS_GITHUB_CONNECTION",
"WAIT_TIMED_OUT",
"FAILED",
].includes(status.status);
Expand Down
55 changes: 55 additions & 0 deletions docs/manual-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,48 @@ Expected:
exist.
- `status.json` reports a dry-run/prepared state.

## Connected GitHub Context

This is an opt-in live test. Use the ask-pro browser profile and authorize only
the intended repository. Review the permissions shown by ChatGPT and GitHub;
`--github` requests read-only work but does not restrict the app's permissions.

1. Pick an authorized repository and a file with a specific fact you can verify
independently, such as the behavior of a named function. Record the expected
fact and commit locally, without including the answer in the prompt.
2. Write `github-question.md`, naming `owner/repo`, the path, requested commit,
and question. Ask Pro to retrieve the file through the connected GitHub app,
cite the source and revision actually read, make no changes, and explicitly
report unavailable access or a revision mismatch.
3. Run without `--files`, so the answer cannot come from an uploaded source file:

```powershell
ask-pro --github --prompt-file github-question.md
```

4. With GitHub disconnected or its connection unconfirmed, expect
`NEEDS_GITHUB_CONNECTION`, an opened ChatGPT GitHub setup page, and a resume
command. No prompt or bundle should have been sent. Complete setup manually
after the command exits, then run the printed `--resume` command. Resuming
without completing setup should pause again; completing setup should submit
the original request once. Keep GitHub optional: a new run without `--github`
should still work without a connection. Do not disconnect an existing account
merely to test this path; use a separate profile/account.
5. With GitHub already connected, expect the run to continue without setup.
Both paths use normal ChatGPT by default. Harvest the answer with
`ask-pro --harvest <session-id>`.
6. Compare the answer against the independently recorded file. After the run has
finished, inspect the saved conversation's tool activity for GitHub retrieval
evidence. A plausible answer, a public web search, or `COMPLETED` alone is not
proof of connected-app use. Record the session, model, repository/ref, and
retrieval evidence without credentials or private source contents.

If GitHub is unavailable, requires manual app selection/approval, or cannot read
the requested revision, record the limitation; the CLI does not automate those
steps. Keep Pro selected and start a new consult with `--files` instead. A separate
manual ChatGPT test does not prove ask-pro can invoke the app. Do not interact
with the guarded run window while Pro is answering.

## First Login / Auth Gate

Run a tiny prompt:
Expand Down Expand Up @@ -126,6 +168,19 @@ should report `responseZip.status = "not_requested"`.

## Recent Smoke Runs

- 2026-10-10 - GitHub onboarding trial
`2026-10-09T221451-return-a-short-markdown-answer-with-no-preamble--3f717c89`
paused with `NEEDS_GITHUB_CONNECTION` before upload or submission when the
connection UI was unconfirmed. After human setup, `--resume` passed the check,
selected Pro, and returned the correct migration marker and SHA-256/10-character
hash details from `src/browser/profilePaths.ts` at
`a673c37480ee516d14e8d1f7c395611ae49244a2`. The bundle contained no source files.
The answer reported GitHub file retrieval and the requested revision, but its
citation link was absent from the harvested Markdown and the activity panel
was not independently inspected. This proves the setup handoff/resume and
answer accuracy, not a verified tool trace or a definitively disconnected
initial account. Final recovery-only fixes were covered by the owning tests
and CI after this live run started.
- 2026-07-10 - `2026-07-10T145226-return-exactly-one-line-and-nothing-else-ask-pro-d95f47cc`
selected `GPT-5.6 Sol`, confirmed `Pro` intelligence, uploaded the README
context bundle, harvested `ASK_PRO_GPT56_SOL_PRO_OK`, and completed browser
Expand Down
7 changes: 7 additions & 0 deletions docs/windows-work.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ Read this when working on `ask_pro` from Windows and add new findings here.
follows a replacement DevTools port recorded by the managed profile.
Challenge recovery requires a visible active challenge control or surface;
queued composer text and class or ID substrings do not trigger it.
- `--github` checks the connection in the managed profile after ChatGPT login
and before any upload/submission. Missing or unconfirmed connections restore
Chrome to the GitHub setup page and return `connect_github_then_resume`.
The controller exits before human OAuth; resume rechecks the connection and
starts the unsent prompt, instead of harvesting an unrelated conversation.
The regular Chrome account can differ from ask-pro's account. Do not copy
cookies or GitHub tokens between profiles.
- Chrome DevTools state is recorded in each session's `browser.json`; use the
saved port for DOM inspection when a live browser needs debugging.
- Concurrent fresh and resumed runs on one managed profile use PID-backed
Expand Down
52 changes: 48 additions & 4 deletions skills/ask-pro/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ When invoked:

1. Inspect the repo and the relevant files.
2. Identify the exact decision Pro should answer.
3. Choose a small, high-signal file bundle with `--files`.
3. For committed code on GitHub, prefer `--github` and give Pro the repository,
ref, relevant paths, and question. Let it retrieve the code instead of assembling
a source bundle. Add a small `--files` bundle only for uncommitted changes,
local-only evidence, or repository content the connection cannot access.
4. Write the prompt yourself using the Prompt Shape below.
5. Run the smallest useful command, usually
`ask-pro --no-temporary --files "<glob>" "<prompt>"` for repo advisories.
5. Run the smallest useful command: `ask-pro --github "<prompt>"` for GitHub
evidence, or `ask-pro --no-temporary --files "<glob>" "<prompt>"` for a file bundle.
For multiline prompts, write a temporary prompt file and use
`ask-pro --no-temporary --prompt-file <path> --files "<glob>"`; do not rely
on shell multiline quoting.
Expand Down Expand Up @@ -88,7 +91,7 @@ again. Example:

## Prompt Shape

Assume Pro has no caller or repository context. Include each material fact and
Assume Pro has no caller context or local checkout. Include each material fact and
instruction once: the goal, current state, hard constraints, evidence, success
criteria, and required output. Do not rely on the agent's conversation context, repo
folklore, prior ask-pro runs, branch names, or unstated user preferences.
Expand All @@ -113,6 +116,46 @@ Keep bundles focused: source files under review, focused tests, relevant docs,
known recent changes, and validation status. Avoid whole-repo bundles unless the
question is explicitly architectural.

## Connected GitHub (optional)

Prefer `--github` for remote repository evidence. It checks
the connection before uploading or submitting, and defaults to normal ChatGPT.
Do not require GitHub for file-based consults. Availability varies by account,
workspace, model, and ChatGPT experience.

If the CLI returns `needs_github_connection` / `connect_github_then_resume`,
direct the human to the opened GitHub page in ask-pro's browser. They install the
plugin if needed, complete sign-in, review permissions, and select repositories;
organization approval may be needed. Wait for them to finish, then run the
emitted resume command without adding `--github`. The requirement is saved with
the session and checked again. Do not start a second session or submit through
the browser yourself. Never collect GitHub credentials or automate authorization.

If available, GitHub's **Allow read actions** permission can avoid read approval
prompts. The human chooses that setting. It controls approval behavior, not the
underlying permissions of a write-capable app; see
[app permissions](https://help.openai.com/en/articles/20001495-managing-app-permissions-in-chatgpt).
The live GitHub plugin advertises write capabilities despite the Help Center's
read-only description. `--github` requests read-only work; it does not restrict
the provider's permissions or prove that writes are impossible.

If GitHub is already connected, proceed without a setup question or source-file
bundle. For the consult, name the repository (`owner/repo`), relevant paths, and
requested branch or commit in the prompt. Explicitly ask Pro
to answer the specific question. The wrapper requests the connected GitHub app,
read-only work, citations, the revision inspected, and access gaps. Do not
infer repository access from ChatGPT login or a successful CLI exit.

Keep `--files` for local/uncommitted changes, non-GitHub evidence, and snapshots
that the app cannot retrieve. Distinguish
uploaded evidence from GitHub evidence, and ask Pro to flag revision conflicts.
If the app or requested revision is unavailable, offer a new consult with a
focused file bundle and
disclose that limitation. Do not silently switch away from Pro. The CLI submits
text; it does not select an app in the composer or handle app approval prompts.
Until a live run demonstrates retrieval, describe this as an available setup
path, not a verified connection. Confirm material claims against the repo.

## Output

Normal `ask-pro` stdout is compact TOON-style telemetry. Use `state`, `action`,
Expand Down Expand Up @@ -148,6 +191,7 @@ state is `INCOMPLETE_ANSWER` / `preamble_without_artifacts`, do not treat
```bash
ask-pro "Review the async billing webhook migration plan and return an implementation plan."
ask-pro --no-temporary --prompt-file question.md --files src --files tests
ask-pro --github "In owner/repo at <commit>, inspect src/auth and explain token validation."
ask-pro --temporary "Review this sensitive migration plan, and fail if Temporary Chat cannot use Pro."
ask-pro --no-temporary "Review this in normal ChatGPT instead of Temporary Chat."
ask-pro --prompt-file question.md --files .\src
Expand Down
Loading
Loading