Repository navigation
Configuration en
한국어 | English
Three paths — where your raw takeout lives, where converted markdown goes,
and where your actual Obsidian vault is — are managed via config.json.
It's auto-created with default values in the project root on first run (see
the repository's config.example.json for its shape). It contains personal
paths, so it's .gitignored.
{
"takeout_paths": { "chatgpt": "", "gemini": "", "claude": "" },
"markdown_output_dir": "result",
"obsidian_vault_dir": "",
"vault_subdirs": { "chatgpt": "ChatGPT", "gemini": "Gemini", "claude": "Claude" }
}CLI flag > config.json > built-in default. If you set nothing, it uses
the defaults (source is data/<vendor>/, output is result/, vault is
unset). Use a CLI flag to override for a single run, or edit config.json
directly if you want to keep using the same location every time.
| Path | config.json key | CLI override | Default |
|---|---|---|---|
| Raw takeout source | takeout_paths.<vendor> |
--input VENDOR=PATH |
data/<vendor>/ |
| Markdown output | markdown_output_dir |
--output-dir PATH |
result/ |
| Obsidian vault | obsidian_vault_dir |
--vault-dir PATH (with --publish) |
unset (no publishing) |
When applying to the vault with --publish, a per-vendor subfolder is
auto-created using the names configured in vault_subdirs
(<vault>/ChatGPT/, <vault>/Gemini/, <vault>/Claude/).
Upserts are based purely on the vault_dir/<vendor_subdir>/<filename>
location — if you move or rename a note inside your vault, that move isn't
tracked, so if that session's content changes later, a new note may be
created at the original location rather than wherever you moved it.
The Claude vendor splits project-attached conversations into subfolders —
result/claude/<project name>/*.md — and --publish reproduces that
subfolder structure as-is when mirroring, at
vault_dir/<vendor_subdir>/<project name>/<filename> (common/publish.py
recursively walks result_dir). For the same reason, if a Claude project
gets renamed later, a new subfolder is created and the old subfolder's file
becomes orphaned — the same kind of limitation as the "moves aren't tracked"
point above.
Re-converting the same session doesn't unconditionally rewrite it. The
frontmatter's content_hash (a SHA-256 of the rendered body) is compared
against the previous note to decide created / updated / unchanged.
Relying on whether a session_id exists alone can't catch a conversation
that was continued later, so the actual rendered output is compared every
time instead. --publish reuses the exact same upsert logic
(common/upsert.py).
Related pages: Output Format, Architecture