A complete reference for the Johnny Decimal filing system managed by jd-cli. Covers principles, structure, conventions, cross-app integration, agent workflows, and the current state of the tooling.
Johnny Decimal is an organizational system that assigns numeric IDs to every folder in a filing hierarchy. This project (jd-cli) is a Python CLI and MCP server that manages the filesystem, enforces conventions, and integrates with external apps (Apple Notes, OmniFocus).
The filesystem is the canonical source of truth. All other systems hold sparse subsets.
- The filesystem is the source of truth. External apps (Notes, OmniFocus, email) hold subsets — never the complete picture.
- Folders are created on-demand. No empty mirroring across apps. A folder exists in an app only when it has content there.
- JD-aware apps signal awareness via a meta marker (e.g., an
XX-XXarea folder). If the marker is absent, the app is not JD-managed. - Agents use the
jdCLI. No hardcoded paths.jd which <id>resolves everything. - Minimum viable organization. JD provides structure. OmniFocus provides action. Don't over-engineer beyond that.
Top-level groupings. Max 10 (digits 0-9). Each area gets a two-digit range: 00-09, 10-19, ..., 90-99.
Two-digit numbered directories inside areas. Each area holds up to 10 categories.
Dotted notation inside categories. Up to 100 per category (00-99). An ID is the atomic unit — it covers a topic, not a single file.
Every category follows this convention:
| ID | Purpose | Notes |
|---|---|---|
xx.00 |
Category meta | Agent workspace, config, templates, README |
xx.01 |
Unsorted | Category-level inbox. Stuff that belongs here but hasn't been specifically filed |
xx.99 |
Archive | Auto-created by jd mv -a |
Pattern: x0 Meta - [Area Name]. Purpose: area-level reference material, templates, conventions.
Each x0 directory should contain a README.md documenting:
- What the area covers and its boundaries
- Setup or tooling needed
- Area-specific conventions
- Links to related resources
Category 01 is the system-wide intake. Items flow through here on their way to proper JD locations.
01.xx (Capture) → xx.01 (Category unsorted) → xx.yy (Final ID)
Tier 1 (Capture → Category): Quick sort. "This is a health thing" → jd mv file 13.01. An agent or human just needs to know the domain.
Tier 2 (Category → ID): Detailed filing. "This is specifically lab results" → jd mv file 13.05. Domain agents handle this.
You don't have to do both tiers at once. Tier 1 is a fast sweep; Tier 2 happens when someone with domain knowledge is available.
| Bucket | Type | Purpose |
|---|---|---|
01.00 |
meta | Capture policy, auto-sort rules, agent config |
01.01 Unsorted |
catch-all | True unknown — needs human decision |
01.02+ |
source/destination | Specific intake channels (downloads, screenshots, scans) or staging for blocked actions (waiting for external drive, waiting for app import) |
Source buckets fill automatically. Destination buckets get emptied when blockers clear.
Cascading config files (jd.yaml) control conventions at any level of the tree. Most specific wins, like .editorconfig.
Lives in the dotfiles repo, symlinked into 00.00 Meta/. Declares:
conventions:
meta_category: true # x0 = "Meta - [Area]"
meta_id: true # xx.00 = category meta
unsorted_id: true # xx.01 = "Unsorted"
capture_category: "01" # system inbox
patterns:
"*.00": { purpose: meta }
"*.01": { purpose: capture }
"x0": { purpose: area-meta }
volumes:
External Drive:
mount: /Volumes/External Drive
root: Documents| Key | Default | Description |
|---|---|---|
ids_as_files |
false |
Allow IDs to be files instead of directories |
ids_files_only |
false |
IDs should contain only files (no subdirectories) |
meta_category |
true |
x0 should be "Meta - [Area]" |
meta_id |
true |
xx.00 = category meta directory |
unsorted_id |
true |
xx.01 = "Unsorted" triage inbox |
- Into JD (preferred): External resources get a symlink inside the JD tree.
- Out of JD (exception): Sync-sensitive dirs (git repos, XDG config) live outside iCloud and get symlinked FROM JD. Avoids iCloud/sync conflicts.
- Some IDs are symlinks to external drives. If the drive is unplugged, the symlink is broken. This is expected —
jd validatereports it but doesn't treat it as an error.
External paths (e.g., ~/.ssh) can symlink into the JD tree. Declare them in jd.yaml under links: so jd validate tracks them:
links:
"06.05":
- ~/.ssh
- ~/.gnupgjd ln creates the symlink and adds the config entry in one step.
Git repos cannot live on iCloud Drive (.git corruption from sync). All repos live in ~/repos/. JD references them via symlinks:
~/repos/myproject/ ← real repo
~/Documents/60-69 Work/61 Projects/61.03 MyProject → ~/repos/myproject/
The dotfiles repo lives at ~/.config/ and contains system config docs (POLICY.md, jd.yaml) symlinked into the JD tree.
Some JD IDs live entirely in Apple Notes rather than the filesystem. The jd notes commands manage this:
- Stub files mark Notes-backed IDs:
26.05 Recipes [Apple Notes].yaml - Config declarations in
jd.yamllist which IDs/categories are Notes-backed jd notes scancompares Notes folders against the JD treejd notes validatechecks consistency between stubs, Notes, and configjd notes create/openmanage notes directly
JD and OmniFocus serve different purposes:
- JD = where things are (filing, artifacts, reference)
- OF = what you need to do (actions, projects, deadlines)
They link via tags (JD:xx.xx), not folder structure. One OF project may reference multiple JD IDs. jd omnifocus commands manage the tag-based linking.
- Agent workspaces hold the agent's mind: config, memory, skills
- JD
xx.00dirs hold the agent's output: reports, drafts, artifacts - Agents read from anywhere in JD but write artifacts to their scoped categories
Each agent workspace can declare its JD scope:
scope:
- "20-29" # Family area
- "13" # Health and medicalScope is enforced on write operations (mv, new, init, archive). Reads always pass.
- Agent produces an artifact
- Agent knows the domain →
jd mv artifact.pdf xx.00(into category meta) - Agent doesn't know →
jd mv artifact.pdf 01.01(into capture unsorted) - Triage pass files it properly
The jd mcp command exposes 29 tools to AI agents, covering navigation, creation, moving, archiving, validation, symlinks, volume management, Apple Notes, OmniFocus, and config.
- Areas:
XX-XX Name(hyphen, not en-dash) - Categories:
XX Name - IDs:
XX.YY Name(or justXX.YYfor meta dirs) - Sentence case for names
- No trailing spaces
- No special characters that break shell commands (
:,*,?)
| File | Purpose | Auto-loaded? |
|---|---|---|
README.md |
What this is, conventions, context. For humans and agents. | No |
CLAUDE.md |
Claude Code session instructions. Rules, preferences, constraints. | Yes |
Don't duplicate content between them. README.md is context; CLAUDE.md is directives.
- Navigation:
cd,ls,which,search,index,json,root,open,stats - Creating:
new id,new category,init,init-all,add - Moving:
mv(renumber, refile, rename), archiving (mv -a),restore - Validation:
validate(with--fix,--force,--dry-run),triage,generate-index - Staging:
stage,unstage,tag add,tag remove - Symlinks:
symlinks,ln - Volumes:
volume list,volume scan,volume index,volume link - Apple Notes:
notes scan,notes validate,notes stub,notes create,notes open - OmniFocus:
omnifocus scan,omnifocus validate,omnifocus open,omnifocus tag,omnifocus create - Config:
config show,config get,config set,config unset,config where,config edit - Agent integration:
claude(cascading context launch) - MCP server: 29 tools + 2 resources, mirrors all CLI functionality
- Cascading config system (
jd.yaml) - Agent scoping via
jd.yaml - Shell completion (zsh)
pipx installpackaging
jd backup— snapshot to tarball/manifestjd cp— copy into JD (like mv but keeps original)jd renum— batch renumber within a categoryjd stats— system-wide statisticsjd gc— clean up empty dirs, broken symlinks, .DS_Store- Config file (
~/.config/johnnydecimal/config.yaml)
- Gap detection (missing IDs in a sequence)
- macOS alias detection
- External drive awareness (skip gracefully if unmounted)
- Auto-fix for simple issues (en-dash → hyphen, trailing spaces)
- CLI-friendly directory naming:
06.03 Dotfiles→06-03-dotfiles - Configurable naming convention in root policy
- Dual-format parser during migration
jd migratecommand with rollback- Touches everything: models, regexes, symlinks, Notes, OF, stubs, policy, completion, iCloud sync
- Email (IMAP) folder structure
- Obsidian vault alignment
jd validate --notes/jd validate --omnifocusfor cross-app checksjd.jsoncached index for faster lookups
johnnydecimal/
cli.py — Click commands (~3600 lines)
mcp_server.py — MCP tools mirroring CLI commands
models.py — JDRoot, JDArea, JDCategory, JDID
config.py — Cascading jd.yaml config system
scope.py — Agent write scoping via jd.yaml
util.py — Path helpers, JD pattern matching
notes.py — Apple Notes connector (JXA)
omnifocus.py — OmniFocus connector (JXA)
tests/ — 153 tests (pytest)
docs/ — Integration guides
| File | What |
|---|---|
README.md |
Quick-start CLI reference |
docs/omnifocus-integration.md |
OmniFocus integration guide |
TODO.md |
Feature roadmap |
Root jd.yaml |
Machine-readable system conventions |
Root POLICY.md |
Full system policy (in dotfiles repo, injected via jd-workflows plugin) |