Obsidian Code Atlas
Mirror your GitHub code into an Obsidian vault.
[!NOTE] Who is this for?
People who write code, host it on GitHub, and use Obsidian as a work, lab, or personal notebook for its "Second Brain" features.
What problem does this solve?
As a scientist, I love having all my laboratory notes and ideas easily searchable within an Obisidian vault. But, when I write code, I often find myself clunkily searching on GitHub in the browser and/or through my local computer's files for previously written scripts.
Why
obsidian-code-atlas?It brings code from all my repos into easily searchable and visually appealling
BaseandNoteobjects inside an existing Obsidian vault. It updates each morning, so scripts are always fresh.
This tool generates four things from your gh-authenticated account:
- Activity.md — recent commits across every repo you touch.
- Repos/ + Repos.base — one note per repo, browsable as an Obsidian Base database.
- Scripts/ + Scripts.base — every source file you’ve written, as searchable notes.
- Issues/ + Issues.base — every open issue in those repos (labels, assignees, milestone, comment count), one note per issue named by number (e.g.
Issues/owner-repo/42.md).
The generated Repos/, Scripts/, Issues/, Activity.md, GitHub Dashboard.md, and *.base files are fully managed by the tool — don’t hand-edit them.
Requirements
- GitHub CLI (
gh) installed and authenticated (gh auth status). - Python 3.9 or newer.
Installation
Install from PyPI with pip:
python3 -m pip install obsidian-code-atlas
For an isolated command-line installation, use pipx:
pipx install obsidian-code-atlas
Upgrade an existing installation:
python3 -m pip install --upgrade obsidian-code-atlas
# or, for pipx:
pipx upgrade obsidian-code-atlas
Installing from source
Install the latest development version directly from the Git URL:
python3 -m pip install git+https://github.com/tiszalab/obsidian-code-atlas.git
Or from a local clone:
git clone https://github.com/tiszalab/obsidian-code-atlas.git
cd obsidian-code-atlas
python3 -m pip install .
The application code lives in your Python environment; generated Obsidian notes live in the vault you choose below.
Initializing a vault
The init command prepares an existing Obsidian vault to receive generated notes and optionally installs a daily scheduler.
obsidian-code-atlas init "/path/to/My Vault" --output "Code Atlas" --scheduler launchd
Arguments:
VAULT_PATH— root of an existing Obsidian vault (the directory containing.obsidian/).--output— name of the generated atlas folder inside the vault (default:Code Atlas).--scheduler—launchd(macOS),cron(Unix), ornone(default).--gitignore— add the generated output folder to the parent Git repository’s.gitignore(default).--no-gitignore(alias--track-generated) — opt out of the default.gitignoreentry.--force— initialize even if.obsidian/is missing.--no-refresh— skip the firstrefresh all.--config— path to a customobsidian-code-atlas.jsonconfiguration file.
init is idempotent: rerunning it updates the scheduler and Git ignore entry without duplicating them or deleting existing generated notes.
Git integration
If your vault is inside a Git worktree, init finds the actual repository root (even when the vault is nested below it) and adds an anchored ignore block:
# BEGIN obsidian-code-atlas: <stable-id>
/path/relative/to/repo/My Vault/Code Atlas/
# END obsidian-code-atlas: <stable-id>
This leaves unrelated .gitignore content untouched. Because the whole output directory is ignored, generated notes and output-local configuration will not be tracked by the parent repository. Use --no-gitignore (or its alias --track-generated) to skip this step. When the output directory is the worktree root there is no rule that could ignore it, so init and doctor say so instead of writing one.
Diagnosing the setup
obsidian-code-atlas doctor --output "/path/to/My Vault/Code Atlas"
doctor checks:
- package and Python version;
- whether the output directory exists and is writable;
- whether the output is inside an Obsidian vault;
ghavailability andgh auth status;- effective configuration file and JSON validity;
- installed scheduler status;
- parent Git worktree and whether the output is ignored.
It exits with status 1 if a condition prevents refresh from working. Warnings that do not block refresh keep a zero status. doctor never prints tokens or credentials.
Refreshing content
Every package invocation needs an output directory. This prevents generated files from silently going into the current working directory or package installation:
obsidian-code-atlas refresh all --output "/path/to/My Vault/Code Atlas"
obsidian-code-atlas refresh activity --output "/path/to/My Vault/Code Atlas"
obsidian-code-atlas refresh repos --output "/path/to/My Vault/Code Atlas"
obsidian-code-atlas refresh scripts --output "/path/to/My Vault/Code Atlas"
obsidian-code-atlas refresh issues --output "/path/to/My Vault/Code Atlas"
The section defaults to all, so obsidian-code-atlas refresh --output PATH is also valid. Paths containing spaces are supported. OBSIDIAN_CODE_ATLAS_OUTPUT may supply the output path, but an explicit --output takes precedence. The command resolves the path to an absolute path and creates it when the refresh starts.
The same interface is available through Python:
python3 -m obsidian_code_atlas --version
python3 -m obsidian_code_atlas refresh all --output "/path/to/My Vault/Code Atlas"
Scheduling
Installed-package users can manage a daily 08:00 refresh without depending on repository scripts:
obsidian-code-atlas scheduler install --launchd --output "/path/to/My Vault/Code Atlas" # macOS
obsidian-code-atlas scheduler install --cron --output "/path/to/My Vault/Code Atlas" # Unix cron
obsidian-code-atlas scheduler status --output "/path/to/My Vault/Code Atlas"
obsidian-code-atlas scheduler uninstall --output "/path/to/My Vault/Code Atlas"
Pass --config PATH to scheduler install to preserve an explicit configuration path in the scheduled command. The generated job uses the absolute interpreter running Obsidian Code Atlas, refresh all, and the absolute output path. Different output directories receive different identifiers, so multiple vaults can coexist. --launchd is rejected outside macOS with a recommendation to use cron.
launchd runs both at login and daily at 08:00. Cron runs daily at 08:00. Cron output is appended to refresh.log in the selected output directory; launchd writes launchd.out.log and launchd.err.log there. The install command creates the output directory before scheduling the first run.
scheduler status prints the expected identifier, installed scheduler types, schedule, and command. It returns status 1 when neither matching job is installed and 0 when at least one is found. Uninstall removes only jobs belonging to the selected output directory.
Configuring language support
Create obsidian-code-atlas.json in the output directory using this structure (repository checkouts also include obsidian-code-atlas.json.example):
{
"excluded_repos": [
"owner/private-repo",
"organization/archived-repo"
],
"script_extensions": {
".ex": {"label": "Elixir", "fence": "elixir"},
".exs": {"label": "Elixir Script", "fence": "elixir"},
".yml": null
}
}
excluded_repos contains case-insensitive owner/repository names. Excluded repositories are omitted from activity, repository, script, and issue output. A script_extensions value of null removes an extension. A configuration file that cannot be parsed or validated aborts the command with an error instead of falling back to the defaults. Configuration is selected in this exact order:
- explicit
--config PATH; OBSIDIAN_CODE_ATLAS_CONFIG;obsidian-code-atlas.jsonin the output directory;- built-in defaults.
Extension environment overrides are applied after the selected file:
OBSIDIAN_CODE_ATLAS_EXTENSIONS=".ex:Elixir:elixir,.exs:Elixir:elixir,-.yml" \
obsidian-code-atlas refresh all --output "/path/to/My Vault/Code Atlas"
The format is .ext:Label:fence; two parts (.ext:Label) and one part (.ext) are also accepted, and a leading - removes an extension.
File layout
|.
├── pyproject.toml
├── src/obsidian_code_atlas/ # installable package
│ ├── __init__.py
│ ├── __main__.py
│ ├── cli.py
│ ├── doctor.py
│ ├── init.py
│ └── scheduler.py
└── obsidian-code-atlas.json.example
Uninstall
Remove the scheduler first, then uninstall the package. Generated notes are retained unless you delete the output folder manually.
obsidian-code-atlas scheduler uninstall --output "/path/to/My Vault/Code Atlas"
python3 -m pip uninstall obsidian-code-atlas
# or, for pipx:
pipx uninstall obsidian-code-atlas
License
MIT © Michael Tisza
Metadata
Release files for obsidian-code-atlas 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| obsidian_code_atlas-0.5.1.tar.gz | 37.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| obsidian_code_atlas-0.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.0 kB
Release files / obsidian_code_atlas-0.5.1.tar.gz
| Download URL | obsidian_code_atlas-0.5.1.tar.gz |
|---|---|
| Size | 37.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09ec12fb49d09742f2b4e43dcd2cbde367db56a45aae5abb32d751a1b79e9ab1
|
|
BLAKE2b-256 checksum How to use checksums |
ad645f85a458802cf3569d38b7c5c2ca11ceec4fdc9fcd9a2760f61849842fc3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency logRelease files / obsidian_code_atlas-0.5.1-py3-none-any.whl
| Download URL | obsidian_code_atlas-0.5.1-py3-none-any.whl |
|---|---|
| Size | 26.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
72b8ac9235c9df73b9e6d0c307e8cd9d7ae08c2da1b2c1f9123a6fb6e10f03f4
|
|
BLAKE2b-256 checksum How to use checksums |
a51840e771b3620d5586158a8e889e724a84e3a45ae440f3d9e9509875e1565a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.
Transparency log