Init
Turns the folder the session was started in into a PARA vault wired to this plugin, and keeps it that way: a second run fills in what is missing and migrates an older schema. It is the only skill that writes the vault's config, and the only one that runs without it.
What it writes
Everything below and nothing else. REPO_ROOT, PARA_DIR, PARA_ROOT as paths.md
defines them.
| What | Where | How |
|---|---|---|
| the PARA folders | PARA_ROOT/ + Projects, Areas, Resources, Archive, Inbox, Templates | created when missing |
| the note templates | PARA_ROOT/Templates/ | seeded from the plugin, only when missing («Templates») |
| the id journal | PARA_ROOT/Archive/.removed-ids | only its header, only when missing (project-id.md «The journal») |
| vault config | REPO_ROOT/.claude/para/vault.md, areas.md; adapter.md only when TASKS is mcp | from config.example/; never overwritten |
| the plugin's block | REPO_ROOT/CLAUDE.md | between the markers only («CLAUDE.md») |
| the skills' permissions | REPO_ROOT/.claude/settings.json | merged, never replaced («Settings») |
| Obsidian settings | REPO_ROOT/.obsidian/app.json, templates.json | merged («Obsidian») |
| the user's notes and folders (B only) | from where they are into the PARA folders | moved, never edited — only the moves the approved plan lists («Scenario B») |
| project overviews (B only) | PARA_ROOT/Projects/PROJ-xxxx <title>/ | new, for a folder the user called a project («Scenario B») |
| the pair key in overviews (C only) | PARA_ROOT/Projects/, PARA_ROOT/Archive/ | the one frontmatter key renamed, nothing else (scenario-c.md) |
| the vault's planning file (C only) | REPO_ROOT/.claude/local/planning.md | new, lifted from the old tooling (scenario-c.md) |
| the old embedded tooling (C only) | REPO_ROOT/.claude/, CLAUDE.md, the old template's docs | to the macOS Trash, Tier 2 (scenario-c.md) |
Base layer
Read before acting, each as ../../para/<file>.md relative to this skill's base directory
(and ../../config.example/, ../../lang/, ../../templates/ the same way):
../../para/paths.md— always: the two roots, «Language», the adapter forTASKS. Everythinginitwrites has to agree with it.../../para/tasks.md— the operations, «Pairing», «Backend missing»../../para/adapters/mcp-generic.md— whenTASKSismcp; the adapter for any other value is named bypaths.md../../para/note-format.md— the frontmatter schema the templates must match../../para/migrations.md— the plugin's currentSCHEMAand how a migration runs../../para/claude-md-block.md— the block forCLAUDE.md../../para/confirm.md— the tiers; the plan below is howinitmeets them../../para/project-id.md— «The journal»: the header of the id journal (Step 5.1)../../config.example/vault.md,areas.md,adapter.md— the shape of the config- scenario B only:
../../para/move.md(how a move keeps links),project-id.md(issuing an id),../../para/thresholds.md(the area-staleness threshold and «The limit of mtime»),paths.md«IGNORE» ../../lang/<LANG>.mdonceLANGis known: theinit.*strings- scenario C only, and C's leftovers on a re-run:
scenario-c.mdin this skill's base directory — everything about the old layout; also../../para/move.md(the Trash)
SKILL_DIR below is this skill's base directory, as the harness announces it.
Rules for the whole run
REPO_ROOTis the session's working directory, as an absolute path (pwd). This is the one place the plugin takes a root from the working directory:initis what writes it down for every other skill. The plan says which folder it is, so a session started in the wrong place is caught before anything is written.- Nothing is written before the plan's «yes». Inspecting, asking and probing the task backend are read-only.
vault.mdis written last. It is what marks a vault as set up; an interrupted run leaves novault.mdand the next run starts over, finding the work already done.- Every step is idempotent: create when missing, merge when present, never
overwrite. Running
inittwice in a row changes nothing the second time. - Quote every path (
paths.md«Rules»).REPO_ROOTmay contain spaces. - Language. Until
LANGis chosen, speak the language the user wrote in (English when there is nothing to go by — a bare/para:init). From then on, everything is inLANGperpaths.md«Language», including the remaining questions; the plan and report headings are theinit.*strings.
Answers given in advance
$ARGUMENTS may already carry answers: the language, PARA_DIR («the default» means
the proposed one), the task backend, the Areas model, in scenario B the sorting answers
(per folder or root note, and for the duplicate names), the snapshot answer, and an
approval of the plan. Take every answer given and do not ask it again; ask only
what is missing. An answer that is not a valid choice is asked again, saying why.
An approval given in advance means: show the plan, then apply it without waiting —
but only while every line of the plan adds something (a folder, a file, a key, an
entry). A line that changes or replaces something that already exists (a differing
Obsidian value, a different CLAUDE.md block, a template in a migration, REPO_ROOT
in an existing vault.md) is Tier 2 (confirm.md): the plan is shown and init waits
for a «yes» even then.
Scenario B's moves are of two kinds, and this is the one statement of how an advance
approval covers them. A move the user decided in their own answers (B3, or the sorting
answers in $ARGUMENTS) changes where a note is, never what it says, and confirm.md
puts moving in Tier 1: the plan's «yes», given then or in advance, covers it. A move
decided by the rules alone (B2: to Archive/ or Resources/) is init's own judgement
of the user's notes: it needs a live «yes» to the plan that shows it, even when an
approval was given in advance. Either way the snapshot is offered before the moves
(Step 4). A sorting question answered in advance is not asked; one left unanswered is
asked, and nothing is applied before its answer, approval or not.
Step 1 — Inspect
Read-only. Find out what the folder is and name the scenario.
REPO_ROOT=$(pwd)
ls -A "$REPO_ROOT"
ls "$REPO_ROOT/.claude" "$REPO_ROOT/.claude/para" "$REPO_ROOT/.claude/skills" 2>/dev/null
# the old embedded layout (scenario C): its config folder or its base layer
ls -d "$REPO_ROOT/.claude/para/local" "$REPO_ROOT/.claude/para/paths.md" 2>/dev/null # old-layout
# skill folders named like the old embedded skills: reported, never C on their own
for s in inbox projects areas resources archive remove vault-lint; do
[ -f "$REPO_ROOT/.claude/skills/$s/SKILL.md" ] && echo "old skill: $s"; done
# notes: every .md not under a dot-entry of REPO_ROOT, except the repository's own files
# and templates (dot-entries are pruned relative to REPO_ROOT, so a vault that itself
# lives under a dot-directory is still seen)
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -name "*.md" -not -path "*/Templates/*" \
! -path "$REPO_ROOT/CLAUDE.md" ! -path "$REPO_ROOT/README.md" ! -path "$REPO_ROOT/CHANGELOG.md" -print | head -50
# a vault above this folder: init was started in a subfolder
d=$(dirname "$REPO_ROOT")
while [ "$d" != "/" ] && [ "$d" != "$HOME" ]; do
{ [ -e "$d/.claude/para/vault.md" ] || [ -d "$d/.obsidian" ]; } && echo "vault above: $d"
d=$(dirname "$d")
done
Stop before anything else when:
REPO_ROOTis$HOMEor/— not a vault; say so.- a vault was found above — say «run
/para:initfrom<that folder>» and stop; never set up a vault inside another one.
Then decide, in this order, and say what was found and which case it is:
- Set up —
.claude/para/vault.mdexists → «Re-run and migration» below. - Scenario C — the old embedded tooling, recognised by its old base layer or config
only (the
old-layoutline above): its config folderlocal/or its base layer filepaths.mdinside.claude/para/. → «Scenario C». A skill folder named like one of the old skills (theold skill:lines) without either is not C: go on to B or A by the rules below. Say which folder was found and thatinitleaves it alone; the plan's «not done» names it again. A.claude/para/holding only this plugin's config files (areas.md,adapter.md) and novault.mdis not C: it is a first run ofinitthat was interrupted or refused beforevault.md. Go on to B or A below as if it were not there, except that those files are the vault's config — never overwritten — and a question they already answer (question 4 forareas.md) is not asked again. The run fills in the rest. - Scenario B — notes were found (the
findabove printed anything). → «Scenario B». - Scenario A — otherwise: an empty folder, or one holding only non-note files
(an
.obsidian/of a new Obsidian vault, aCLAUDE.md). → Step 2.
Step 2 — Questions
One at a time, in this order, skipping every one answered in advance. Each question shows its choices and, where there is one, the default.
-
Language (
LANG). The choices are the languages the plugin ships: a code with bothlang/<code>.mdandtemplates/<code>/(paths.md«Language»):for f in "$SKILL_DIR"/../../lang/*.md; do l=$(basename "$f" .md) [ -d "$SKILL_DIR/../../templates/$l" ] && echo "$l"; done -
PARA folder (
PARA_DIR). Default per scenario: A —PARA, a subfolder, so the tooling and the notes stay apart; B —., so the PARA folders sit next to the user's own folders, except as «Scenario B» says..puts the PARA folders straight intoREPO_ROOT. Valid:.or one folder name — no/, not starting with., not one of the PARA folder names themselves. -
Task backend (
TASKS). The choices aretodoist,filesormcp; the first is recommended. Say in one line what each means: an app's inbox and projects; task notes in the vault, no app; any other to-do app connected over MCP. Then probe — «Probing the backend» below. A value whose probe failed is never recorded. -
Areas model (
areas.md). One question: are Areas grouped —none, or the names of the groups (for example Work, Home)? Everything else is derived and shown in the plan, where the user can change it:Key Grouped noneGROUPINGthe field name, sphereunless the user named one (the seed templates' key)noneGROUPSthe names given empty PLACEMENTAreas/<GROUP>/<title>.mdAreas/<title>.mdREVIEW_GROUPINGone block per group, groups with nothing to report dropped one flat list PLACEMENT_CHECKa note's GROUPINGfield must name the top-level folder it sits undernoneAREA_TASK_PROJECTSthe GROUPSnamesone name: the areas.review.headingstring
Probing the backend
The probe is inbox.list, run per the adapter for the chosen value (paths.md). It is
read-only; an empty inbox is a success. Print only the count, never the captures.
files— nothing to probe: the adapter is the disk, and «Backend missing» cannot happen (tasks.md).mcp— run steps 1–6 ofadapters/mcp-generic.mdnow. Step 1 finding no tools is a failed probe. Step 6 is the probe when the user agrees to it and the app has an inbox; otherwise the plan says the mapping is unverified. Steps 7–8 belong to the plan and to «Step 5» below.- Any other value — run
inbox.listper its adapter. If the adapter's tools are not in this session, or the call fails: say so in one line (tasks.md«Backend missing»), do not record the value, and offerfilesinstead — or connecting the app and running/para:initagain. Never write aTASKSvalue whose probe did not succeed, and never record one value while promising to switch later.
Step 3 — The plan
One list of everything that will be created, merged or changed, headed by the
init.plan.heading string. It is a proposal the user edits, not an announcement: an
edit makes a new plan, shown again. It ends with the init.plan.approve string, and
nothing is applied before a «yes» (or the approval given in advance, within the limit
«Answers given in advance» sets).
The plan names, in this order:
REPO_ROOT(this folder) andPARA_ROOT.- The folders to create, and the id journal when it is missing.
- The templates to copy, each with the changes «Templates» makes to it.
In scenario B, here: the moves, the projects,
IGNORE, the held duplicates and the links that will break («Scenario B», «The plan's B part»). - The config: every key of
vault.mdandareas.mdwith its value; formcp, the adapter table (mcp-generic.mdstep 7 — the plan's «yes» is that table's «yes»). CLAUDE.md,.claude/settings.json,.obsidian/app.json,.obsidian/templates.json: what is added to each. Each existing value that would change is a line of its own, showing the old and the new value.- The probe result: the backend answered (with the inbox count), or
files. - What will not be done, and why (a template already present, a value kept).
Step 4 — Snapshot (B and C only)
Before applying a plan that touches an existing vault, offer a zip copy next to it —
always asked, refusal accepted, an answer given in advance taken. It is asked in the same
message as the plan's approval (the init.snapshot string), so the user answers once:
zip_path="$(dirname "$REPO_ROOT")/$(basename "$REPO_ROOT")-backup-$(date +%Y-%m-%d).zip"
ditto -c -k --keepParent "$REPO_ROOT" "$zip_path"
Scenario A has nothing to save and skips this step.
Step 5 — Apply
In this order, each step create-when-missing / merge-when-present:
- Folders —
mkdir -peach, quoted. ThenArchive/.removed-ids, when missing, holding only the headerproject-id.md«The journal» gives; an existing one is kept as it is (scenario C's included). - Templates («Templates»).
- Scenario B: then the moves and the projects («Scenario B», «B4»).
They need the folders and
Templates/Project.mdin place, and they come before anything is written outside the PARA folders.
- Scenario B: then the moves and the projects («Scenario B», «B4»).
They need the folders and
- Obsidian settings («Obsidian»).
.claude/settings.json(«Settings»).CLAUDE.md(«CLAUDE.md»).areas.md;adapter.mdformcp(mcp-generic.mdstep 8).vault.md— last.
vault.md and areas.md are written from config.example/<file>, keeping its title,
its «Keys» table and every section after it, and dropping the paragraphs addressed to
someone filling in the form (the one opening with bold «Vault-specific», and «Fill this
in from scratch»); adapter.md is written as mcp-generic.md step 8 says. In the
table, every italic placeholder is replaced by the value in backticks — | `LANG` | `en` | — and an empty value leaves the cell empty
(IGNORE in scenario A, GROUPS under GROUPING = none); a list is the backticked
items separated by commas. REPO_ROOT is the absolute path, no ~. SCHEMA is the
current one from migrations.md.
A step that fails is reported as it is, and the steps after it still run. When a PARA
folder or a template could not be written, the vault is not there: skip areas.md,
adapter.md and vault.md (no config without a vault), report it, and the next run
starts over as a first run. Any other failure (areas.md, adapter.md, a merge into
CLAUDE.md, .claude/settings.json or .obsidian/) does not hold vault.md back: the
report names it, the skills stop on a missing areas.md (its own opening says so) or
adapter.md (paths.md «The task backend and its adapter») by themselves, and the next
run fills the gap. Writes into .claude/ may need the user's permission; a
refusal is a failure like any other, reported and never worked around.
Step 6 — Report
Headed by the init.report.heading string: what was created or changed, as short paths.
Then the init.report.skipped string and what was not done, each with its reason (a
template kept because it was already there, an Obsidian value the user kept, a backend
that did not answer). In scenario B, also the B part of the report («Scenario B»).
End with one line on what to try next: /para:inbox add …,
/para:vault-lint. When TASKS has an app behind it, say that init did not create
the AREA_TASK_PROJECTS projects in it — the user creates them when first needed.
The pieces
Templates
Copy each file of templates/<LANG>/ into PARA_ROOT/Templates/ only when that file
is missing there. A template already present belongs to the vault — the vault's
Templates/ is what note-format.md reads, and the user may have edited it — and is
never replaced, except by a migration (migrations.md). Changes made while copying, so
that the copy matches the schema of this vault (note-format.md «Frontmatter schema»):
Task.md— copied only whenTASKSisfiles. Under any other backend a task template contradicts «Tasks/is legacy» (note-format.md).Area.md— the seed's grouping keysphere:is renamed to theGROUPINGfield name; withGROUPING = nonethe line is removed.Project.md— withGROUPING = nonethearea:line is removed; underpairing: none(the adapter's,tasks.md«Pairing») thetasks_ref:line is removed.
Nothing else in a template changes.
Obsidian
.obsidian/ sits in REPO_ROOT (paths.md). Merge — read the file, set only the keys
below, keep every other key and value, write valid JSON; create the file (and the
folder) when missing. A file that is not plain JSON (comments, trailing commas, a
syntax error) is not touched: report it, as with unbalanced CLAUDE.md markers.
| File | Key | Value |
|---|---|---|
app.json | newFileLocation | "folder" |
app.json | newFileFolderPath | "<PARA_DIR>/Inbox", or "Inbox" when PARA_DIR is . (paths.md) |
app.json | alwaysUpdateLinks | true |
app.json | userIgnoreFilters | contains "CLAUDE.md" — appended, other entries kept |
templates.json | folder | "<PARA_DIR>/Templates", or "Templates" when PARA_DIR is . |
A key already holding a different value is a plan line of its own (old → new). If the user keeps the old value, keep it and say in the report what follows from it (new files from Obsidian land elsewhere; links stop updating on rename).
Settings
Merge into REPO_ROOT/.claude/settings.json — never replace it; a file that is not
plain JSON is not touched and is reported, as in «Obsidian». Add each of these to
permissions.allow when it is not there yet, keeping every other key and entry:
Bash(find:*), Bash(grep:*), Bash(stat:*), Bash(cat:*), Bash(ls:*),
Bash(head:*), Bash(tail:*), Bash(sort:*), Bash(uniq:*),
Bash(wc:*), Bash(diff:*), Bash(basename:*), Bash(date:*), Bash(tr:*)
They let these fourteen commands run without a prompt, with any arguments — and nothing
else: every other command (a move, trash, sed, awk, a write) still asks as usual.
The skills use them to read only; find in particular is used read-only, never with
-delete or an -exec that changes a file. Nothing else in the file is touched, and no
permission is removed — init only merges additions.
CLAUDE.md
The block is the text from <!-- para:begin --> to <!-- para:end --> in
claude-md-block.md, both markers included, verbatim.
- No
CLAUDE.md→ create it holding the block. CLAUDE.mdwithout the markers → append a blank line and the block at the end.- The markers present once each, the text between them identical → nothing to do.
- The markers present, the text differs → a plan line of its own (Tier 2): replace what is between them. Everything outside the markers is never touched.
- A marker missing its pair, or present twice → touch nothing; report it.
Re-run and migration
vault.md exists. The config is never overwritten, and its questions are not asked
again: their answers are in it.
- Read
vault.md. Compare itsREPO_ROOTwith this folder, both resolved (pwd -P, andcd "<value>" && pwd -P), so a symlink or a/privateprefix is not a move. If they differ, the vault has moved: a plan line of its own (Tier 2) to update that one key. - Compare
SCHEMAwithmigrations.md. Higher → stop, aspaths.md«Schema version» says. Lower or missing → the stepsmigrations.mdlists for each version up to the current one join the plan, run as it says. - Check every piece of «Step 5» against the disk with the stored values: missing
folders and id journal, missing templates (
Task.mdonly underfiles),areas.md(missing → ask question 4 only),adapter.mdundermcp(missing → themcp-generic.mdprocedure), the Obsidian keys, the permissions, theCLAUDE.mdblock, and what scenario C may have left (scenario-c.md«Leftovers on a re-run»). Only what is missing or different joins the plan. - Probe the backend (read-only, as in Step 2) and say the result; a failure is reported and changes nothing in the config.
- An answer given in advance that disagrees with
vault.mdis not applied: say which value the vault keeps, and that changing it is an edit tovault.md, not a re-run. A Tier 2 line the user declined on an earlier run (an Obsidian value, theCLAUDE.mdblock) is shown again, once per run, as a plan line; declined again, the report says the old value was kept. - A re-run never sorts notes again. Scenario B happens once, on the first run:
the
IGNORElist, the folders the user left as they are and anything added since are not reclassified, and the sorting answers in$ARGUMENTSare not used. Notes outside the PARA folders are/para:vault-lint's to report. - Nothing missing → say the
init.nothing_to_dostring and stop. Write nothing — not even a file rewritten with the same content. - Otherwise the plan, the «yes», the application and the report, as on a first run.
Scenario A — an empty folder
Steps 2, 3, 5 and 6 as above, with PARA_DIR defaulting to PARA. Every file and
folder is new, so the plan is additions only (an approval given in advance applies),
except where an existing .obsidian/ or CLAUDE.md holds a different value.
Scenario B — an existing Obsidian vault
The folder holds notes this plugin did not write. init sorts what is obvious into PARA
itself, asks about what is disputed — in groups — and moves; nothing else. Steps 2–6
run as above, with the additions below. PARA_DIR defaults to ..
The rule over everything else: a note is never changed. Not its body, not its
frontmatter, not its name, and none is deleted — note-format.md «Notes predating the
convention». Checkboxes are left as they are. The only thing done to a user's note or
folder is a plain move (move.md), only of what the approved plan lists, never onto an
existing path. Everything init writes is a new file: the PARA folders, the templates,
the project overviews, the config.
A vault with nothing to sort
A new Obsidian vault holds one note, Obsidian's own Welcome.md, at its top level. When
that is the only note found, there is nothing to sort: say so, and run scenario A
(PARA_DIR defaulting to PARA, no snapshot) with one more plan line — Welcome.md
stays where it is and joins IGNORE. The user may delete it instead; init does not.
B1 — Survey
Read-only, before the setup questions, so that question 2's default is known.
# the units: every top-level entry but the dot-entries and the repository's own files
find "$REPO_ROOT" -mindepth 1 -maxdepth 1 ! -name '.*' \
! -name CLAUDE.md ! -name README.md ! -name CHANGELOG.md | sort
# every file outside the dot-entries, with its mtime: counts per folder, newest change
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -type f -exec stat -f '%m %N' {} +
# what Obsidian says: the attachments, daily-notes and templates folders
cat "$REPO_ROOT/.obsidian/app.json" "$REPO_ROOT/.obsidian/daily-notes.json" \
"$REPO_ROOT/.obsidian/templates.json" 2>/dev/null
# notes with checkboxes; notes with an unticked one
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -type f -name '*.md' -print0 \
| xargs -0 grep -lE '^[[:space:]]*[-*+] \[[ xX]\]' /dev/null
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -type f -name '*.md' -print0 \
| xargs -0 grep -lE '^[[:space:]]*[-*+] \[ \]' /dev/null
# basenames that occur more than once
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -type f -name '*.md' -print \
| sed 's|.*/||' | LC_ALL=C sort | LC_ALL=C uniq -d
# links that carry a path: wikilinks and embeds with a folder in them, markdown links
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -type f -name '*.md' -print0 \
| xargs -0 grep -nHoE '!?\[\[[^]|#]*/[^]]*\]\]|\]\([^)]+\)' /dev/null \
| grep -vE '\]\((https?|mailto|obsidian|file):|\]\(#'
# the plugin's template names against the user's notes: init must not make a duplicate
for t in "$SKILL_DIR"/../../templates/<LANG>/*.md; do
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -type f -name "$(basename "$t")" -print; done
Only .md notes are scanned for links; canvases, bases and bookmarks are not, and the
plan says so (init.b.links_scope). A template name that is already a user's note
(the last command; Task.md counts only under TASKS = files) is asked about before
the plan, as note-format.md «Name collisions» says — init never copies a template
that would make a second note of that name.
The PARA folders already there. When REPO_ROOT already has a folder named like a
PARA folder (Projects, Areas, Resources, Archive, Inbox, Templates), .
would pour the user's folder into PARA's. . is then refused, saying which folder is
in the way, and a subfolder is proposed — PARA, or, if PARA exists too, none: the
user names one. The user's same-named folder is then an ordinary unit, classified like
any other. With PARA_DIR
a subfolder, every destination below is under PARA_ROOT as usual.
B2 — Classify
The unit is a folder, not a note. Each top-level folder is one unit with everything
under it. A folder one rule below decides as a whole moves as one. A mixed folder —
its notes do not all fall under one rule — is split once: the notes directly in it that a
rule decides go their own way, one by one; the rest of it (its other notes and its
subfolders) is one disputed unit. A move never carries a note that has a line of its
own: a mixed folder's remainder is always moved entry by entry, skipping every note
decided on its own and every held duplicate (B4) — never as one mv of the folder. Each note at the top level is a unit of its own.
Non-note files at the top level stay and join IGNORE.
The rules, first match wins:
| # | Unit | Signs | Goes |
|---|---|---|---|
| 1 | daily notes | the folder of .obsidian/daily-notes.json; or notes named by date (2026-09-01.md) | stays, IGNORE |
| 2 | attachments | the attachmentFolderPath of .obsidian/app.json; or a folder of files with no note in it | stays, IGNORE |
| 3 | the user's Obsidian templates | the folder of .obsidian/templates.json; or notes made of {{date}}, {{title}} placeholders | stays, IGNORE — never mixed into the plugin's Templates/ |
| 4 | reference | named as one (reference, cheat sheet, glossary, how-to, wiki — in any language), or reading as one: commands, definitions, links, quotes; no unticked checkbox, no date ahead | Resources/<name> |
| 5 | finished | its newest file untouched for longer than the area-staleness threshold (thresholds.md), and no sign of active work: no unticked checkbox, no date from today on | Archive/<name> |
| 6 | anything else | — | disputed: asked (B3) |
- A folder that reads as a project — a goal, an end, open checkboxes — is never obvious: it becomes a project only on the user's «yes» (rule 6).
- When every file's mtime falls inside one hour (a copy or a sync —
thresholds.md«The limit of mtime»), rule 5 decides nothing: those units are asked. - Duplicates come before the rules. Every note whose basename occurs more than once
across
REPO_ROOTstays where it is and is in no move, whatever its folder does: a folder that moves while holding one is moved entry by entry around it, and stays behind holding it (B4). The duplicates are asked about (B3). This ismove.md: a move is safe only while the basename is unique. - Collisions. A destination that already exists, or a project overview name that
already exists anywhere under
REPO_ROOT(note-format.md«Name collisions»), holds that unit: it is asked about, with the reason, and never moved onto.
B3 — Questions
After the setup questions, one message with every disputed unit, numbered:
- the folders, largest first (by notes), each as the
init.b.questionstring, with what helps to answer — how long it has been unchanged (the span, never a verdict:thresholds.md), two or three note names, how many notes carry checkboxes, andinit's guess when it has one; - the top-level notes, one line each, with the same choices — at most 10 lines; past that, the rest go on one line together, «N other notes at the top level», with the same choices for all of them (the user may still single one out);
- the duplicates: the
init.b.duplicatesstring, each name with all its paths, and theinit.b.duplicates_choicestring.
The user answers them all in one reply. Never one question per note inside a folder
— a flood of questions is as unpredictable as silent decisions. If the user wants a
folder split, they say how; init never proposes it note by note. Answers:
- project → «Projects» below.
- area →
Areas/<name>; whenGROUPINGis notnone, under the group the user names,Areas/<GROUP>/<name>(Tier 1.5 if not named). The notes move as they are; no area note is written for them — the report suggests/para:areas create. - resource →
Resources/<name>; archive →Archive/<name>. - leave as is (
init.b.leave) → stays, joinsIGNORE. - duplicates, leave → every copy stays and its path joins
IGNORE— or, for a copy left behind in a folder that otherwise moved, that folder's path, since the copy is all it still holds (otherwise/para:vault-lintreports the folder as stray); say that/para:vault-lintkeeps reporting the name until one is renamed (paths.md«IGNORE»), and, for a folder, theinit.b.ignored_folderstring. Duplicates, stop → stop the whole run: nothing is written, and the next run is a first run again.initnever renames a note itself.
Projects
A folder becomes a project only on the user's «yes» for that folder. Then:
- the id is issued per
project-id.md, one per project in plan order; - the title is the folder's name, cleaned as
note-format.md«Title and filename» says;shortis proposed in the plan (note-format.md«Filling fields»); - the new folder is
Projects/PROJ-xxxx <title>/withNotes/,Resources/,Results/(paths.md«Map»). Every entry of the old folder moves intoNotes/as it is — subfolders and attachments too — except a held duplicate. The emptied old folder is removed withrmdir, which refuses one that is not empty; neverrm -r; - the overview
PROJ-xxxx <title>.mdis written from the vault'sTemplates/Project.md(copied in Step 5.2 of this run, or the vault's own) pernote-format.md: title,short, id, the tag slug,createdtoday. Every other field and section stays blank —initadopts a folder and does not invent a goal; project.createruns only underpairing: separate(tasks.md«Pairing»), and its id goes into the new overview'stasks_ref. A failed call leaves the project unpaired and is reported (tasks.md«Backend missing»).
The plan's B part
At the plan's item 3, each block headed by its string:
init.b.moves— one line per move,from → to, grouped by destination; a folder moved whole is one line; a folder moved entry by entry says what stays behind or moves on its own line. A project line names the new folder, its id, itsshortand the overview, and — underpairing: separate— that the project is also created in the task app (project.create, «Projects»). Then the totals: moves, and notes moved — computed, not tallied: the sum of the survey's note counts for the units that move (a folder's count minus the notes that stay behind or move on their own line, each counted once).init.b.ignore— every path that joinsIGNORE, each with why.init.b.duplicates— the names, their paths, the answer.init.b.links— every link the moves may break: the note, the line, the link, then theinit.b.links_scopestring. A link may stop working when its target moves, or when it is a relative markdown link and its note moves while its target does not (a link inside a folder that moves whole keeps working). Resolve a wikilink's path fromREPO_ROOT, a markdown link's (%20is a space) from its note's folder, then fromREPO_ROOT. «None» when there are none.
B4 — Applying the B part
At Step 5.2, line by line in plan order. Each move checks first and never forces:
src="$REPO_ROOT/<from>"; dest="$PARA_ROOT/<to>"
if [ -e "$dest" ]; then echo "exists, not moved: $dest"
elif [ ! -e "$src" ]; then echo "gone, not moved: $src"
else mkdir -p "$(dirname "$dest")" && mv "$src" "$dest"; fi
The notes decided on their own move first. A folder that holds a held duplicate or a
note with a line of its own — a mixed folder's remainder always — is moved entry by
entry: mkdir -p "$dest", then the same check and mv for each entry of
find "$src" -mindepth 1 -maxdepth 1, skipping those notes (and descending into the
subfolder that holds one, if it is deeper).
Then check that nothing was lost: every note the survey found is at its old path or at
its planned new one, and the number of notes outside the dot-entries is the survey's
count plus the files init created:
find "$REPO_ROOT" -mindepth 1 -name '.*' -prune -o -type f -name '*.md' -print | wc -l
Count the notes now at the planned destinations; it must equal the plan's «notes moved»
total. A mismatch either way is reported first in the report, with the paths. The IGNORE entries go
into vault.md at Step 5.7: paths relative to REPO_ROOT, as paths.md «IGNORE»
spells the value.
The report's B part
- what moved: the count per destination, the projects with their ids, and the total checked against the plan's;
- what stays (
IGNORE), and the duplicates left in place; for a folder inIGNOREthat only holds a left-behind duplicate, theinit.b.ignored_folderstring; - the
init.b.linkslist again — these links may be broken now; - the
init.b.checkboxesstring with the count, and the folders those notes sit in; - the snapshot's path, if one was made.
Scenario C — a vault with the old embedded tooling
The vault was built from the old PARA vault template and still carries its tooling in
.claude/. C moves its config into this plugin's, renames the pair key in the project
overviews to tasks_ref, lifts planning into a file of the vault's own, and retires the
old tooling to the Trash. Read scenario-c.md in this skill's base directory and
follow it: it holds C's steps (C1–C5) in place of Steps 2–6, and it is the only place
that describes the old layout. Steps 2–6 above apply where it points to them.