feat: projects command — find the project IDs the config is missing

Reading gizmo_id during normalization fixed attribution, but it cannot
answer "which projects am I missing?": normalization only runs on
conversations being exported, and a normal run skips everything already
cached. Discovering the gaps would have meant --force re-exporting the
whole archive.

`ai-chat-exporter projects` does it directly. It lists conversations,
collects the g-p- ids they belong to, resolves display names, and prints a
table marking which are absent from .env plus a paste-ready
CHATGPT_PROJECT_IDS line. --write applies it; --deep falls back to one
detail request per conversation when the listing does not carry gizmo_id
(unverified which shape this account returns, so the command reports which
path it took rather than assuming).

This matters beyond tidiness: attribution is now self-correcting, but the
listing pass still needs the ids. Conversations that live only inside a
project never appear in the default listing, so an unlisted project's chats
are not merely misfiled — they are never fetched.

6 CLI tests: reporting, the already-configured case, the g-p- guard, --deep,
the hint when --deep is needed, and --write. 324 pass.
This commit is contained in:
JesseMarkowitz
2026-08-17 13:19:34 -04:00
parent 710889b65f
commit d083aea135
4 changed files with 278 additions and 0 deletions
+22
View File
@@ -201,6 +201,28 @@ ChatGPT project conversations are stored separately from your main conversation
### Finding your project IDs
The quickest way is to let the exporter find them:
```
ai-chat-exporter projects
```
It lists every project your conversations actually belong to, marks which are
missing from `.env`, and prints a paste-ready `CHATGPT_PROJECT_IDS=` line
(`--write` updates `.env` for you). If your ChatGPT account does not include
the project on conversation summaries, add `--deep` and it reads each
conversation's detail instead — slower, one request per conversation, but
complete.
Why it matters: project attribution is resolved from each conversation's own
`gizmo_id`, so exports file correctly whether or not a project is configured.
But the *listing* pass still needs `CHATGPT_PROJECT_IDS` — conversations that
live only inside a project never appear in the default conversation list, so an
unlisted project's chats are never fetched at all. Every export run also names
any unconfigured project it encounters.
To find them by hand instead:
1. Open ChatGPT and click a Project in the left sidebar
2. Look at the browser URL — it will look like:
`https://chatgpt.com/g/g-p-68c2b2b3037c8191890036fb4ae3ed9f-my-project/project`