Files
AIChatExporter/docs/cli.md
T
JesseMarkowitzandClaude Opus 5 04ebb8987c release: v1.0.0, and split the README into docs/
The archive does what it was built to do, unattended, on both machines:
four providers, incremental and resumable, Markdown plus Joplin, on a
schedule, with results pushed to a phone and failures that announce
themselves. The one item left on the roadmap — a StartOS service that
consolidates both machines into one corpus — is an optional convenience,
not a gap. Hence 1.0.0 rather than another 0.x.

The code in this release shipped across b6ce636..8c3e00a: the claude.ai
403 session-invalid fix, the fork-subagent recursion fix, the FAILED-push
backstops on both platforms, and asset-download retries with cross-run
recovery, a circuit breaker and media counts in the notification. This
commit cuts the release and pays a documentation debt.

The README had reached 939 lines / 6,490 words / 48 KB / 96 headings,
doing four unrelated jobs at once, and FUTURE.md asked for the split to
ride along with the next substantial README edit rather than break every
existing anchor for its own sake. A release is that edit. README.md is
now 303 lines and keeps only what you need to install and configure the
tool; the rest moved verbatim into docs/providers.md, docs/cli.md,
docs/scheduling.md and docs/troubleshooting.md. No section was rewritten
or dropped and every original heading still exists, so each anchor still
resolves somewhere — but bookmarks into moved sections now need the
docs/ path, which is the cost this deferral was protecting against.

