Independent open-source agent skill

submit‑chatgpt‑feedback

Report a Codex or ChatGPT problem from the task it happened in, while it is still happening — with no feedback dialog opening, no window taking focus, and no diagnostics attached that you did not ask for.

Install it Source on GitHub

Not affiliated with, endorsed by, or an official package of OpenAI. It is a wrapper around a capability that the Codex client you already have installed exposes locally.

01

What it does

Codex and ChatGPT already have good feedback routes. The friction is the timing: the moment worth reporting is usually the moment you are least willing to stop, and the interactive /feedback route asks you to stop — open a dialog, fill it in, decide about logs, submit.

This skill removes that trade-off for the private in-product route. When you tell your agent to file the bug, it writes a sanitized report, sends it through the same first-party upload request the desktop feedback dialog uses, and tells you what came back. Nothing opens. Nothing takes focus. Your task keeps running.

What it is

  • A skill package for a Codex-style agent, plus one Node helper script.
  • A client of feedback/upload on the local Codex App Server that ships inside the ChatGPT/Codex desktop app and the Codex IDE extension.
  • An operating contract that keeps drafting, private sending, and public posting apart.
  • Reviewable in one sitting: one Node script with no third-party imports, three shell scripts, no build step.

What it is not

  • Not an OpenAI product, and not a documented public CLI subcommand.
  • Not UI automation. It never clicks, types, screenshots, or scripts a menu.
  • Not a log collector. Logs, browser data, screenshots and attachments stay off.
  • Not a way to post publicly. GitHub issues need their own explicit permission.

02

The two paths

Both paths send the same first-party feedback/upload request — what happens after the client accepts it is not something this project can observe, so it does not claim it. They differ in what they cost you while you are working. Path A is the documented interactive dialog, and it remains the honest fallback whenever the background path is unavailable. Path B is what this skill adds. Step through it.

Path A interactive /feedback

your task, mid‑thought

Supported and documented. It also interrupts you, and it starts with diagnostics switched on.

Path B background feedback/upload

  1. report.txt
  2. readiness check
  3. local app server
  4. response

A short-lived local process, one JSON request over a pipe, and no window at all.

1 Draft, and send nothing

The agent writes the sanitized report to a private temporary file: what happened, what you expected, the build, the reproduction. Task ids, private paths, prompts and credentials get reviewed out. Drafting is not permission to send.

/tmp/report.txt   ← sanitized text only
threadId          ← the exact affected task
classification    ← bug | bad-result | good-result | safety_check | other

2 Check compatibility, still sending nothing

--check asks your installed client to emit its own stable app-server schema into a temporary directory, verifies that feedback/upload and every payload field are still there, reports the client it resolved, deletes the schema, and exits. It never opens a session and never uploads. No test feedback is ever sent to prove the transport works.

$ node ~/.codex/skills/submit-chatgpt-feedback/scripts/submit-feedback.mjs --check
{"ok":true,"ready":true,"submitted":false,"ui":false,"method":"feedback/upload",
 "surface":"codex_desktop","defaults":{"includeLogs":false,"extraLogFiles":null}}

3 Send exactly this, once authorized

The helper starts the bundled client's app server on a pipe, initializes, and sends one request. Every diagnostic field is hard-coded off. Use --dry-run first to print this payload without connecting.

{
  "method": "feedback/upload",
  "params": {
    "classification": "bug",
    "reason": "…your sanitized report…",
    "threadId": "…the affected task…",
    "includeLogs": false,
    "extraLogFiles": null,
    "tags": { "surface": "codex_desktop", "submission_source": "authorized_codex_agent" }
  }
}

4 Accept only real evidence

Success is claimed only when the app server answers with a response that actually carries threadId — which the desktop client presents as the correlation identifier, and there is no separate support ticket number, so the helper does not invent one. An error, a timeout, an early exit, or a reply that merely lacks an error is reported as not submitted, and the interactive /feedback handoff is offered rather than performed.

{"ok":true,"submitted":true,"method":"feedback/upload",
 "threadId":"…","correlationIdentifier":"threadId",
 "includeLogs":false,"extraLogFiles":null}
Four steps, no JavaScript required: the control above is a plain radio group, so it works with keyboard arrows and with scripting disabled.

03

Install

On skill use, the agent checks for public updates at most weekly after a successful check, with a shared local lease. Available updates come with a brief explanation and a question: do you want to install this update? Installation happens only after you agree, through a safe route that preserves local edits. Python 3 is needed for the check; it starts no background service. You can opt out. Failed checks retry later; plugin updates use the host updater.

You need Node.js 18+ and a ChatGPT or Codex client installed locally. Nothing is downloaded at install time, nothing is compiled, and no packages are added. The installer copies the files listed in the package manifest and nothing else.

git clone https://github.com/EthanSK/submit-chatgpt-feedback.git
cd submit-chatgpt-feedback

./install.sh --dry-run   # show exactly what would change
./install.sh             # copy the manifest files into place
./check.sh               # confirm what is installed

The default destination is ${CODEX_HOME:-~/.codex}/skills/submit-chatgpt-feedback. Pass --dest <dir> to install somewhere else. Then verify that your local client still exposes the upload route — this sends nothing:

node ~/.codex/skills/submit-chatgpt-feedback/scripts/submit-feedback.mjs --check

Removing it is symmetrical, and it will not delete a file you edited unless you insist:

./uninstall.sh --dry-run
./uninstall.sh           # keeps files you modified
./uninstall.sh --force   # removes them too

04

Use it

Most of the time you never touch the helper. You talk to your agent, and the skill tells the agent how to behave: find the affected task, sanitize, state the payload, send once, report the identifier verbatim.

“File this as a Codex bug from the task where it happened.”

