@imunitic/synapse 0.0.1-test.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Index.md.template +23 -0
- package/bin/synapse-hook.cjs +19 -0
- package/bin/synapse-setup.cjs +420 -0
- package/bin/synapse.cjs +20 -0
- package/commands/synapse-design-note.md +229 -0
- package/commands/synapse-init.md +354 -0
- package/commands/synapse-note.md +196 -0
- package/commands/synapse-rebuild-diff.md +314 -0
- package/commands/synapse-rebuild-full.md +152 -0
- package/commands/synapse-status.md +144 -0
- package/commands/synapse-task-note.md +133 -0
- package/commands/synapse-vault-tidy.md +187 -0
- package/harness/claude/hooks.json +54 -0
- package/harness/codex/hooks.json +54 -0
- package/harness/codex/skills/synapse-design-note/SKILL.md +236 -0
- package/harness/codex/skills/synapse-init/SKILL.md +354 -0
- package/harness/codex/skills/synapse-note/SKILL.md +212 -0
- package/harness/codex/skills/synapse-rebuild-diff/SKILL.md +315 -0
- package/harness/codex/skills/synapse-rebuild-full/SKILL.md +149 -0
- package/harness/codex/skills/synapse-status/SKILL.md +146 -0
- package/harness/codex/skills/synapse-task-note/SKILL.md +133 -0
- package/harness/codex/skills/synapse-vault-tidy/SKILL.md +187 -0
- package/harness/opencode/plugin/synapse.js +164 -0
- package/lib/obsidian-mcp-refresh.cjs +303 -0
- package/lib/resolve-binaries.cjs +54 -0
- package/package.json +26 -0
- package/skills/synapse-node/SKILL.md +211 -0
- package/skills/synapse-node-authoring/SKILL.md +188 -0
- package/skills/synapse-node-format/SKILL.md +205 -0
- package/skills/synapse-orientation/SKILL.md +468 -0
- package/skills/synapse-query/SKILL.md +99 -0
- package/skills/synapse-task/SKILL.md +261 -0
- package/skills/synapse-vault/SKILL.md +107 -0
- package/synapse-claude.md +220 -0
- package/synapse-fence-languages.conf.template +24 -0
- package/synapse-ignore-files.conf.template +45 -0
- package/synapse-module-boilerplate.conf.template +24 -0
- package/synapse-projects.conf.template +14 -0
- package/synapse-prompt-stopwords.conf.template +594 -0
- package/synapse.conf.template +23 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: synapse-rebuild-full
|
|
3
|
+
description: Wipe a repo's Synapse namespace and rebuild it from scratch via /synapse-init, for the case where diff-driven triage isn't the right tool — the graph has drifted too far, or a clean rebuild is just wanted directly. Preserves any hand-written `## Notes` content first and auto-merges what it can back into the new nodes. For ordinary same-branch drift, use /synapse-rebuild-diff instead — it's cheaper and never deletes a node outright.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Synapse Rebuild Full: Wipe and Rebuild a Namespace From Scratch
|
|
7
|
+
|
|
8
|
+
`/synapse-rebuild-diff` triages drift node by node — reseat, patch, or re-orient — and never deletes
|
|
9
|
+
anything. This command is the other tool: it deletes the current namespace outright and rebuilds it
|
|
10
|
+
from nothing via `/synapse-init`'s own First-time-build procedure. Reach for it when the graph has
|
|
11
|
+
drifted past the point where triage is worth it (most nodes would land in *re-orient* anyway), when
|
|
12
|
+
the namespace is corrupted or was built badly, or when a clean rebuild is simply what's wanted —
|
|
13
|
+
never as a reflex for ordinary drift, which `/synapse-rebuild-diff` handles more cheaply and without
|
|
14
|
+
touching anything irreplaceable.
|
|
15
|
+
|
|
16
|
+
**Unlike `/synapse-rebuild-diff`, this command does not care which branch is checked out beyond the
|
|
17
|
+
ordinary sense.** It isn't diffing against anything — it resolves `{repo}@{branch}` for whatever's
|
|
18
|
+
currently checked out and rebuilds *that* namespace, exactly as `/synapse-init` does. There is no
|
|
19
|
+
branch-identity guardrail here because there is nothing to compare against; the branch you're on is
|
|
20
|
+
simply the branch being rebuilt.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/synapse-rebuild-full
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
No arguments — always operates on the repo and branch containing the current working directory.
|
|
29
|
+
|
|
30
|
+
## Prerequisites
|
|
31
|
+
|
|
32
|
+
- Requires the `obsidian` MCP server (`mcp__obsidian__*` tools). If unreachable, say so and stop —
|
|
33
|
+
same requirement `/synapse-init` has.
|
|
34
|
+
- Must be run from inside a git repository, on a named branch (not detached `HEAD`) — same
|
|
35
|
+
requirement `/synapse-init` has, since `synapse_namespace` needs a branch to key on.
|
|
36
|
+
|
|
37
|
+
## Procedure
|
|
38
|
+
|
|
39
|
+
### 1. Resolve the namespace
|
|
40
|
+
|
|
41
|
+
Same resolution `/synapse-init` uses: repo root (`git rev-parse --show-toplevel`), namespace key
|
|
42
|
+
(`{repo}@{branch}`, printed by `synapse namespace`), remote (for the `Index.md`
|
|
43
|
+
verification field).
|
|
44
|
+
|
|
45
|
+
Check whether `synapse/{repo}@{branch}/Index.md` exists.
|
|
46
|
+
|
|
47
|
+
- **Doesn't exist** → there is nothing to wipe. This is just a first build, not a rebuild — hand off
|
|
48
|
+
directly to `/synapse-init` and stop here. Do not run the wipe step at all in this case; it would
|
|
49
|
+
only fail on a directory that isn't there.
|
|
50
|
+
- **Exists, `remote` mismatches** → belongs to a different repo sharing this key. Same refusal
|
|
51
|
+
`/synapse-init` gives in this case: stop, name both remotes, do not touch it.
|
|
52
|
+
- **Exists, `remote` matches** → continue to step 2.
|
|
53
|
+
|
|
54
|
+
### 2. Preview the wipe and get explicit confirmation
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
~/.synapse graph-wipe --dry-run
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Report its output plainly: node count, and — the one thing this step exists to surface — how many
|
|
61
|
+
nodes carry hand-written `## Notes` content that's about to be deleted, and which ones. `## Notes` is
|
|
62
|
+
human-authored, lives outside every generated fence, and no rebuild regenerates it; a wipe is the one
|
|
63
|
+
operation in this pair of commands that actually deletes files rather than overwriting them with
|
|
64
|
+
preservation, so it earns an explicit stop here that `/synapse-rebuild-diff` deliberately does not
|
|
65
|
+
have.
|
|
66
|
+
|
|
67
|
+
**Get an explicit yes before continuing.** This is a hard-to-reverse filesystem operation on content
|
|
68
|
+
that includes irreplaceable human prose — do not proceed past this point on an assumption, even if
|
|
69
|
+
the human is the one who typed `/synapse-rebuild-full` in the first place. Typing the command signals
|
|
70
|
+
intent to rebuild; it is not itself confirmation of a delete that touches N nodes with hand-written
|
|
71
|
+
notes attached, which the human hasn't seen a number for yet.
|
|
72
|
+
|
|
73
|
+
### 3. Wipe
|
|
74
|
+
|
|
75
|
+
Once confirmed:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
~/.synapse graph-wipe
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This deletes `synapse/{repo}@{branch}/` and, if any node had non-empty `## Notes`, first dumps that
|
|
82
|
+
content verbatim to `scratchpad/{repo}@{branch} — preserved notes before full rebuild.md`. See
|
|
83
|
+
`synapse graph-wipe`'s own header for the exact mechanics (belt-and-braces path check, same
|
|
84
|
+
discipline `synapse graph-clean` uses for its own deletion).
|
|
85
|
+
|
|
86
|
+
### 4. Rebuild from scratch
|
|
87
|
+
|
|
88
|
+
Run `/synapse-init`'s **First-time build** procedure (its steps 1–8) against the now-empty namespace,
|
|
89
|
+
by reference rather than repeating it here — enumerate, read hint files, orientation pass, cluster
|
|
90
|
+
into `manifest.tsv`, gate, write each node, build `_index.bin`, build `Index.md`. Same command, same
|
|
91
|
+
judgment calls, nothing rebuild-specific about this phase: from the namespace's perspective this is
|
|
92
|
+
identical to a first build, because as of step 3 it is one.
|
|
93
|
+
|
|
94
|
+
### 5. Merge preserved notes back
|
|
95
|
+
|
|
96
|
+
Skip this step entirely if step 2 found nothing preserved (no staging note was created).
|
|
97
|
+
|
|
98
|
+
**Only once the new `Index.md` exists** — not during clustering, not node-by-node as nodes are
|
|
99
|
+
written. Read the staging note (`scratchpad/{repo}@{branch} — preserved notes before full rebuild.md`)
|
|
100
|
+
and classify each preserved note's old title + content against the finished new node list, the same
|
|
101
|
+
technique `/synapse-init`'s `_unassigned` sweep already uses for classifying files against an existing
|
|
102
|
+
node list: read the note against the new summaries, judge which node it best fits.
|
|
103
|
+
|
|
104
|
+
- **Confident match** → append the note's content into that node's `## Notes` section (every node
|
|
105
|
+
written by `synapse write-node` already carries one, empty if nothing else was there — never a
|
|
106
|
+
"create the section" case) with a one-line provenance breadcrumb: `(carried over from "{old node
|
|
107
|
+
title}" during full rebuild on {date})`. The note is losing its original context by moving to a new
|
|
108
|
+
home, and that breadcrumb is the only way a future reader recovers why it's there.
|
|
109
|
+
|
|
110
|
+
**Report the placement even though it succeeded.** Say which old node's notes went to which new
|
|
111
|
+
node, for every single one, not only the ones that failed to place. A wrong auto-placement is most
|
|
112
|
+
dangerous exactly when it's silent — this is the one class of content in the whole system marked
|
|
113
|
+
irreplaceable, and "it succeeded" is not the same claim as "it succeeded correctly."
|
|
114
|
+
- **No confident match** → leave it in the staging note, and say why: no equivalent concept survived
|
|
115
|
+
the re-cluster, or more than one new node looked equally plausible. Do not guess past a stated
|
|
116
|
+
uncertainty here — a note in the wrong node is worse than a note sitting in scratchpad waiting for a
|
|
117
|
+
human to place it.
|
|
118
|
+
|
|
119
|
+
Once every preserved note has been classified: if every one found a confident home, delete the
|
|
120
|
+
staging note — nothing is left needing manual attention. If any remain unplaced, leave the staging
|
|
121
|
+
note live containing only the leftovers, trimmed of everything that did get merged.
|
|
122
|
+
|
|
123
|
+
### 6. Report
|
|
124
|
+
|
|
125
|
+
- Old node count vs. new node count.
|
|
126
|
+
- Whether any notes were preserved, and the outcome of every single one from step 5 (merged where, or
|
|
127
|
+
left for manual placement and why) — not just a summary count.
|
|
128
|
+
- If the staging note still exists, say so explicitly and give its path — it needs a human look.
|
|
129
|
+
|
|
130
|
+
## Guardrails
|
|
131
|
+
|
|
132
|
+
- **Never wipe without running `--dry-run` first and getting explicit confirmation on its output.**
|
|
133
|
+
The preview step exists specifically so "how many notes are about to be deleted" is answered before
|
|
134
|
+
it happens, not after.
|
|
135
|
+
- **Never invent a placement for a preserved note that isn't a confident match.** Leaving it in
|
|
136
|
+
scratchpad, flagged, is the correct outcome when nothing else is — don't fill the gap with a guess
|
|
137
|
+
to make the report look cleaner.
|
|
138
|
+
- **Never merge into a node's generated region.** The merge target is always `## Notes`, appended, never
|
|
139
|
+
touching anything inside the `<!-- synapse:generated:start -->`…`<!-- synapse:generated:end -->`
|
|
140
|
+
fence — that region belongs to `synapse write-node` alone.
|
|
141
|
+
- **Never treat this as the default repair path.** `/synapse-rebuild-diff` is cheaper, preserves every
|
|
142
|
+
node rather than deleting them, and is the right tool for ordinary drift. Use this command when
|
|
143
|
+
triage genuinely isn't worth it, not as a heavier habit that replaces the lighter one.
|
|
144
|
+
|
|
145
|
+
## Integration
|
|
146
|
+
|
|
147
|
+
- Delegates the actual rebuild to `/synapse-init`'s First-time-build procedure — this command owns
|
|
148
|
+
only the wipe-with-preservation step before it and the note-merge step after it.
|
|
149
|
+
- The wipe itself is `synapse graph-wipe` (via `synapse graph-wipe`), mirroring
|
|
150
|
+
`synapse graph-clean` as the only other destructive tool in the system.
|
|
151
|
+
- Resolves the namespace the same way `/synapse-init` and `/synapse-rebuild-diff` do -- one chain, in `core/identity.zig`
|
|
152
|
+
— never re-derives repo/branch/remote independently.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Print a read-only report of what in Synapse Vault currently needs a human decision -- design notes still Discussing, design notes marked Ready with no compiled task yet, design notes with open questions, task notes with unchecked items, and task notes stuck in REVIEW. Use whenever the user wants a status check on the vault ("what's outstanding", "what needs my attention", "vault status", "what did we leave open"). Never modifies anything -- a report only, not a task-management action. Not for creating, continuing, or listing a specific note kind (that's synapse-note/synapse-design-note/synapse-task-note's own --list modes) -- this is the one cross-cutting view over all of them at once.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Synapse Status: Vault-Wide Attention Report
|
|
6
|
+
|
|
7
|
+
A read-only sweep over Synapse Vault answering one question: what currently needs a human decision?
|
|
8
|
+
Five categories, one pass, printed as plain chat text -- an org-agenda-style check-in, not a document
|
|
9
|
+
to hand to someone else and not a live dashboard (a published Artifact has no route to the local
|
|
10
|
+
Obsidian REST API, so nothing here is ever presented that way). Run it on demand, or from a
|
|
11
|
+
scheduled `/loop`/cron invocation of this same command -- never wired into `SessionStart`: two of
|
|
12
|
+
the five categories need a per-note body read, not just a frontmatter check, and unlike `Index.md`
|
|
13
|
+
this report is a periodic human check-in, not something the agent needs injected every session to
|
|
14
|
+
behave correctly.
|
|
15
|
+
|
|
16
|
+
Scoped to `designs/`/`tasks/` only -- the two folders `/synapse-design-note`/`/synapse-note --task`
|
|
17
|
+
structurally require, so every Synapse install has them in the same shape. The free-form taxonomy
|
|
18
|
+
(`inbox/`/`research/`/`scratchpad/`) is per-install customizable (see `Index.md`), not guaranteed to
|
|
19
|
+
exist or mean the same thing across installs, and `inbox/` specifically is for the vault owner's own
|
|
20
|
+
periodic look on their own schedule -- this report doesn't cover it.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/synapse-status # Print the current vault status report
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
Requires the `obsidian` MCP server (`mcp__obsidian__*` tools). If unreachable, say so and stop --
|
|
31
|
+
there is no local-file fallback.
|
|
32
|
+
|
|
33
|
+
## Producing the report
|
|
34
|
+
|
|
35
|
+
Run all five queries in parallel where the tool call shape allows it; none depends on another's
|
|
36
|
+
result.
|
|
37
|
+
|
|
38
|
+
**1. Design notes still `Discussing`.** Design notes carry status in-body under `## Status`, not in
|
|
39
|
+
frontmatter (unlike task notes) -- a content match, scoped to `designs/`:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
{"and": [
|
|
43
|
+
{"glob": ["designs/*", {"var": "path"}]},
|
|
44
|
+
{"regexp": ["## Status\\nDiscussing", {"var": "content"}]}
|
|
45
|
+
]}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**2. `Ready` design notes with no compiled task yet.** `/synapse-task-note`'s own "Linking back"
|
|
49
|
+
step patches a compiled design note with a `> Compiled task: [[...]]` line right after its title --
|
|
50
|
+
"`Ready` and missing that line" is a direct signal, not fuzzy title-matching against `tasks/`:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
{"and": [
|
|
54
|
+
{"glob": ["designs/*", {"var": "path"}]},
|
|
55
|
+
{"regexp": ["## Status\\nReady", {"var": "content"}]},
|
|
56
|
+
{"!": [{"regexp": ["Compiled task:", {"var": "content"}]}]}
|
|
57
|
+
]}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**3. Design notes (any status) with a non-empty `## Open Questions`.** Match the heading followed by
|
|
61
|
+
at least one bullet -- a heading with nothing under it (fully pruned, per the Ready-gate convention
|
|
62
|
+
`/synapse-design-note` now follows) doesn't count as open. Since this section spans every status,
|
|
63
|
+
each line in the composed report also shows *which* status the note is currently in:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
{"and": [
|
|
67
|
+
{"glob": ["designs/*", {"var": "path"}]},
|
|
68
|
+
{"regexp": ["## Open Questions\\n- ", {"var": "content"}]}
|
|
69
|
+
]}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`regexp` is boolean-only (no captured groups), so getting each match's actual status needs a second
|
|
73
|
+
pass. Run the same query three more times, `and`-ed with `{"regexp": ["## Status\\nDiscussing", ...]}`
|
|
74
|
+
/ `Ready` / `Reference` respectively, to sort the matches from the first query into the three known
|
|
75
|
+
statuses without a body read. **A design note written before `## Status` was standardized on those
|
|
76
|
+
exact three words can carry free text there instead** (e.g. `Superseded by [[...]]`) -- it matches
|
|
77
|
+
the first query but none of the three status-scoped ones. Whatever's left over after removing the
|
|
78
|
+
Discussing/Ready/Reference matches from the first query's full result set is exactly this case:
|
|
79
|
+
report those under a fourth bucket, "Other", rather than silently dropping them -- surfacing an odd
|
|
80
|
+
note beats losing it, the same reasoning behind reporting a 0-unchecked task instead of hiding it
|
|
81
|
+
(see Query 4 below).
|
|
82
|
+
|
|
83
|
+
**4. Open task notes with at least one unchecked item.** Task notes carry `status:` in frontmatter,
|
|
84
|
+
unlike design notes -- filter there first:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
{"in": [{"var": "frontmatter.status"}, ["TODO", "IN-PROGRESS"]]}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then `mcp__obsidian__vault_read` each match and count `- [ ]` lines in the body. A match with zero
|
|
91
|
+
unchecked lines (a checklist that's fully checked but hasn't been promoted to `REVIEW` yet) is still
|
|
92
|
+
worth surfacing -- report it under this section with its count shown as 0, rather than silently
|
|
93
|
+
dropping it, since that state itself is worth a human noticing.
|
|
94
|
+
|
|
95
|
+
**5. Task notes stuck in `REVIEW`.** Frontmatter-only, no body read needed -- a fully-checked
|
|
96
|
+
checklist waiting specifically on human sign-off, since `synapse-task` deliberately never promotes a
|
|
97
|
+
note past `REVIEW` on its own:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
{"==": [{"var": "frontmatter.status"}, "REVIEW"]}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Composing the report
|
|
104
|
+
|
|
105
|
+
One section per category, in the order above. Each line names the note (title, or filename if no
|
|
106
|
+
`title` frontmatter) plus the one identifying detail that category needs. The Open Questions section
|
|
107
|
+
is the one place a note's status also belongs on the line -- every other section's heading already
|
|
108
|
+
implies it (the "Discussing" section only ever holds `Discussing` notes), but Open Questions spans
|
|
109
|
+
every status (`Discussing`/`Ready`/`Reference`/the "Other" catch-all from Query 3 above), so put the
|
|
110
|
+
status first, before the title, so it's the first thing scanned:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
## Discussing
|
|
114
|
+
- {title}
|
|
115
|
+
|
|
116
|
+
## Ready, not yet compiled
|
|
117
|
+
- {title}
|
|
118
|
+
|
|
119
|
+
## Open questions
|
|
120
|
+
- **{status}** — {title}
|
|
121
|
+
|
|
122
|
+
## In progress (unchecked items)
|
|
123
|
+
- {title} ({N} unchecked)
|
|
124
|
+
|
|
125
|
+
## Awaiting review
|
|
126
|
+
- {title}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Omit a section entirely when it has zero matches -- matching `/synapse-note --list`'s own convention
|
|
130
|
+
of leaving out empty headers -- rather than printing five headers with nothing under most of them. If
|
|
131
|
+
every category is empty, say so in one line ("Vault is clear -- nothing outstanding.") instead of five
|
|
132
|
+
empty headers.
|
|
133
|
+
|
|
134
|
+
Print the report directly in the response, not left only in tool-call output the user would have to
|
|
135
|
+
go dig for.
|
|
136
|
+
|
|
137
|
+
## Constraints
|
|
138
|
+
|
|
139
|
+
- Read-only end to end. Never calls `vault_write`/`vault_patch`/`vault_move`/`vault_delete`/
|
|
140
|
+
`vault_copy` -- if a step here ever seems to need one, that step is out of scope, not a case to
|
|
141
|
+
special-case around.
|
|
142
|
+
- No Artifact/web-UI output. No `SessionStart` wiring. Not a new binary CLI subcommand -- every
|
|
143
|
+
category above is a plain mechanical query already reachable through `mcp__obsidian__*` tools.
|
|
144
|
+
- Scoped to `designs/`/`tasks/` only -- never `inbox/`/`research/`/`scratchpad/`.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Compile a Ready design note into a single tracked, checklist-based task note in Synapse Vault — delegates the actual note creation to synapse-note --task under the hood. Use whenever the user wants to turn a settled design discussion into actionable, tracked work ("let's compile a task note", "let's turn this into a task", "make this a task now") — not for starting or continuing the design discussion itself (that's synapse-design-note).
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Synapse Task Note: Compile a Design into a Tracked Checklist
|
|
6
|
+
|
|
7
|
+
Compiles a `Ready` design note (from `/synapse-design-note`) into a single tracked task, using the
|
|
8
|
+
vault's existing task-tracking machinery instead of a bespoke format: creation goes through
|
|
9
|
+
`/synapse-note --task`, and status transitions from then on belong entirely to the `synapse-task`
|
|
10
|
+
skill. This command's only job is the compile step — turning a design into an ordered checklist —
|
|
11
|
+
not tracking progress itself.
|
|
12
|
+
|
|
13
|
+
One design compiles into **one** task note, one `task_id`, and the checklist items *are* the
|
|
14
|
+
steps — matching how every other task in the vault already works (see `synapse-task`'s "Task file
|
|
15
|
+
structure").
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
/synapse-task-note "topic" # Compile the task note for a Ready design note
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
No `--continue`/`--list` here — once created, the task note's own progress (checked items,
|
|
24
|
+
`status:` frontmatter) is what `/synapse-note --list` and the `synapse-task` skill already track.
|
|
25
|
+
Use those instead of reinventing a parallel view.
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
|
|
29
|
+
- Requires a matching Obsidian design note (`designs/`) with `Status: Ready`.
|
|
30
|
+
- No matching note → "No Ready design note found for '{topic}'. Run
|
|
31
|
+
`/synapse-design-note \"{topic}\"` first." Never generate a checklist from scratch.
|
|
32
|
+
- Matching note but `Status: Discussing` → "Design note for '{topic}' is still in Discussing. Finish
|
|
33
|
+
it first."
|
|
34
|
+
- Matching note but `Status: Reference` → "Design note for '{topic}' concluded as Reference —
|
|
35
|
+
nothing to compile. Reopen it with `/synapse-design-note \"{topic}\"` and mark it Ready if that's
|
|
36
|
+
changed."
|
|
37
|
+
- A task note already exists for this design (check the design note's `> Compiled task:` annotation,
|
|
38
|
+
or `search_query` for a `tasks/` note linking to it) → show its current state (title, `status:`,
|
|
39
|
+
checked/total) and ask: view it, or recompile (only on explicit confirmation — recompiling rewrites
|
|
40
|
+
the checklist, so any progress on items that no longer exist is lost).
|
|
41
|
+
|
|
42
|
+
**A recompile updates the existing note in place. It never creates a second one and never bumps
|
|
43
|
+
the `task_id`.** One design has exactly one task note for its whole life; a design that gets
|
|
44
|
+
revised mid-implementation is the normal case, not a new task. Preserve `task_id`, `created`,
|
|
45
|
+
`status`, the filename, and any `## Notes` content the human added; replace the checklist and the
|
|
46
|
+
pre-implementation notes. Carry forward `- [x]` marks for items that survive the recompile
|
|
47
|
+
unchanged — work already done doesn't become undone because the plan around it grew.
|
|
48
|
+
|
|
49
|
+
Record *why* in the note itself, in one line at the top of the body: what the old plan got wrong
|
|
50
|
+
and what changed. A recompiled task with no explanation reads like a plan that was always this
|
|
51
|
+
shape, which hides the fact that implementation found a gap.
|
|
52
|
+
|
|
53
|
+
## Compiling the checklist
|
|
54
|
+
|
|
55
|
+
1. Read the design note in full.
|
|
56
|
+
2. Break the approach into an ordered list of small, sequential, independently-completable steps.
|
|
57
|
+
3. For each step, write it the way `synapse-task`'s own checklist convention expects: a short
|
|
58
|
+
`- [ ] {Do}` line; for substantive steps (type definitions, API surfaces, interface signatures)
|
|
59
|
+
add a one-line nested description plus a fenced code block showing the exact interface, per that
|
|
60
|
+
skill's "Inline code examples in checklist items". Don't invent separate Files/Tests/AC fields —
|
|
61
|
+
fold what matters into the item's own description instead (e.g. "...; test: X returns Y").
|
|
62
|
+
4. Note explicit exclusions — things a reasonable implementer might also attempt that are out of
|
|
63
|
+
scope — for the `## Notes` section below, not a separate heading.
|
|
64
|
+
|
|
65
|
+
## Creating the note
|
|
66
|
+
|
|
67
|
+
Follow `/synapse-note`'s task-mode procedure exactly (its "Creating the note" section) — don't
|
|
68
|
+
duplicate that scaffolding here, just supply its inputs:
|
|
69
|
+
|
|
70
|
+
- **Title:** a short, plain description of the compiled plan (e.g. "Rollup direct storage
|
|
71
|
+
implementation") — `/synapse-note --task` resolves the project prefix and `task_id` and prepends
|
|
72
|
+
them itself.
|
|
73
|
+
- **Project:** derive from the source design note's `project:` frontmatter — that prefix is already
|
|
74
|
+
resolved (the design note went through `/synapse-design-note`'s resolution when it was created,
|
|
75
|
+
which reads/appends the resolved `synapse-projects.conf`, per `/synapse-note`'s tiered lookup),
|
|
76
|
+
so supply it directly instead of
|
|
77
|
+
re-deriving or re-asking. This is the *prefix* (`ecs`, `sb`, ...), not the `tasks/{project}/`
|
|
78
|
+
folder name — `/synapse-note --task`'s own "Resolving the project folder" step turns it into
|
|
79
|
+
the folder name. Never hardcode a specific project/prefix pair in this command's own
|
|
80
|
+
instructions — the conf file is machine-local and deliberately outside the portable
|
|
81
|
+
Synapse package, so projects from unrelated contexts (e.g. personal vs. work) must
|
|
82
|
+
never end up in the same place.
|
|
83
|
+
- **Body:** the checklist from "Compiling the checklist" above, under the single top-level heading —
|
|
84
|
+
exactly the structure `synapse-task`'s "Task file structure" requires (no `## Step` sub-headings).
|
|
85
|
+
- **`## Notes` (pre-implementation):** populate per `synapse-task`'s own convention —
|
|
86
|
+
- Design reference: `[[{design note title}]]`
|
|
87
|
+
- Key constraints the implementor must not miss
|
|
88
|
+
- Deliberate exclusions (from step 4 above) and why
|
|
89
|
+
|
|
90
|
+
## Linking back
|
|
91
|
+
|
|
92
|
+
After creation, patch the design note: add `> Compiled task: [[{task note title}]]` near the top
|
|
93
|
+
(via `mcp__obsidian__vault_patch`) — a small annotation line, same idea as any other cross-reference
|
|
94
|
+
you'd drop near a note's title. This is a one-time link; the design note's `Status` itself does not
|
|
95
|
+
change and does not need to track the task's progress — Obsidian's own backlinks panel already
|
|
96
|
+
surfaces the live task note from the design note, and the task's `status:` frontmatter is the
|
|
97
|
+
authoritative progress signal from here on.
|
|
98
|
+
|
|
99
|
+
## Confirm
|
|
100
|
+
|
|
101
|
+
Report the created path and title, and remind the user that `status:` transitions (TODO →
|
|
102
|
+
IN-PROGRESS → REVIEW) happen automatically via the `synapse-task` skill once work starts — this
|
|
103
|
+
command doesn't set or watch that field itself.
|
|
104
|
+
|
|
105
|
+
## Integration
|
|
106
|
+
|
|
107
|
+
- Reads `/synapse-design-note`'s `Ready` output only — never invents a design.
|
|
108
|
+
- Delegates note creation to `/synapse-note --task` and status tracking to the `synapse-task` skill
|
|
109
|
+
— this command owns only the design → checklist compile step.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Example
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
User: /synapse-task-note "rollup direct storage"
|
|
117
|
+
|
|
118
|
+
Claude: [reads designs/{PROJECT} — Rollup direct storage.md, Status: Ready]
|
|
119
|
+
[no existing linked task note]
|
|
120
|
+
|
|
121
|
+
Compiled checklist:
|
|
122
|
+
- [ ] Add ReplacingMergeTree target table for the daily rollup
|
|
123
|
+
- [ ] Point the rollup MV at the new table instead of AggregatingMergeTree
|
|
124
|
+
- [ ] Migrate existing daily/monthly rollups (drop+recreate, sort-key change)
|
|
125
|
+
- [ ] Update the 5 retrofitted dashboards' rollup queries to drop argMaxMerge
|
|
126
|
+
|
|
127
|
+
Project already known: {PROJECT} → {prefix}-005.
|
|
128
|
+
|
|
129
|
+
Created: tasks/{PROJECT}/{prefix}-005 — Rollup direct storage implementation.md
|
|
130
|
+
Linked back from designs/{PROJECT} — Rollup direct storage.md.
|
|
131
|
+
|
|
132
|
+
Status transitions happen automatically via the synapse-task skill once you start work.
|
|
133
|
+
```
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run one on-demand vault-tidy pass over Synapse Vault -- surface recategorization candidates (notes that no longer fit their folder, clusters that deserve a new top-level category, categories that have gone stale) into a single inbox/ proposal, silently fill in the one class of missing-frontmatter default that has an unambiguous fix, and fold the rest of note health (broken links, orphaned notes, duplicate titles) into that same proposal since none of them has a safe mechanical repair either. Use whenever the user wants the vault's own organization checked or tidied ("tidy the vault", "check vault health", "any notes drifted out of their folder", "find orphaned/duplicate notes"). Never invoked automatically -- no SessionStart wiring, no autonomous scheduling; run it yourself, or under your own /loop if you want a cadence. Scoped to everything except designs/, tasks/, and synapse/ (the code graph's own generated namespace), which stay entirely /synapse-status's (the first two) or /synapse-init's (the third) territory.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Synapse Vault Tidy: Recategorization Proposals and Note-Health Fixes
|
|
6
|
+
|
|
7
|
+
A single on-demand pass over Synapse Vault's free-form taxonomy (`inbox/`/`research/`/`scratchpad/`/
|
|
8
|
+
whatever a given install's `Index.md` currently lists) that a person would otherwise have to notice
|
|
9
|
+
and fix by hand: a note's folder fit drifting since it was filed, several notes sharing enough in
|
|
10
|
+
common to deserve a category of their own, a category that stopped earning its keep, a link that
|
|
11
|
+
broke when its target moved, a note nothing points to or from, two notes that are really the same
|
|
12
|
+
thing under slightly different titles. Nothing here runs unprompted — it exists because nothing else
|
|
13
|
+
in the vault periodically re-checks any of this, not because it's meant to run behind the user's
|
|
14
|
+
back.
|
|
15
|
+
|
|
16
|
+
Never touches `designs/`, `tasks/`, or `synapse/` — the first two are `/synapse-design-note`'s and
|
|
17
|
+
`/synapse-note --task`'s own interface for directing project work (any gap found there is
|
|
18
|
+
`/synapse-status`'s territory, not this command's); `synapse/` is the code graph's own generated
|
|
19
|
+
namespace, a different note kind entirely, owned and kept fresh by `/synapse-init` and the
|
|
20
|
+
`synapse-node`/`synapse-node-format` skills. `Index.md` and any vault-root `README.md` are also out
|
|
21
|
+
of scope — foundational files, not taxonomy notes.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
/synapse-vault-tidy # Run one vault-tidy pass
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Prerequisites
|
|
30
|
+
|
|
31
|
+
Requires the `obsidian` MCP server (`mcp__obsidian__*` tools). If unreachable, say so and stop —
|
|
32
|
+
there is no local-file fallback.
|
|
33
|
+
|
|
34
|
+
No compiled code anywhere in this command. `adapters/obsidian/store.zig`'s `ObsidianStore` does now
|
|
35
|
+
implement `read`/`list`/`search`, but for `synapse frontmatter get`/`set`'s narrow one-field-at-a-time
|
|
36
|
+
use — a full-vault tidy sweep, reading every note's whole body to judge category, tags and broken
|
|
37
|
+
links, is a different shape of work entirely, and stays on these same MCP tools rather than the
|
|
38
|
+
compiled binary, the same way `/synapse-status` and `/synapse-rebuild-diff`'s vault-side checks
|
|
39
|
+
already work. The one exception is Step 3's broken-link history check, a plain `git log` call (via
|
|
40
|
+
Bash, not a compiled tool) against the vault's own local repo when one exists — best-effort, never
|
|
41
|
+
a hard requirement.
|
|
42
|
+
|
|
43
|
+
## Step 1: Inventory sweep
|
|
44
|
+
|
|
45
|
+
Enumerate every note in scope and read each one exactly once — every later step reuses this same
|
|
46
|
+
sweep rather than re-reading anything.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
{"and": [
|
|
50
|
+
{"!": [{"glob": ["designs/*", {"var": "path"}]}]},
|
|
51
|
+
{"!": [{"glob": ["tasks/*", {"var": "path"}]}]},
|
|
52
|
+
{"!": [{"glob": ["synapse/*", {"var": "path"}]}]},
|
|
53
|
+
{"!=": [{"var": "path"}, "Index.md"]},
|
|
54
|
+
{"!=": [{"var": "path"}, "README.md"]}
|
|
55
|
+
]}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`mcp__obsidian__search_query` with that filter, then `mcp__obsidian__vault_read` each match. Keep
|
|
59
|
+
the full result (`path`, `frontmatter`, `tags`, `links`, `backlinks`, `unresolvedLinks`, `stat`,
|
|
60
|
+
`content`) per note — Steps 2–4 below only ever reason over the structured fields, never `content`
|
|
61
|
+
itself; the judgment layer in Step 5 is the first point anything actually reads note *bodies*, and
|
|
62
|
+
only for the subset flagged by then. `content` is fetched for free in this same call regardless, so
|
|
63
|
+
there's no separate "light" read to bother with.
|
|
64
|
+
|
|
65
|
+
## Step 2: Frontmatter defaults (fixed directly)
|
|
66
|
+
|
|
67
|
+
The only note-health fix applied silently, because it's the only one with exactly one correct
|
|
68
|
+
answer per note. Design and task notes are out of scope entirely; the plain/free-form schema is
|
|
69
|
+
just two fields (`title`, `created` — see `synapse-note`'s bare-mode format), both derivable without
|
|
70
|
+
guessing:
|
|
71
|
+
|
|
72
|
+
- Missing `title` → the filename with its `.md` extension stripped.
|
|
73
|
+
- Missing `created` → `stat.ctime`, formatted `YYYY-MM-DD HH:MM` to match every other note's
|
|
74
|
+
convention.
|
|
75
|
+
|
|
76
|
+
Apply via read-modify-write on the whole file (`vault_read` → edit the one frontmatter line in the
|
|
77
|
+
returned content → `vault_write` the whole file back) — never `vault_patch` with
|
|
78
|
+
`targetType: frontmatter`, which re-serializes the entire YAML block and silently reformats
|
|
79
|
+
unrelated fields, the same hazard `synapse-vault`/`synapse-task` already document.
|
|
80
|
+
|
|
81
|
+
## Step 3: Note-health findings (reported, not fixed)
|
|
82
|
+
|
|
83
|
+
From the same inventory, no additional vault reads (the broken-link history check below reads the
|
|
84
|
+
vault's local git log, not more notes). Each of these three has no safe mechanical repair —
|
|
85
|
+
fixing any of them means guessing at intent — so they become findings for the Step 6 proposal
|
|
86
|
+
instead of a silent edit:
|
|
87
|
+
|
|
88
|
+
- **Broken links** — `unresolvedLinks` non-empty. Before writing the finding, check the vault's own
|
|
89
|
+
local git history to say *why* it's broken instead of leaving that to guesswork — the vault is
|
|
90
|
+
usually a git repo (`db-sync` auto-commits every agent-driven edit into it, opt-in per vault, same
|
|
91
|
+
precondition as that hook). Resolve the vault's filesystem path the same way `synapse.conf`
|
|
92
|
+
already does (`OBSIDIAN_VAULT_DIR`), skip this sub-step entirely if `{vault}/.git` doesn't exist,
|
|
93
|
+
and never let a missing/unreachable git repo block the rest of the finding — worst case it's
|
|
94
|
+
reported with no history context, same as today.
|
|
95
|
+
- `git -C {vault} log --all --diff-filter=A --name-only --pretty=format: -- "**/{target}.md"` — a
|
|
96
|
+
hit means a note by that exact title was created at some point (even if later renamed or
|
|
97
|
+
deleted): report it as *"used to be a note — find what it's called now, or was deleted"*.
|
|
98
|
+
- No hit → `git -C {vault} log --all -p -S"[[{target}]]" -- .` and look at the earliest match's
|
|
99
|
+
diff: if the line first appears inside a `new file mode` addition (the note was created with the
|
|
100
|
+
link already broken), report it as *"born broken — never pointed at a real note; probably meant
|
|
101
|
+
as plain text or an external reference, not a vault link"* rather than implying anything was
|
|
102
|
+
lost.
|
|
103
|
+
- Neither check resolves anything more specific → report the target plainly, same as before.
|
|
104
|
+
- **Orphaned notes** — `links` empty *and* `backlinks` empty.
|
|
105
|
+
- **Duplicate/near-duplicate titles** — group notes by title normalized (lowercased, trimmed,
|
|
106
|
+
internal whitespace collapsed); any group with 2+ members is a finding. This is a mechanical
|
|
107
|
+
string-normalization match, not fuzzy similarity — genuinely fuzzy "these might be the same
|
|
108
|
+
note" calls belong to the judgment layer in Step 5, not here.
|
|
109
|
+
|
|
110
|
+
## Step 4: Recategorization signal layer
|
|
111
|
+
|
|
112
|
+
Mechanical, from the same inventory, still no note-body reasoning:
|
|
113
|
+
|
|
114
|
+
- **Weak folder fit** — a note (not already an orphan from Step 3) that shares no tag with any
|
|
115
|
+
other note in its own folder, and where fewer than half of its combined `links`+`backlinks` point
|
|
116
|
+
to notes within that same folder.
|
|
117
|
+
- **Uncovered clusters** — a tag or keyword held by 3+ notes spanning 2+ different folders, where no
|
|
118
|
+
existing top-level folder name (from `Index.md`'s current list) already matches it.
|
|
119
|
+
- **Stale categories** — a scope-eligible top-level folder whose newest note's `stat.mtime` is more
|
|
120
|
+
than 90 days old (no new note gained in that window) is flagged as a merge-back candidate, the
|
|
121
|
+
same way a newly proposed split is flagged — one signal, checked at a different point in a
|
|
122
|
+
category's life.
|
|
123
|
+
|
|
124
|
+
## Step 5: Recategorization judgment layer
|
|
125
|
+
|
|
126
|
+
For each Step 4 candidate — a weak-fit note, a cluster, or a stale folder — read its full content
|
|
127
|
+
(and, for a cluster, every member's content) and decide with real judgment, not the mechanical
|
|
128
|
+
signal alone:
|
|
129
|
+
|
|
130
|
+
- Weak-fit note: does it genuinely belong in a different existing category, or was the signal a
|
|
131
|
+
false positive (a note that's fine where it is, just thin on tags/links)?
|
|
132
|
+
- Cluster: given what these notes actually say, is this a real emerging category, or just a
|
|
133
|
+
coincidental shared keyword?
|
|
134
|
+
- Stale folder: has it truly stopped being useful, or is low volume expected and fine (a narrow but
|
|
135
|
+
still-active category)?
|
|
136
|
+
|
|
137
|
+
Never moves a note or creates a folder here — every conclusion becomes one line in the Step 6
|
|
138
|
+
proposal, for the vault owner to act on.
|
|
139
|
+
|
|
140
|
+
## Step 6: Compose the proposal
|
|
141
|
+
|
|
142
|
+
One note, `inbox/Vault tidy — {YYYY-MM-DD}.md` (fetch machine local time, never infer it), written
|
|
143
|
+
via `vault_write` (creates `inbox/` automatically if it doesn't exist yet; if `inbox/` isn't already
|
|
144
|
+
in `Index.md`'s folder list, add it there in the same pass, matching the folder-layout rule every
|
|
145
|
+
other command that can create a top-level folder already follows). Two sections:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
## Recategorization
|
|
149
|
+
- {note or cluster}: {proposed move/new category/merge}, because {reasoning}
|
|
150
|
+
|
|
151
|
+
## Note health
|
|
152
|
+
- Broken link in {note}: → {target text that doesn't resolve} ({history classification, if the
|
|
153
|
+
vault's local git repo yielded one — "used to be a note", "born broken", or omitted entirely if
|
|
154
|
+
neither check resolved anything})
|
|
155
|
+
- Orphaned: {note}
|
|
156
|
+
- Possible duplicate: {note A} / {note B}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Omit either section if Steps 3–5 found nothing for it. If both are empty, don't write a proposal at
|
|
160
|
+
all — say so directly instead of creating an empty note.
|
|
161
|
+
|
|
162
|
+
This command never edits `Index.md` itself beyond the `inbox/` bootstrap case above — creating or
|
|
163
|
+
renaming a category is the vault owner's call, made by hand using
|
|
164
|
+
[[sb — Make vault folder taxonomy user-customizable via Index.md.template]]'s already-shipped
|
|
165
|
+
mechanism, not something this command does on its own.
|
|
166
|
+
|
|
167
|
+
## Step 7: Report
|
|
168
|
+
|
|
169
|
+
Print a short summary directly in the response, not left only in tool-call output:
|
|
170
|
+
|
|
171
|
+
- Count of frontmatter defaults silently filled, with filenames.
|
|
172
|
+
- Count of recategorization candidates and note-health findings written to the proposal, with a
|
|
173
|
+
link to `inbox/Vault tidy — {date}.md`.
|
|
174
|
+
- If nothing was found anywhere: "Vault's in good shape — nothing to fix or propose."
|
|
175
|
+
|
|
176
|
+
## Constraints
|
|
177
|
+
|
|
178
|
+
- Never touches — moves, edits, or reorganizes — anything inside `designs/`, `tasks/`, or
|
|
179
|
+
`synapse/`. `Index.md` and a vault-root `README.md` are also out of scope.
|
|
180
|
+
- Recategorization never auto-moves a note or auto-creates a folder — proposal only, to `inbox/`.
|
|
181
|
+
- Only the frontmatter-default class of note-health fix (Step 2) is applied directly and silently.
|
|
182
|
+
Broken links, orphaned notes, and duplicate titles are proposal findings, never auto-repaired.
|
|
183
|
+
- Invoked on demand only — no `SessionStart` wiring, no autonomous scheduling. Run it directly, or
|
|
184
|
+
under a `/loop` the user sets up themselves.
|
|
185
|
+
- No compiled code — every step above is a plain `mcp__obsidian__*` call, or (Step 3's broken-link
|
|
186
|
+
history check only) a plain `git log` via Bash against the vault's own local repo; this command
|
|
187
|
+
itself never calls into `ObsidianStore`.
|