.gitignore needed the fix FUTURE.md predicted: the blanket *.md rule
(exported conversations are Markdown and may be private) matched all
four new pages, and git check-ignore confirmed every one would have been
silently ignored. !docs/*.md is added with a comment saying why. Gitea's
rendering of relative docs/ links cannot be checked before pushing, so
it still needs confirming after.

Also corrects two stale claims in the changelog: the Windows backstop is
no longer "not yet run under Windows PowerShell 5.1 or Task Scheduler"
(confirmed on winmini today, LastTaskResult 0 — only the FAILED push
itself remains unexercised there), and the test count is 414, not 409.

414 tests passing. No code changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gCWSknsWvhLnMFwa7qnGp
2026-10-20 22:18:37 -04:00

8.9 KiB

CLI Reference

Every command, its flags and what it does.

← README · Providers and Tokens · Scheduling · Troubleshooting


Global flags

--verbose / -v     DEBUG output to console
--quiet / -q       WARNING and above only
--debug            DEBUG + full tracebacks + redacted API response bodies
--no-log-file      Disable file logging
--version          Print version and exit

auth — Interactive token setup

ai-chat-exporter auth                  # manual wizard (DevTools flow)

Guided wizard to find and save session tokens and ChatGPT project IDs. Detects OS and shows the correct DevTools shortcut, then writes the values to .env.

doctor — Health check

ai-chat-exporter doctor

Checks: token presence, JWT validity and expiry, directory permissions, disk space, live API reachability. Exits with code 0 if all pass, 1 if any fail.

export — Export conversations

# Export everything (new/updated only)
ai-chat-exporter export

# Single provider
ai-chat-exporter export --provider claude

# JSON output
ai-chat-exporter export --format json

# Both Markdown and JSON
ai-chat-exporter export --format both

# Only conversations updated since a date
ai-chat-exporter export --since 2024-06-01

# Only conversations in a specific project (case-insensitive substring)
ai-chat-exporter export --project "learning python"

# Only conversations outside any project
ai-chat-exporter export --project none

# Write to a custom directory
ai-chat-exporter export --output /path/to/my/notes

# Preview without writing anything
ai-chat-exporter export --dry-run

Options: --provider [chatgpt|claude|claude-code|codex|all], --format [markdown|json|both], --output PATH, --since YYYY-MM-DD, --project NAME, --hidden-content [full|placeholder|omit], --download-media [images|all|off], --max-conversations N, --force, --dry-run

Re-rendering the whole archive after an upgrade. New formatting or features (collapse policy, media downloads) only change conversations as they're re-exported. To re-render everything you already have, use --force — it re-exports every conversation even if unchanged, without cache --clear, so your Joplin note links are preserved (a later joplin run updates the existing notes instead of duplicating them).

A force re-render runs as a tracked "campaign": each run re-renders the least-recently-exported conversations, the "still to go" count shrinks each run, and finished providers do no further work. The simplest approach is one uncapped pass:

ai-chat-exporter export --force      # re-renders everything in one paced pass
ai-chat-exporter joplin              # update notes + upload media

Or spread the load with --max-conversations, re-running until it reports "Force re-render complete":

ai-chat-exporter export --force --max-conversations 50   # repeat until complete

list — List conversations

# List all conversations for all providers
ai-chat-exporter list

# Single provider
ai-chat-exporter list --provider chatgpt

# Filter by project
ai-chat-exporter list --project "learning python"

# Only conversations outside any project
ai-chat-exporter list --project none

Fetches and displays all conversations without exporting them. Useful for verifying what the tool can see before running an export.

joplin — Sync to Joplin

# Sync all pending conversations to Joplin
ai-chat-exporter joplin

# Preview what would be synced without sending anything
ai-chat-exporter joplin --dry-run

# Sync a single provider
ai-chat-exporter joplin --provider chatgpt

# Sync only conversations in a specific project
ai-chat-exporter joplin --project "learning python"

# Sync only conversations outside any project
ai-chat-exporter joplin --project none

Reads the local export cache and pushes each exported Markdown file to Joplin as a note. Notebooks are created automatically. Re-running is safe — notes are updated (not duplicated).

Prerequisites:

  1. Run export first to generate the Markdown files
  2. Open Joplin → Tools → Options → Web Clipper → enable the service
  3. Copy the Authorization token and add JOPLIN_API_TOKEN=<token> to your .env
  4. Joplin desktop must be open when you run this command

Options: --provider [chatgpt|claude|claude-code|codex|all], --project NAME, --dry-run

sync — Export and sync in one run

# The whole archive run: export, then push to Joplin
ai-chat-exporter sync

# One provider
ai-chat-exporter sync --provider codex

# Export only; don't touch Joplin
ai-chat-exporter sync --skip-joplin

# Joplin being closed is a warning, not a failure (used by the schedulers)
ai-chat-exporter sync --joplin-optional

Equivalent to export followed by joplin with the same --provider. Intended for scheduled runs — see Scheduling a Daily Run.

Unlike the individual commands, sync sets a meaningful exit code: non-zero if any conversation failed to export or any note failed to sync. A provider whose listing call fails outright (an expired web session token being the usual cause) counts its whole batch as failed. A provider that is simply unconfigured, or that had nothing new, is ordinary success. export on its own always exits 0, which is fine when you're reading the summary table and useless to a scheduler.

--joplin-optional downgrades an unreachable Joplin to a warning: the export has already captured the local transcripts, and the notes are rebuilt from the cache by the next run that finds Joplin open.

Options: --provider [chatgpt|claude|claude-code|codex|all], --since YYYY-MM-DD, --hidden-content [full|placeholder|omit], --max-conversations N, --skip-joplin, --joplin-optional, --notify/--no-notify, --dry-run

Note this is a deliberate subset of export's options — --format, --output, --project, --download-media and --force are not passed through. Use export directly for those. (--download-media still applies from .env; the flag is only a per-run override.)

notify — Push-notification settings and test

# Show the current settings
ai-chat-exporter notify

# Send a test push to confirm the topic works
ai-chat-exporter notify --test

Shows the resolved ntfy configuration and which machine name will appear in the title. See Getting notified for what a scheduled run sends.

Options: --test

prune — Delete stale export files

# Preview what would be deleted
ai-chat-exporter prune --dry-run

# Delete (asks for confirmation; -y skips the prompt)
ai-chat-exporter prune

Deletes export files no longer referenced by the cache manifest — leftovers from old folder layouts or fixed bugs that would otherwise double-sync into Joplin. Refuses to run when the manifest is empty (e.g. right after cache --clear) so it can never wipe a freshly cleared archive. The doctor command separately verifies that every manifest entry's file exists on disk.

projects — Discover ChatGPT project IDs

# List the projects your conversations belong to
ai-chat-exporter projects

# Also inspect conversations whose listing entry doesn't name a project
ai-chat-exporter projects --deep

# Write the discovered IDs straight into .env
ai-chat-exporter projects --write

CHATGPT_PROJECT_IDS is maintained by hand, and a project missing from it is invisible to the listing pass — conversations that live only inside that project are never fetched at all. This reports every project your conversations belong to, marks the ones absent from .env, and prints a paste-ready line.

--deep fetches each conversation's detail when the listing doesn't name its project: complete, but one request per conversation, so it's slow.

Options: --deep, --write

canary — Check for provider API drift

ai-chat-exporter canary
ai-chat-exporter canary --provider chatgpt

The web providers are undocumented internal APIs that can change shape without notice, and the failure mode is silent — a renamed field means content is quietly dropped rather than an error being raised. The canary fetches one listing page and one conversation per provider and asserts only the fields the normalizer actually depends on.

Findings are ERROR (a load-bearing field is missing or mistyped — the parser will break or silently lose data) or WARN (something unfamiliar appeared; worth investigating, not necessarily broken). Exits non-zero on any ERROR, so it can be scheduled or run in CI. Local providers have no remote schema and are not probed.

Options: --provider [chatgpt|claude|all]

cache — Manage the sync manifest

# Show statistics
ai-chat-exporter cache --show

# Clear all cached entries (forces full re-export next run)
ai-chat-exporter cache --clear

# Clear a single provider
ai-chat-exporter cache --clear --provider claude