“Draft feedback about this, but don't send it yet.”

“Report this and tell me exactly what you attached.”

To drive the helper yourself:

HELPER=~/.codex/skills/submit-chatgpt-feedback/scripts/submit-feedback.mjs

# 1. is the local route still there? (sends nothing)
node "$HELPER" --check

# 2. what exactly would be sent? (connects to nothing)
node "$HELPER" --report /tmp/report.txt --thread TASK_ID --dry-run

# 3. send it once
node "$HELPER" --report /tmp/report.txt --thread TASK_ID --classification bug

Use --no-thread for feedback that genuinely belongs to no task. On success one line of JSON goes to standard output; on failure one line of JSON goes to standard error and the exit status is 1. There is no partial success.

If two different first-party clients are running — the desktop app and the Codex VS Code extension, say — the surface cannot be inferred, so the helper refuses rather than guessing and lists what it found. Several processes of the same binary are deduplicated first, because that is not ambiguity. Name the client for the surface the report is about, and no UI is needed to do it:

node "$HELPER" --cli /path/to/ChatGPT.app/Contents/Resources/codex --check

That choice is load-bearing: surface, app_version and app_build follow the binary you name, so a guess would mislabel an otherwise correct report. An explicit --cli or CODEX_FEEDBACK_CLI always wins and skips detection entirely.

05

Three permissions, never merged

Most feedback mistakes are permission mistakes: something private ends up public, or something meant as a draft gets sent. The skill treats these as three separate gates.

  1. Gate 1 · Draft

    “Draft”, “prepare”, “write up” — produce the sanitized report and stop. Asking for a draft is never permission to send.

  2. Gate 2 · Send privately

    “File”, “report”, “submit” — permission for one private in-product submission, with the payload stated first and diagnostics off.

  3. Gate 3 · Publish publicly

    A GitHub issue, comment or reaction needs its own explicit words. Private permission never implies public posting, and duplicates get a reaction rather than a new issue.

06

Privacy and security

Everything runs locally against the Codex client you already trust with your work. This project has no server, no telemetry, no analytics, and no third-party code. The website you are reading loads no fonts, no scripts and no assets from anywhere else.

Sent, when you authorize a submission

  • Your sanitized report text, exactly as reviewed.
  • The classification and the affected threadId, or none.
  • Client tags: app version and build, surface, and an honest submission source.

Never sent by this helper

  • Logs — includeLogs is hard-coded false.
  • Files — extraLogFiles is hard-coded null.
  • Screenshots, browser tabs, browser logs, or session transcripts.
  • Anything at all during --check or --dry-run.

Two extra guards refuse the send rather than trusting it: report text over the size limit is rejected instead of truncated, so a pasted log cannot slip through, and text matching an obvious credential shape — private key blocks, API-key and token patterns, Authorization headers — is refused outright. Widening diagnostics is deliberately not a flag on this helper. If you truly want to attach logs, review them and call feedback/upload yourself with the exact approved payload.

The full statement of what the code can and cannot do, the guards, the residual risks and how to report a vulnerability privately is in SECURITY.md.

07

Limitations, stated plainly

  • It rides an undocumented-for-external-use surface. feedback/upload is a real, stable, first-party request in the bundled app server's own generated schema, but public documentation describes /feedback as a dialog and does not document this as an ordinary CLI subcommand. A client update can change or remove it. That is why --check exists and why every failure is fail-closed.
  • macOS is the verified platform. Client auto-detection knows about /Applications, ~/Applications and the VS Code Codex extension. Elsewhere it can still work with --cli or codex on PATH, and it labels itself platformSupport: "unverified" rather than pretending.
  • No delivery guarantee beyond the response. A successful response means the upload was accepted. It is not a promise that a human will read it, and there is no status to poll.
  • Sanitization is a judgement, not a filter. The credential guard is a backstop for obvious mistakes. Reviewing the report text is still the agent's and your responsibility.
  • Public GitHub posting is out of scope for automation. The skill documents how to route and de-duplicate a public report; it does not post one for you without explicit permission.
  • The test suite never submits feedback. It exercises the whole protocol against a local fake client, so passing tests do not prove your real client is compatible. --check is how you prove that.

08

Architecture

The skill is instructions plus one script. SKILL.md is the operating contract an agent reads; the references hold the conditional detail; the helper opens no socket of its own — it exchanges JSON over ordinary pipes with a short-lived local app-server child process, and the first-party client owns the network upload.

submit-chatgpt-feedback/
├─ skill/submit-chatgpt-feedback/     the distributable
│  ├─ SKILL.md                        operating contract and router
│  ├─ MANIFEST                        the only files install/uninstall touch
│  ├─ agents/openai.yaml              display metadata
│  ├─ references/
│  │  ├─ background-upload.md         the app-server route, honestly scoped
│  │  ├─ reporting-routes.md          private vs public routing per surface
│  │  └─ evidence-quality.md          observation vs inference, evidence bars
│  └─ scripts/submit-feedback.mjs     the helper: check, dry-run, send
├─ install.sh · uninstall.sh · check.sh   manifest-scoped lifecycle
├─ tests/                             package, helper, installer, docs, privacy
├─ examples/                          a worked report and real output shapes
└─ docs/                              this site, dependency-free

The request path, end to end:

agent  →  report.txt (temp)  →  submit-feedback.mjs
                                    │
                                    ├─ resolve the active bundled client
                                    ├─ spawn: codex app-server --stdio   (local pipe)
                                    ├─ initialize
                                    └─ feedback/upload  ─────────────►  the client's own upload path
                                                        ◄─────────────  { threadId }

Everything is verifiable from the repository: read the helper, read the contract, or run node tests/run-tests.mjs.