@imunitic/synapse 0.4.2 → 0.4.4
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 +4 -4
- package/commands/synapse-design-note.md +22 -15
- package/commands/synapse-init.md +5 -5
- package/commands/synapse-note.md +1 -1
- package/commands/synapse-rebuild-diff.md +16 -16
- package/commands/synapse-rebuild-full.md +2 -2
- package/commands/synapse-task-note.md +6 -6
- package/commands/synapse-vault-tidy.md +10 -12
- package/harness/codex/skills/synapse-design-note/SKILL.md +27 -14
- package/harness/codex/skills/synapse-init/SKILL.md +3 -3
- package/harness/codex/skills/synapse-note/SKILL.md +3 -2
- package/harness/codex/skills/synapse-rebuild-diff/SKILL.md +15 -15
- package/harness/codex/skills/synapse-rebuild-full/SKILL.md +2 -2
- package/harness/codex/skills/synapse-task-note/SKILL.md +4 -4
- package/harness/codex/skills/synapse-vault-tidy/SKILL.md +10 -11
- package/harness/opencode/plugin/synapse.js +14 -1
- package/package.json +5 -5
- package/skills/synapse-node/SKILL.md +16 -16
- package/skills/synapse-node-authoring/SKILL.md +2 -2
- package/skills/synapse-node-format/SKILL.md +12 -14
- package/skills/synapse-orientation/SKILL.md +4 -4
- package/skills/synapse-query/SKILL.md +4 -4
- package/skills/synapse-task/SKILL.md +3 -3
- package/skills/synapse-vault/SKILL.md +23 -0
- package/synapse-claude.md +20 -28
- package/synapse-ignore-files.conf.template +6 -2
- package/synapse-module-boilerplate.conf.template +6 -3
- package/synapse-projects.conf.template +5 -1
- package/synapse-prompt-stopwords.conf.template +6 -2
- package/synapse.conf.template +6 -9
|
@@ -27,7 +27,7 @@ reinventing a parallel view — this skill has no separate list/continue mode of
|
|
|
27
27
|
|
|
28
28
|
## Prerequisites
|
|
29
29
|
|
|
30
|
-
- Requires a matching
|
|
30
|
+
- Requires a matching design note (`designs/`) with `Status: Ready`.
|
|
31
31
|
- No matching note → "No Ready design note found for '{topic}'. Start or continue that design note
|
|
32
32
|
first." Never generate a checklist from scratch.
|
|
33
33
|
- Matching note but `Status: Discussing` → "Design note for '{topic}' is still in Discussing. Finish
|
|
@@ -123,7 +123,7 @@ skill doesn't set or watch that field itself.
|
|
|
123
123
|
```
|
|
124
124
|
User: let's compile a task note for "rollup direct storage"
|
|
125
125
|
|
|
126
|
-
Codex: [reads designs/{
|
|
126
|
+
Codex: [reads designs/{project}/Rollup direct storage.md, Status: Ready]
|
|
127
127
|
[no existing linked task note]
|
|
128
128
|
|
|
129
129
|
Compiled checklist:
|
|
@@ -134,8 +134,8 @@ Codex: [reads designs/{PROJECT} — Rollup direct storage.md, Status: Ready]
|
|
|
134
134
|
|
|
135
135
|
Project already known: {PROJECT} → {prefix}-005.
|
|
136
136
|
|
|
137
|
-
Created: tasks/{
|
|
138
|
-
Linked back from designs/{
|
|
137
|
+
Created: tasks/{project}/Rollup direct storage implementation.md (task_id: {prefix}-005)
|
|
138
|
+
Linked back from designs/{project}/Rollup direct storage.md.
|
|
139
139
|
|
|
140
140
|
Status transitions happen automatically via the synapse-task skill once you start work.
|
|
141
141
|
```
|
|
@@ -30,14 +30,14 @@ there is no argument to parse, every run produces the same one-pass sweep.
|
|
|
30
30
|
## Prerequisites
|
|
31
31
|
|
|
32
32
|
Requires the `synapse` CLI on `PATH`, resolving a vault with a working link graph
|
|
33
|
-
(`synapse vault-backlinks`/`vault-links`/`vault-unresolved`/`vault-orphans`/`vault-deadends`).
|
|
34
|
-
real
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
(`synapse vault-backlinks`/`vault-links`/`vault-unresolved`/`vault-orphans`/`vault-deadends`).
|
|
34
|
+
`disk`, the one real backend, always has one -- case-insensitive wikilink resolution over the vault
|
|
35
|
+
folder directly, no external dependency. If `SYNAPSE_VAULT_INTEGRATIONS` ever resolves to a backend
|
|
36
|
+
with no link graph at all, these commands exit 1 saying so; stop and report that rather than
|
|
37
|
+
falling back to anything else.
|
|
38
38
|
|
|
39
39
|
This skill reaches the vault store only through the `synapse` CLI's `vault-*` subcommands, the same
|
|
40
|
-
door every other skill uses — no MCP tool, no direct
|
|
40
|
+
door every other skill uses — no MCP tool, no direct store call. `vault-links`/
|
|
41
41
|
`vault-backlinks` each answer for one note at a time, so Step 1's inventory sweep runs one pair per
|
|
42
42
|
note in scope: `2N` process spawns for an `N`-note vault. Acceptable for an on-demand, rare command.
|
|
43
43
|
Step 3's broken-link history check is a plain `git log` call (via the shell, not a compiled tool)
|
|
@@ -87,10 +87,9 @@ derivable without guessing:
|
|
|
87
87
|
- Missing `created` → `stat.ctime`, formatted `YYYY-MM-DD HH:MM` to match every other note's
|
|
88
88
|
convention.
|
|
89
89
|
|
|
90
|
-
Apply
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
the same hazard the vault and task-status skills already document.
|
|
90
|
+
Apply with `synapse frontmatter set <path> <key> <value>` (or `vault-patch --frontmatter`, which
|
|
91
|
+
delegates to the same byte-preserving mechanism) — either writes exactly this one field and leaves
|
|
92
|
+
every other line untouched, the way the vault and task-status skills already document.
|
|
94
93
|
|
|
95
94
|
## Step 3: Note-health findings (reported, not fixed)
|
|
96
95
|
|
|
@@ -199,4 +198,4 @@ Print a short summary directly in the response, not left only in tool-call outpu
|
|
|
199
198
|
asked, or on a recurring cadence the user sets up themselves.
|
|
200
199
|
- Every step above goes through the `synapse` CLI's `vault-*` subcommands, or (Step 3's broken-link
|
|
201
200
|
history check only) a plain `git log` via the shell against the vault's own local repo; this skill
|
|
202
|
-
never calls
|
|
201
|
+
never calls a store implementation directly.
|
|
@@ -105,6 +105,12 @@ const EDIT_TOOLS = new Set(["write", "edit"])
|
|
|
105
105
|
// yet -- delivered on that session's next `chat.message`.
|
|
106
106
|
const pendingNudge = new Map()
|
|
107
107
|
|
|
108
|
+
// sessionID -> queued `staleness` output, same reason and same delivery as
|
|
109
|
+
// `pendingNudge` above: `tool.execute.after` has no message `output` to push
|
|
110
|
+
// a part onto, so a drift/grounding warning from it would otherwise be
|
|
111
|
+
// silently discarded instead of just reaching the next turn late.
|
|
112
|
+
const pendingStaleness = new Map()
|
|
113
|
+
|
|
108
114
|
export const Synapse = async ({ directory, client }) => {
|
|
109
115
|
return {
|
|
110
116
|
"chat.message": async (input, output) => {
|
|
@@ -123,6 +129,12 @@ export const Synapse = async ({ directory, client }) => {
|
|
|
123
129
|
newParts.push(textPart(sessionID, output, `[SYNAPSE-STOP-NUDGE]\n${queuedNudge}`))
|
|
124
130
|
}
|
|
125
131
|
|
|
132
|
+
const queuedStaleness = pendingStaleness.get(sessionID)
|
|
133
|
+
if (queuedStaleness) {
|
|
134
|
+
pendingStaleness.delete(sessionID)
|
|
135
|
+
newParts.push(textPart(sessionID, output, `[SYNAPSE-STALENESS]\n${queuedStaleness}`))
|
|
136
|
+
}
|
|
137
|
+
|
|
126
138
|
const promptText = (output.parts || [])
|
|
127
139
|
.filter((p) => p.type === "text")
|
|
128
140
|
.map((p) => p.text)
|
|
@@ -136,10 +148,11 @@ export const Synapse = async ({ directory, client }) => {
|
|
|
136
148
|
"tool.execute.after": async (input) => {
|
|
137
149
|
const filePath = input.args?.filePath
|
|
138
150
|
if (EDIT_TOOLS.has(input.tool) && filePath) {
|
|
139
|
-
runHook("staleness", {
|
|
151
|
+
const text = runHook("staleness", {
|
|
140
152
|
session_id: input.sessionID,
|
|
141
153
|
tool_input: { file_path: filePath },
|
|
142
154
|
})
|
|
155
|
+
if (text) pendingStaleness.set(input.sessionID, text)
|
|
143
156
|
}
|
|
144
157
|
},
|
|
145
158
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imunitic/synapse",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "Memory for Claude Code, Codex CLI, and OpenCode: a durable
|
|
3
|
+
"version": "0.4.4",
|
|
4
|
+
"description": "Memory for Claude Code, Codex CLI, and OpenCode: a durable markdown vault plus a per-repo code graph.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/imunitic/synapse"
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"*.conf.template"
|
|
24
24
|
],
|
|
25
25
|
"optionalDependencies": {
|
|
26
|
-
"@imunitic/synapse-darwin-arm64": "0.4.
|
|
27
|
-
"@imunitic/synapse-linux-x64": "0.4.
|
|
28
|
-
"@imunitic/synapse-linux-arm64": "0.4.
|
|
26
|
+
"@imunitic/synapse-darwin-arm64": "0.4.4",
|
|
27
|
+
"@imunitic/synapse-linux-x64": "0.4.4",
|
|
28
|
+
"@imunitic/synapse-linux-arm64": "0.4.4"
|
|
29
29
|
},
|
|
30
30
|
"license": "SEE LICENSE IN LICENSE"
|
|
31
31
|
}
|
|
@@ -21,7 +21,7 @@ needs one.
|
|
|
21
21
|
|
|
22
22
|
## Procedure
|
|
23
23
|
|
|
24
|
-
1. **Verify the whole project once, with the script.** Run
|
|
24
|
+
1. **Verify the whole project once, with the script.** Run `synapse query stale` from
|
|
25
25
|
inside the repo. It prints one `{node title}\t{reason}` line per stale node and nothing at all
|
|
26
26
|
when everything is current, so its output is the complete stale set for the project.
|
|
27
27
|
|
|
@@ -50,7 +50,7 @@ needs one.
|
|
|
50
50
|
So skip the frontmatter entirely:
|
|
51
51
|
|
|
52
52
|
```sh
|
|
53
|
-
|
|
53
|
+
synapse query body "{Node title}"
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
That prints only what is between the generated fences — so it excludes `## Notes` as well as the
|
|
@@ -71,7 +71,7 @@ needs one.
|
|
|
71
71
|
4. **Regeneration (only if step 1 or 2 found the node stale).** You re-author the prose; a script
|
|
72
72
|
writes the file. **The node contract itself — frontmatter fields, the crux pointer, `## Links`,
|
|
73
73
|
`grounded_in` — is the `synapse-node-format` skill**, shared with `/synapse-init` and
|
|
74
|
-
`/synapse-rebuild`. What follows here is only what differs when *re*-authoring an existing
|
|
74
|
+
`/synapse-rebuild-diff`. What follows here is only what differs when *re*-authoring an existing
|
|
75
75
|
node rather than writing a new one. Everything mechanical — hashes, `sources_digest`, the `## Sources` mirror,
|
|
76
76
|
`built_at`, `commit`, `stale: false`, and preserving `## Notes` — belongs to
|
|
77
77
|
`synapse write-node`, because a hub node's `sources` can no more be *emitted* into a tool call
|
|
@@ -81,11 +81,11 @@ needs one.
|
|
|
81
81
|
- **Get the node's path list into a file, never into context:**
|
|
82
82
|
|
|
83
83
|
```sh
|
|
84
|
-
|
|
84
|
+
synapse query sources "{Node title}" > "$W/paths.txt"
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
If `$W/manifest.tsv` exists (or the namespace has `_manifest.tsv`), prefer re-running
|
|
88
|
-
|
|
88
|
+
`synapse build-lists` instead and use the regenerated `lists/NN.txt`: it
|
|
89
89
|
re-derives every list from the clustering patterns, so files *added* since the last build are
|
|
90
90
|
picked up automatically rather than sitting in `_unassigned`. `synapse query sources` can only
|
|
91
91
|
return what the node already claims.
|
|
@@ -101,11 +101,11 @@ needs one.
|
|
|
101
101
|
and discards findings the diff has nothing to say about. Project the diff as carefully as
|
|
102
102
|
`sources`: names first, `--stat` to size it, hunks only for the selection.
|
|
103
103
|
- Fall back to reading the files when patching cannot be justified — a large fraction changed, the
|
|
104
|
-
`crux` file is gone, or the baseline is unusable. Then try
|
|
104
|
+
`crux` file is gone, or the baseline is unusable. Then try `synapse tags {path}`
|
|
105
105
|
first (exit 0 use the tags, exit 1 fall back to reading the file, exit 2 run the discovery
|
|
106
106
|
procedure `/synapse-init` documents, then retry), and read the load-bearing files in full — the
|
|
107
107
|
tags signal informs regrouping, it never substitutes for reading a file before rewriting its prose.
|
|
108
|
-
If this is happening across many nodes at once, stop and run `/synapse-rebuild` instead: that is
|
|
108
|
+
If this is happening across many nodes at once, stop and run `/synapse-rebuild-diff` instead: that is
|
|
109
109
|
the instrument for major drift, and it triages node by node rather than paying full cost for each.
|
|
110
110
|
- Re-author `## Summary`, `## Crux` and `## Links` to match what the files contain now, into
|
|
111
111
|
`$W/body.md`. Re-check the node's one-line `summary` as well; keep the existing one with
|
|
@@ -127,22 +127,22 @@ needs one.
|
|
|
127
127
|
- **Write it back with the script:**
|
|
128
128
|
|
|
129
129
|
```sh
|
|
130
|
-
|
|
130
|
+
synapse write-node --title "{Node title}" --summary "{one line}" \
|
|
131
131
|
--paths "$W/paths.txt" --body "$W/body.md"
|
|
132
132
|
```
|
|
133
133
|
|
|
134
134
|
It replaces only the generated region and re-emits everything after the closing fence verbatim,
|
|
135
135
|
which is what makes the `## Notes` guarantee enforceable rather than a promise.
|
|
136
136
|
- **Never hand-write the frontmatter**, with `vault_patch` at `targetType: frontmatter` or
|
|
137
|
-
otherwise.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
otherwise. `--frontmatter` only ever writes a single scalar value, so it can't represent
|
|
138
|
+
`sources:`/`grounded_in:` at all, and enumerating the rest of a node's fields by hand one call
|
|
139
|
+
at a time is how `summary` and `commit` get silently dropped — which then breaks the next
|
|
140
|
+
`synapse build-project-index` run, far from the cause.
|
|
141
141
|
- **`## Notes` is human-authored only.** Never write into it — not at regeneration, not to record
|
|
142
142
|
what you just did. (Task notes in `tasks/` are a different artifact: the `synapse-task` skill *does*
|
|
143
143
|
append there. Do not carry that habit into a Synapse node.)
|
|
144
144
|
- If the node's `summary` or title changed, rebuild the index so the map matches:
|
|
145
|
-
|
|
145
|
+
`synapse build-project-index`.
|
|
146
146
|
- **Say out loud that a regeneration happened** — e.g. "Node '{title}' was stale, regenerated
|
|
147
147
|
before use." This has real latency and token cost, unlike Tier 1/2's detection; it must never
|
|
148
148
|
be absorbed silently into the read.
|
|
@@ -150,13 +150,13 @@ needs one.
|
|
|
150
150
|
- Read the bucket with a shell command, not into context — the index runs to tens of megabytes:
|
|
151
151
|
|
|
152
152
|
```sh
|
|
153
|
-
|
|
153
|
+
synapse index unassigned
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
Empty → nothing to do, skip silently (an empty sweep isn't worth announcing).
|
|
157
157
|
- Otherwise read `synapse/{project}/Index.md` for the current node list (titles + summaries).
|
|
158
158
|
- Tag the whole bucket in **one** call — write the paths to a list and run
|
|
159
|
-
|
|
159
|
+
`synapse tags --paths {list}`, whose output is attributable (an unindented
|
|
160
160
|
line is a path, the tab-indented lines under it are its tags). A per-file loop costs ~33× more
|
|
161
161
|
for the same answer. Fall back to a full read for ambiguous cases, then classify against that
|
|
162
162
|
node list. **The judgment is which cluster a path belongs to; the bookkeeping is not yours to
|
|
@@ -167,7 +167,7 @@ needs one.
|
|
|
167
167
|
immediately. If there is no manifest, add the path to that node's list file instead.
|
|
168
168
|
- **Fits nothing** → leave it unassigned. A genuinely new subsystem wants its own manifest line
|
|
169
169
|
and its own node, which is `/synapse-init` work, not a sweep.
|
|
170
|
-
- Then rebuild the projection with
|
|
170
|
+
- Then rebuild the projection with `synapse build-index`. **Never hand-edit
|
|
171
171
|
`_index.bin`** — it is derived, binary, and tens of megabytes; there is nothing to
|
|
172
172
|
hand-edit.
|
|
173
173
|
- **Announce every outcome**, same transparency rule as regeneration: which file, and which
|
|
@@ -126,8 +126,8 @@ candidate list, not something to copy verbatim.
|
|
|
126
126
|
Never hard-wrap. Write each paragraph as one single unbroken line and let the editor soft-wrap
|
|
127
127
|
it — a newline exists only where a real break is intended (between paragraphs, list items,
|
|
128
128
|
headings). This is a vault-wide rule, not specific to this node, and it is not optional: a
|
|
129
|
-
hard-wrapped paragraph renders as a ragged stack of short lines in
|
|
130
|
-
text.
|
|
129
|
+
hard-wrapped paragraph renders as a ragged stack of short lines in a soft-wrapping viewer instead of
|
|
130
|
+
flowing text.
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
Same model as the orchestrating session, no override — matches the constraint that "a
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: synapse-node-format
|
|
3
|
-
description: The contract for a Synapse code-graph node — frontmatter fields, the crux pointer, `## Links`, `grounded_in`, `## Sources`, and what `synapse write-node` adds or refuses. Load before authoring or regenerating any node, whether from /synapse-init's first build, /synapse-rebuild's triage, or the synapse-node skill's lazy regeneration. Not for reading the graph (that is synapse-query) or for task notes in the vault (that is synapse-task).
|
|
3
|
+
description: The contract for a Synapse code-graph node — frontmatter fields, the crux pointer, `## Links`, `grounded_in`, `## Sources`, and what `synapse write-node` adds or refuses. Load before authoring or regenerating any node, whether from /synapse-init's first build, /synapse-rebuild-diff's triage, or the synapse-node skill's lazy regeneration. Not for reading the graph (that is synapse-query) or for task notes in the vault (that is synapse-task).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# What a node is, and how to author one
|
|
7
7
|
|
|
8
8
|
Every component that writes a node loads this: `/synapse-init` (first build), the `synapse-node`
|
|
9
|
-
skill (Tier 2 lazy regeneration), and `/synapse-rebuild` (reseat, patch, re-orient).
|
|
9
|
+
skill (Tier 2 lazy regeneration), and `/synapse-rebuild-diff` (reseat, patch, re-orient).
|
|
10
10
|
|
|
11
11
|
All three write the same artifact, so the format belongs in one place rather than being restated
|
|
12
12
|
wherever it is used. What stays with each caller is what is genuinely specific to it — the skill's
|
|
@@ -42,15 +42,15 @@ to orient someone already inside. A node without one is an error, not a default.
|
|
|
42
42
|
|
|
43
43
|
- **Filename/title:** short, senior-engineer-style description of the concept (e.g. "World —
|
|
44
44
|
entity/component/resource core"). Filesystem-illegal characters (`/ : * ? " < > |`) are
|
|
45
|
-
sanitized — but **reword the title instead of relying on that**, because
|
|
46
|
-
|
|
45
|
+
sanitized — but **reword the title instead of relying on that**, because a wikilink resolves
|
|
46
|
+
by *filename*, so `[[World — entity/component/resource core]]` silently resolves to
|
|
47
47
|
nothing once the file becomes `...entity_component_resource core.md`. A broken wikilink is a
|
|
48
48
|
valid link to a not-yet-existing note, so it fails quietly. The writer warns when a title needs
|
|
49
49
|
sanitizing; treat that warning as "rename this node". Same trap when you *retitle* a node
|
|
50
50
|
mid-build: inbound links already written keep pointing at the old name.
|
|
51
51
|
- **`sources`:** **every** file the node covers — repo-relative path plus that file's
|
|
52
52
|
`git hash-object <path>` output, run from the repo root at the moment of writing. Exhaustive,
|
|
53
|
-
not a sample: this is a **machine** field, and it is what makes
|
|
53
|
+
not a sample: this is a **machine** field, and it is what makes a path-based lookup able to reach
|
|
54
54
|
a node from any file it covers (searching a class name that appears in no node's prose still
|
|
55
55
|
finds its node via this list). Do **not** trim it to a handful of "representative" files —
|
|
56
56
|
doing so silently destroys that lookup, leaves the node unable to answer "which files am I
|
|
@@ -65,17 +65,15 @@ to orient someone already inside. A node without one is an error, not a default.
|
|
|
65
65
|
someone through this subsystem), `crux` (the few lines that carry the actual logic — **authored
|
|
66
66
|
as line numbers, stored as text**: you point, the writer slices, so composing is impossible at
|
|
67
67
|
authoring time and nothing decays afterwards the way a stored line number would), `links` (typed
|
|
68
|
-
|
|
68
|
+
wikilinks to other nodes in this same namespace: `depends_on`, `part_of`, `uses`, or
|
|
69
69
|
another type that fits better if one doesn't — for `depends_on`/`uses` specifically, `/synapse-init`
|
|
70
70
|
computes candidates before any node exists via `synapse link-graph`; read that node's rows from
|
|
71
71
|
`links.tsv` rather than guessing which siblings it relates to, `part_of` stays a judgement call
|
|
72
72
|
with nothing mechanical behind it), a `## Sources` section, and an empty `## Notes` section.
|
|
73
73
|
- **Break a "does N things" enumeration into real bullets, not inline `(1)/(2)/(3)`.** A sentence
|
|
74
74
|
enumerating three or more parallel sub-points reads as a wall of text once each item carries its
|
|
75
|
-
own clause or parenthetical -- the node is read by a human skimming it
|
|
76
|
-
|
|
77
|
-
was chosen specifically so it stays "just as readable by a human directly in Obsidian as it is by
|
|
78
|
-
Claude"), and a dense inline run-on defeats that. Use a markdown bullet list under the sentence
|
|
75
|
+
own clause or parenthetical -- the node is read by a human skimming it as much as by an agent,
|
|
76
|
+
and a dense inline run-on defeats that. Use a markdown bullet list under the sentence
|
|
79
77
|
introducing them instead. This is narrow, not a general "prefer bullets" rule: an aside of one or
|
|
80
78
|
two items, or connected causal narrative ("X, because Y, which is why Z"), stays flowing prose --
|
|
81
79
|
over-bulleting ordinary narrative just trades one readability problem for another.
|
|
@@ -138,10 +136,10 @@ to orient someone already inside. A node without one is an error, not a default.
|
|
|
138
136
|
per owning directory or module with a file count, `LC_ALL=C` sorted. A node covering 941 files
|
|
139
137
|
would otherwise put 75 KB of paths in front of a reader who wants to know which modules are
|
|
140
138
|
involved — and the frontmatter already carries every path for search, so the mirror doesn't
|
|
141
|
-
need to repeat them. Rewritten from `sources` on every write, never hand-edited. (
|
|
142
|
-
|
|
143
|
-
why a mirror exists at all — but that is an argument for aggregating *the mirror*,
|
|
144
|
-
trimming the field.)
|
|
139
|
+
need to repeat them. Rewritten from `sources` on every write, never hand-edited. (A raw YAML
|
|
140
|
+
list in frontmatter renders as a flattened, truncated one-line string in typical note-viewer
|
|
141
|
+
UI, which is why a mirror exists at all — but that is an argument for aggregating *the mirror*,
|
|
142
|
+
not for trimming the field.)
|
|
145
143
|
- **`## Notes` is human-authored only.** Claude never writes into it — not at build time, not at
|
|
146
144
|
regeneration. It is created empty and preserved verbatim forever after.
|
|
147
145
|
- **Fence the generated region.** Everything the generator owns sits between
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: synapse-orientation
|
|
3
|
-
description: How to work out where meaning lives in a codebase you have not seen before — first mechanically, from the repo's own symbol vocabulary, and where no grammar exists, by four questions in order with the cheap commands that answer each. Includes tree-sitter grammar discovery. Use when clustering a repo into Synapse graph nodes for the first time, when re-deriving a node's premises in /synapse-rebuild's re-orient class, or any time you need to orient in an unfamiliar tree before making claims about it.
|
|
3
|
+
description: How to work out where meaning lives in a codebase you have not seen before — first mechanically, from the repo's own symbol vocabulary, and where no grammar exists, by four questions in order with the cheap commands that answer each. Includes tree-sitter grammar discovery. Use when clustering a repo into Synapse graph nodes for the first time, when re-deriving a node's premises in /synapse-rebuild-diff's re-orient class, or any time you need to orient in an unfamiliar tree before making claims about it.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Orienting in an unfamiliar repo
|
|
7
7
|
|
|
8
|
-
Loaded by `/synapse-init` at its orientation step, and by `/synapse-rebuild` when a node lands in
|
|
8
|
+
Loaded by `/synapse-init` at its orientation step, and by `/synapse-rebuild-diff` when a node lands in
|
|
9
9
|
the *re-orient* class and its premises have to be re-derived rather than patched. Useful on its own
|
|
10
10
|
terms too: nothing here is Synapse-specific except what you do with the answers.
|
|
11
11
|
|
|
@@ -116,8 +116,8 @@ script fetched from a notes vault. Begin any re-run by reading it, and improve i
|
|
|
116
116
|
re-deriving from scratch. Nothing like it ships, because which aggregations carry signal depends
|
|
117
117
|
on the codebase — a distributed one would encode the wrong ecosystem's conventions.
|
|
118
118
|
|
|
119
|
-
`.txt`, with markdown formatting inside, for a measured reason:
|
|
120
|
-
notes, so a `_profile.md`
|
|
119
|
+
`.txt`, with markdown formatting inside, for a measured reason: a vault viewer indexes `.md` files
|
|
120
|
+
as notes, so a `_profile.md` would turn up in search and note-browsing UI, where it is pure noise
|
|
121
121
|
to a human reading notes. A non-`.md` extension is invisible to all of those and still perfectly
|
|
122
122
|
readable. Note the `_` prefix does *nothing* mechanically — it is only a hint to a human who sees
|
|
123
123
|
the file, matching `_manifest.tsv`. Record **negative results** here too ("this abbreviation has no
|
|
@@ -32,10 +32,10 @@ already named, to locate a specific detail inside it — never as an unscoped re
|
|
|
32
32
|
instead of asking Synapse first. If you catch yourself about to grep the whole repo before checking
|
|
33
33
|
`synapse/{project}/Index.md`, stop and check the index instead.
|
|
34
34
|
|
|
35
|
-
**Why the cost difference is real, not just tidiness.** `synapse query body <node>`
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
**Why the cost difference is real, not just tidiness.** `synapse query body <node>` is a direct disk
|
|
36
|
+
read that extracts only the prose between the generated fences, skipping the node's `sources` list
|
|
37
|
+
entirely. Reading the whole note instead would move that node's entire frontmatter — megabytes — to
|
|
38
|
+
print a few hundred words. On a large
|
|
39
39
|
repo (dozens to hundreds of thousands of tracked files), that difference is the entire reason a
|
|
40
40
|
query stays cheap instead of dominating the turn.
|
|
41
41
|
|
|
@@ -73,9 +73,9 @@ into `status:` either — always go through this skill, which caps at
|
|
|
73
73
|
back what you read.
|
|
74
74
|
|
|
75
75
|
`synapse vault-patch <path> --frontmatter <key> --replace` is also byte-preserving now (it
|
|
76
|
-
delegates to the same field-local mechanism `frontmatter set` uses internally)
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
delegates to the same field-local mechanism `frontmatter set` uses internally) — but it only ever
|
|
77
|
+
writes a plain scalar, and it's a full read-apply-write round trip through the patch layer for
|
|
78
|
+
one field. `frontmatter set` stays the
|
|
79
79
|
right tool for this step: narrower, and the one call that exists specifically for it.
|
|
80
80
|
6. **For completion only:** append implementation bullets to the existing
|
|
81
81
|
`## Notes` section with `synapse vault-patch`:
|
|
@@ -95,6 +95,29 @@ On a `SYNAPSE_VAULT_INTEGRATIONS=git` vault, every edit commits automatically, s
|
|
|
95
95
|
calls, not merely a record of intentional edits. The default `disk` backend keeps no history at all,
|
|
96
96
|
so this safety net only exists once that backend is chosen.
|
|
97
97
|
|
|
98
|
+
## Creating notes: authoring commands, not freeform writes
|
|
99
|
+
|
|
100
|
+
Every new note is created through an authoring command, never by freeforming a file at
|
|
101
|
+
`vault-write`:
|
|
102
|
+
|
|
103
|
+
- `/synapse-note` (bare mode) creates plain notes in any category -- `research/`,
|
|
104
|
+
`scratchpad/`, `inbox/`, anything that is not a tracked task or a design note. It mints the
|
|
105
|
+
frontmatter (`schema`, `title`, `note_id`, `created`, `updated`, `tags`) to the note schema's
|
|
106
|
+
contract, including full-timestamp format.
|
|
107
|
+
- `/synapse-note --task` creates tracked task notes under `tasks/{project}/`.
|
|
108
|
+
- `/synapse-design-note` and `/synapse-task-note` handle design discussions and compiled tasks --
|
|
109
|
+
both delegate the file-minting mechanics to `/synapse-note` internally.
|
|
110
|
+
|
|
111
|
+
`vault-write` is for *editing existing notes*, or the rare read-modify-write round trip. If a
|
|
112
|
+
direct creation is ever genuinely unavoidable, follow bare mode's frontmatter contract exactly
|
|
113
|
+
(`schema`/`title`/`note_id`/`created`/`updated`/`tags`, full `%Y-%m-%d %H:%M:%S %Z` timestamps)
|
|
114
|
+
rather than improvising a shape.
|
|
115
|
+
|
|
116
|
+
Schemas are opt-in by declaration: a note whose frontmatter declares `schema:` is held to that
|
|
117
|
+
schema's contract on every `vault-write`/`vault-patch`; a note without the field is never
|
|
118
|
+
validated or failed. Legacy notes predate the feature and stay valid as they are -- no backfill
|
|
119
|
+
is wanted, and "helpfully" adding `schema:` to old notes is not a service to anyone.
|
|
120
|
+
|
|
98
121
|
## Tagging is part of writing a note, not a separate pass
|
|
99
122
|
|
|
100
123
|
Every note-authoring command (`synapse-note`, `synapse-design-note`, `synapse-task-note`) applies tags as one of the steps in creating or substantially updating a note. There is no separate tagging pass or command — `/synapse-vault-tidy`'s recategorization signal reads tag data, it never writes it.
|
package/synapse-claude.md
CHANGED
|
@@ -100,27 +100,20 @@ a real yes/no answer, not a formality to wave past.
|
|
|
100
100
|
The vault is reached through the `synapse` CLI — `synapse vault-read`/`vault-write`/`vault-list`/
|
|
101
101
|
`vault-search`/`vault-search-text`/`vault-doc-map`/`vault-patch`/`vault-backlinks`/`vault-links`/
|
|
102
102
|
`vault-unresolved`/`vault-orphans`/`vault-deadends`/`vault-ambiguous`/`vault-rename` — for reads
|
|
103
|
-
*and* for writes, never by resolving a vault path or
|
|
104
|
-
Which concrete store the CLI talks to (`SYNAPSE_VAULT_INTEGRATIONS unset`, the default; `
|
|
105
|
-
into for
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
whatsoever: `read`/`write`/`list`/`search`/the link graph/rename are all plain disk I/O and direct
|
|
111
|
-
computation against the vault folder. Under the opted-in `obsidian` backend, `search`/the link
|
|
112
|
-
graph/rename go through Obsidian's own CLI over its local socket instead when Obsidian is running,
|
|
113
|
-
falling back to the same disk-backed behavior automatically and silently (a one-line stderr note,
|
|
114
|
-
nothing an agent turn needs to react to) whenever it isn't — no precondition to check or fail on
|
|
115
|
-
either way.
|
|
103
|
+
*and* for writes, never by resolving a vault path and reading or writing the note file directly.
|
|
104
|
+
Which concrete store the CLI talks to (`SYNAPSE_VAULT_INTEGRATIONS unset`, the default; or `git`,
|
|
105
|
+
opted into for the vault to own its own version control -- commit on every write, push/pull in the
|
|
106
|
+
background) is resolved once, inside the compiled binary, from `SYNAPSE_VAULT_INTEGRATIONS`/
|
|
107
|
+
`SYNAPSE_VAULT_DIR` — never something a skill or an agent turn needs to know or branch on. `read`/
|
|
108
|
+
`write`/`list`/`search`/the link graph/rename are all plain disk I/O and direct computation against
|
|
109
|
+
the vault folder, in every configuration.
|
|
116
110
|
|
|
117
111
|
**Every write to a note goes through `synapse vault-write` or `vault-patch`. Never the `Write`/`Edit`
|
|
118
|
-
tools on the on-disk path
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
which is why the habit never self-corrects on its own. The reason is not a
|
|
112
|
+
tools on the on-disk path** — not for a one-line change, and least of all when `Write`/`Edit` are
|
|
113
|
+
already in hand from editing code earlier in the same turn, because that proximity is precisely
|
|
114
|
+
what causes this to be violated. The vault being an ordinary directory means the wrong path
|
|
115
|
+
*works*: the auto-commit hook matches `Write|Edit`/`Bash` running `vault-write`/`vault-patch`, and
|
|
116
|
+
nothing visibly breaks — which is why the habit never self-corrects on its own. The reason is not a
|
|
124
117
|
failure mode to dodge; it is that an invariant upheld only when convenient is worth nothing. Nothing
|
|
125
118
|
else in the system can rely on it, and every note then has to be re-checked by hand instead of
|
|
126
119
|
trusted. Synapse's own tooling holds this line — `synapse write-node` goes through the same `Store`
|
|
@@ -129,19 +122,18 @@ differ. If the CLI itself ever fails (not installed, no vault configured), that'
|
|
|
129
122
|
failure to report and stop on, never a reason to fall back to a raw file edit.
|
|
130
123
|
|
|
131
124
|
Every shipped command and skill, `/synapse-vault-tidy` included, reaches the vault only through
|
|
132
|
-
the `synapse` CLI's `vault-*` subcommands — none of them
|
|
125
|
+
the `synapse` CLI's `vault-*` subcommands — none of them writes to the vault folder directly.
|
|
133
126
|
|
|
134
127
|
- You may create and edit notes in this vault **without asking for
|
|
135
128
|
permission first**, as long as each note is placed in the folder
|
|
136
129
|
matching its category per `Index.md`.
|
|
137
|
-
- Filenames are human-readable titles (not timestamp-prefixed —
|
|
138
|
-
|
|
139
|
-
poorly there). Sanitize filesystem-illegal characters (`/ : * ? " < > |`)
|
|
130
|
+
- Filenames are human-readable titles (not timestamp-prefixed — the filename is the display title,
|
|
131
|
+
so a timestamp prefix reads poorly). Sanitize filesystem-illegal characters (`/ : * ? " < > |`)
|
|
140
132
|
but otherwise keep the title as-is.
|
|
141
133
|
- Frontmatter carries what the filename no longer does: `title`, `created`
|
|
142
134
|
(real timestamp at creation time), and for task notes `task_id` /
|
|
143
135
|
`status` (`TODO`/`IN-PROGRESS`/`REVIEW`/`DONE`/`CANCELED`).
|
|
144
|
-
- Link with
|
|
136
|
+
- Link with wikilinks: `[[filename]]` or `[[filename|display
|
|
145
137
|
text]]` (no extension, exact filename minus `.md`).
|
|
146
138
|
- **Never hard-wrap note bodies. A newline exists if and only if a break is
|
|
147
139
|
intended in the output** — source line structure mirrors the output's block
|
|
@@ -156,16 +148,16 @@ the `synapse` CLI's `vault-*` subcommands — none of them calls an `mcp__obsidi
|
|
|
156
148
|
makes the text **renderer-independent**: if a newline never appears where
|
|
157
149
|
no break is wanted, then "does a single newline render as `<br>` or as a
|
|
158
150
|
space?" never arises, and strict CommonMark, non-strict CommonMark,
|
|
159
|
-
|
|
151
|
+
pandoc and GitHub all produce the same result — the ambiguous
|
|
160
152
|
input case is simply gone. This is HTML's content model applied to plain
|
|
161
153
|
text: a newline is markup meaning "break here", not cosmetic formatting of
|
|
162
154
|
the source file.
|
|
163
155
|
|
|
164
156
|
It also keeps **line length a view decision rather than a content one**.
|
|
165
157
|
Hard-wrapping is the author asserting a measure, baking one viewport into
|
|
166
|
-
the text; unwrapped, the same bytes are correct at every width —
|
|
167
|
-
|
|
168
|
-
mobile, print. Hard-wrapped prose fails both ways and is unfixable at read
|
|
158
|
+
the text; unwrapped, the same bytes are correct at every width — any
|
|
159
|
+
reader's readable-line-length setting on or off, a narrow split pane, a
|
|
160
|
+
wide monitor, mobile, print. Hard-wrapped prose fails both ways and is unfixable at read
|
|
169
161
|
time: at 80 columns it double-wraps raggedly in a narrow pane, and sits as a
|
|
170
162
|
fixed narrow ribbon in a wide one. Wanting a ~66-character measure is right
|
|
171
163
|
(it's a real typographic optimum, which is why that setting exists) — put it
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
1
|
+
# Read live from the Synapse package as a fallback (tier 3) -- never copied or
|
|
2
|
+
# seeded anywhere, and this template file is never written to. To customize,
|
|
3
|
+
# create your own synapse-ignore-files.conf at $XDG_CONFIG_HOME/synapse/, else
|
|
4
|
+
# ~/.config/synapse/ (the legacy ~/.claude/ also works); your own copy always
|
|
5
|
+
# wins and is never touched by Synapse.
|
|
6
|
+
# Edit or extend your own copy any time.
|
|
3
7
|
#
|
|
4
8
|
# Extra path patterns dropped at enumeration by synapse build-lists, on top
|
|
5
9
|
# of the built-in exclusions (compiled objects, archives, media, model weights,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
#
|
|
1
|
+
# Read live from the Synapse package as a fallback (tier 3) -- never copied or
|
|
2
|
+
# seeded anywhere, and this template file is never written to. To customize,
|
|
3
|
+
# create your own synapse-module-boilerplate.conf at $XDG_CONFIG_HOME/synapse/, else
|
|
4
|
+
# ~/.config/synapse/ (the legacy ~/.claude/ also works); your own copy always
|
|
5
|
+
# wins and is never touched by Synapse.
|
|
6
|
+
# Add your own ecosystem's conventions any time.
|
|
4
7
|
#
|
|
5
8
|
# Boilerplate path-segment chains that module_of() (synapse query,
|
|
6
9
|
# synapse write-node's ## Sources mirror) strips through wholesale when
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Read live from the Synapse package as a fallback (tier 3) -- never copied or
|
|
2
|
+
# seeded anywhere, and this template file is never written to. To customize,
|
|
3
|
+
# create your own synapse-projects.conf at $XDG_CONFIG_HOME/synapse/, else
|
|
4
|
+
# ~/.config/synapse/ (the legacy ~/.claude/ also works); your own copy always
|
|
5
|
+
# wins and is never touched by Synapse.
|
|
2
6
|
#
|
|
3
7
|
# Known project -> task-prefix mappings for THIS machine only. Deliberately
|
|
4
8
|
# separate from the portable Synapse package (never committed
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
1
|
+
# Read live from the Synapse package as a fallback (tier 3) -- never copied or
|
|
2
|
+
# seeded anywhere, and this template file is never written to. To customize,
|
|
3
|
+
# create your own synapse-prompt-stopwords.conf at $XDG_CONFIG_HOME/synapse/, else
|
|
4
|
+
# ~/.config/synapse/ (the legacy ~/.claude/ also works); your own copy always
|
|
5
|
+
# wins and is never touched by Synapse.
|
|
6
|
+
# Edit or extend your own copy any time.
|
|
3
7
|
#
|
|
4
8
|
# Common English function words, filtered out by `synapse vocab` before
|
|
5
9
|
# building a search pattern from a raw prompt -- without this, an ordinary
|
package/synapse.conf.template
CHANGED
|
@@ -5,15 +5,12 @@ SYNAPSE_VAULT_DIR="$HOME/Vault/YourVault"
|
|
|
5
5
|
# Unset by default: reads/writes the vault folder directly, no external
|
|
6
6
|
# dependency at all -- search and the link graph
|
|
7
7
|
# (backlinks/links/unresolved/orphans/deadends/ambiguous) are computed by
|
|
8
|
-
# DiskStore's own scan.
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# "git,obsidian" combines both. The disk store itself is never named -- it's
|
|
15
|
-
# always the implicit innermost element.
|
|
16
|
-
#SYNAPSE_VAULT_INTEGRATIONS="obsidian"
|
|
8
|
+
# DiskStore's own scan. The one optional integration, "git", layers extra
|
|
9
|
+
# behavior on top instead: it owns the vault's own git lifecycle (commit on
|
|
10
|
+
# write, push once enough pile up) -- see
|
|
11
|
+
# docs/synapse/synapse-extended-store.md. The disk store itself is never
|
|
12
|
+
# named -- it's always the implicit innermost element.
|
|
13
|
+
#SYNAPSE_VAULT_INTEGRATIONS="git"
|
|
17
14
|
|
|
18
15
|
# Where Synapse clones/builds tree-sitter grammars (shared across every
|
|
19
16
|
# project -- not per-repo). Safe to leave at the default.
|