@imunitic/synapse 0.1.1 → 0.2.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/bin/synapse-setup.cjs +1 -1
- package/commands/synapse-design-note.md +20 -12
- package/commands/synapse-init.md +4 -4
- package/commands/synapse-note.md +12 -13
- package/commands/synapse-rebuild-diff.md +1 -1
- package/commands/synapse-rebuild-full.md +1 -1
- package/commands/synapse-status.md +55 -40
- package/commands/synapse-task-note.md +13 -6
- package/commands/synapse-vault-tidy.md +1 -1
- package/harness/claude/hooks.json +1 -1
- package/harness/codex/hooks.json +1 -1
- package/harness/codex/skills/synapse-design-note/SKILL.md +19 -11
- package/harness/codex/skills/synapse-init/SKILL.md +4 -4
- package/harness/codex/skills/synapse-note/SKILL.md +11 -11
- package/harness/codex/skills/synapse-rebuild-diff/SKILL.md +1 -1
- package/harness/codex/skills/synapse-rebuild-full/SKILL.md +1 -1
- package/harness/codex/skills/synapse-status/SKILL.md +48 -37
- package/harness/codex/skills/synapse-task-note/SKILL.md +13 -6
- package/harness/codex/skills/synapse-vault-tidy/SKILL.md +1 -1
- package/harness/opencode/plugin/synapse.js +25 -5
- package/lib/obsidian-mcp-refresh.cjs +3 -3
- package/package.json +4 -4
- package/skills/synapse-node/SKILL.md +6 -6
- package/skills/synapse-query/SKILL.md +2 -2
- package/skills/synapse-task/SKILL.md +36 -41
- package/skills/synapse-vault/SKILL.md +46 -38
- package/synapse-claude.md +48 -48
- package/synapse.conf.template +1 -1
package/bin/synapse-setup.cjs
CHANGED
|
@@ -69,7 +69,7 @@ function readObsidianPluginData() {
|
|
|
69
69
|
const { resolveVaultDir } = require("../lib/obsidian-mcp-refresh.cjs");
|
|
70
70
|
const vault = resolveVaultDir();
|
|
71
71
|
if (!vault || !fs.existsSync(vault)) {
|
|
72
|
-
fail("no
|
|
72
|
+
fail("no SYNAPSE_VAULT_DIR resolvable (see synapse.conf) -- can't configure the obsidian MCP connection");
|
|
73
73
|
}
|
|
74
74
|
const pluginDataPath = path.join(vault, ".obsidian", "plugins", "obsidian-local-rest-api", "data.json");
|
|
75
75
|
if (!fs.existsSync(pluginDataPath)) {
|
|
@@ -17,14 +17,14 @@ Not every design discussion ends with something to build. See `Status: Reference
|
|
|
17
17
|
```
|
|
18
18
|
/synapse-design-note "topic" # Start or resume a design discussion
|
|
19
19
|
/synapse-design-note --continue # Resume an incomplete design note
|
|
20
|
-
/synapse-design-note --list # List every
|
|
20
|
+
/synapse-design-note --list # List every design note in the vault, regardless of status
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Prerequisites
|
|
24
24
|
|
|
25
|
-
- Requires the `
|
|
26
|
-
there is no
|
|
27
|
-
- If `designs/` doesn't exist in the vault yet, `
|
|
25
|
+
- Requires the `synapse` CLI on `PATH`. If it errors (no vault configured), say so and stop —
|
|
26
|
+
there is no other fallback for this command.
|
|
27
|
+
- If `designs/` doesn't exist in the vault yet, `synapse vault-write` creates it implicitly on
|
|
28
28
|
first write — but add a `designs/` entry to the vault's `Index.md` folder layout in the same
|
|
29
29
|
action (per the Synapse Vault folder-layout rule: a new top-level folder must never fall behind the
|
|
30
30
|
index).
|
|
@@ -34,7 +34,7 @@ Not every design discussion ends with something to build. See `Status: Reference
|
|
|
34
34
|
Every design note is tagged with the project it belongs to — both in the title
|
|
35
35
|
(`{PROJECT} — {Topic}`) and as `project: {prefix}` in frontmatter (the same short prefix
|
|
36
36
|
`/synapse-note --task` uses for task IDs) — so a flat `designs/` folder still reads clearly, and
|
|
37
|
-
both note kinds can be filtered together via `
|
|
37
|
+
both note kinds can be filtered together via `synapse vault-search`.
|
|
38
38
|
|
|
39
39
|
Same resolution `/synapse-note` uses for a missing task ID (its "Resolving a missing task ID"),
|
|
40
40
|
reading the same file:
|
|
@@ -59,15 +59,22 @@ unrelated contexts (e.g. personal vs. work) never end up in the same place.
|
|
|
59
59
|
## Handling Arguments
|
|
60
60
|
|
|
61
61
|
**No arguments:**
|
|
62
|
-
1. Check for incomplete design notes: search `
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
1. Check for incomplete design notes: `synapse vault-search --fields frontmatter.title` with the
|
|
63
|
+
query below on stdin — one call, no separate read needed:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
{"and": [
|
|
67
|
+
{"glob": ["designs/*", {"var": "path"}]},
|
|
68
|
+
{"regexp": ["## Status\\nDiscussing", {"var": "content"}]}
|
|
69
|
+
]}
|
|
70
|
+
```
|
|
65
71
|
2. If found: show a short state summary — title and current section — and offer to resume.
|
|
66
72
|
3. If none: ask "What are we designing?"
|
|
67
73
|
|
|
68
74
|
**With a topic:**
|
|
69
|
-
1. Search first — `
|
|
70
|
-
Synapse Vault rule: link/reuse over duplicate). Also check for an
|
|
75
|
+
1. Search first — `synapse vault-search-text "{topic}"` for the topic text, then filter matches to
|
|
76
|
+
`designs/` paths (per the Synapse Vault rule: link/reuse over duplicate). Also check for an
|
|
77
|
+
obvious title match.
|
|
71
78
|
2. If found with `Status: Discussing` → ask "Resume this design?" or "Start fresh?"
|
|
72
79
|
3. If found with `Status: Ready` → ask "Already marked Ready. Reopen to revise, or start a new note?"
|
|
73
80
|
4. If found with `Status: Reference` → ask "This concluded as Reference (no implementation intended).
|
|
@@ -82,8 +89,9 @@ unrelated contexts (e.g. personal vs. work) never end up in the same place.
|
|
|
82
89
|
4. None → "No incomplete design note found. Start one with `/synapse-design-note \"topic\"`."
|
|
83
90
|
|
|
84
91
|
**--list:**
|
|
85
|
-
1. `
|
|
86
|
-
|
|
92
|
+
1. `synapse vault-search --fields frontmatter.title,content` with `{"glob": ["designs/*", {"var":
|
|
93
|
+
"path"}]}` on stdin — every design note regardless of status, title and `## Status` line both in
|
|
94
|
+
the same row.
|
|
87
95
|
2. None found → "No design notes yet. Start one with `/synapse-design-note \"topic\"`."
|
|
88
96
|
3. Group into **Active** (`Discussing`, `Ready`) and **Closed** (`Reference`) — active first, title
|
|
89
97
|
and status in backticks, not bold.
|
package/commands/synapse-init.md
CHANGED
|
@@ -23,7 +23,7 @@ No arguments — always operates on the repo containing the current working dire
|
|
|
23
23
|
|
|
24
24
|
## Prerequisites
|
|
25
25
|
|
|
26
|
-
- Requires the `
|
|
26
|
+
- Requires the `synapse` CLI on `PATH`. If it errors (no vault configured), say so and stop.
|
|
27
27
|
- Must be run from inside a git repository. Synapse assumes git throughout (source hashing uses
|
|
28
28
|
`git hash-object`, file enumeration uses `git ls-files`) — if `git rev-parse --show-toplevel`
|
|
29
29
|
fails, stop and say this only works inside a git repo.
|
|
@@ -65,8 +65,8 @@ Every step below needs the same three facts, resolved once up front:
|
|
|
65
65
|
|
|
66
66
|
## Already initialized?
|
|
67
67
|
|
|
68
|
-
Check whether `synapse/{repo}@{branch}/Index.md` exists
|
|
69
|
-
|
|
68
|
+
Check whether `synapse/{repo}@{branch}/Index.md` exists: `synapse vault-read
|
|
69
|
+
"synapse/{repo}@{branch}/Index.md"` and see whether it succeeds or reports "no such note".
|
|
70
70
|
|
|
71
71
|
- **Doesn't exist** → this is a first-time build. Go to "First-time build" below.
|
|
72
72
|
- **Exists, `remote` frontmatter matches** the resolved remote/path → this namespace already
|
|
@@ -97,7 +97,7 @@ yours and cannot be scripted because what counts as signal differs per codebase.
|
|
|
97
97
|
(hash, digest, `## Sources` mirror, PUT), `synapse push-nodes`, `synapse build-index`,
|
|
98
98
|
`synapse build-project-index`.
|
|
99
99
|
|
|
100
|
-
**The work directory** defaults to `~/.
|
|
100
|
+
**The work directory** defaults to `~/.cache/synapse/work/{repo}@{branch}/`, created on demand, and
|
|
101
101
|
holds `manifest.tsv`, `all.txt`, `lists/`, the authored `b-NN.md` bodies and the coverage files. Override with `$SYNAPSE_WORK_DIR` if you need to. Two things never to do: point it
|
|
102
102
|
at the repo (`synapse` runs from inside the repo, so its working files would land in the user's
|
|
103
103
|
checkout) or at the vault (Obsidian would index a file list that runs to six figures of lines).
|
package/commands/synapse-note.md
CHANGED
|
@@ -26,7 +26,7 @@ If no match is found, **don't just leave it blank** — see "Resolving a missing
|
|
|
26
26
|
|
|
27
27
|
## List mode
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Run `synapse vault-search --fields frontmatter.task_id,frontmatter.status,frontmatter.title` with the JsonLogic query `{"var": "frontmatter.task_id"}` on stdin — one call returns every file that has a `task_id` set, each row already carrying `task_id`/`status`/`title` together, no second query or extra read needed.
|
|
30
30
|
|
|
31
31
|
Categorize:
|
|
32
32
|
- **Prefixed notes**: `task_id` matches `{prefix}-\d+`. Group by the distinct prefix found (whatever
|
|
@@ -43,8 +43,8 @@ Do not modify any files in list mode.
|
|
|
43
43
|
|
|
44
44
|
Everything after `--search` (trimmed, quotes stripped) is the query.
|
|
45
45
|
|
|
46
|
-
1. Run `
|
|
47
|
-
2. If the query looks like it's targeting metadata specifically (a tag, a task ID, a status value) rather than free text, also run `
|
|
46
|
+
1. Run `synapse vault-search-text "{query}"` — this gives full-text relevance-ranked matches with context, the closest equivalent to a title/body search.
|
|
47
|
+
2. If the query looks like it's targeting metadata specifically (a tag, a task ID, a status value) rather than free text, also run `synapse vault-search --fields frontmatter.title` with an appropriate JsonLogic filter on stdin (e.g. `{"==": [{"var": "frontmatter.task_id"}, "proj-032"]}`).
|
|
48
48
|
3. Report matches as `{title} — {file path relative to vault root}`, deduped across both. If nothing matches, say so plainly — the caller (agent or user) needs a clear "no existing note" signal to proceed with `--task`-less creation.
|
|
49
49
|
|
|
50
50
|
Do not modify any files in search mode.
|
|
@@ -54,8 +54,8 @@ Do not modify any files in search mode.
|
|
|
54
54
|
Triggered when `--task` is given but the title doesn't match `{prefix}-\d+`.
|
|
55
55
|
|
|
56
56
|
The known project/prefix pairs live in a plain local file named `synapse-projects.conf` (one
|
|
57
|
-
`project-name=prefix` line each) — read/appended with the Read/Edit tools, not the `
|
|
58
|
-
|
|
57
|
+
`project-name=prefix` line each) — read/appended with the Read/Edit tools, not the `vault-*` subcommands,
|
|
58
|
+
since it's outside the vault. It is deliberately **not** part of the portable
|
|
59
59
|
Synapse package and never copied between machines, so contexts that shouldn't mix (e.g.
|
|
60
60
|
personal vs. work projects) never end up in the same file. It's self-managed — this command appends
|
|
61
61
|
newly resolved pairs to it — but also plain text, so the user can add, fix, or remove a line by hand
|
|
@@ -77,8 +77,8 @@ unchanged for anyone who has never touched an XDG config directory.
|
|
|
77
77
|
2. Check the resolved `synapse-projects.conf` for a line whose project name matches (loosely —
|
|
78
78
|
case/whitespace-insensitive). If found, use that prefix directly — no need to ask.
|
|
79
79
|
3. If the file doesn't have it yet, fall back to deducing from the vault itself (useful the first
|
|
80
|
-
time this runs, or for a project whose notes predate this file): `
|
|
81
|
-
project/repo name across existing notes, and/or `
|
|
80
|
+
time this runs, or for a project whose notes predate this file): `synapse vault-search-text` for
|
|
81
|
+
the project/repo name across existing notes, and/or `synapse vault-search` on
|
|
82
82
|
`{"var": "frontmatter.task_id"}` to see which prefixes exist, then check whether any of those
|
|
83
83
|
prefixed notes reference this project. If exactly one prefix confidently matches, use it.
|
|
84
84
|
4. If nothing confidently matches (new project, or an ambiguous/multiple match), ask the user
|
|
@@ -87,11 +87,10 @@ unchanged for anyone who has never touched an XDG config directory.
|
|
|
87
87
|
5. Whenever step 3 or step 4 resolves a pair not already in the conf file (including a fresh
|
|
88
88
|
`project-name=prefix` line matching what was just deduced or asked), append it — so the next task
|
|
89
89
|
for this project resolves from step 2 without a search or a question.
|
|
90
|
-
6. Once the prefix is known, find the next number: run
|
|
91
|
-
`
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
for that prefix, start at 1.
|
|
90
|
+
6. Once the prefix is known, find the next number: run `synapse vault-search --fields
|
|
91
|
+
frontmatter.task_id` with `{"var": "frontmatter.task_id"}` on stdin, filter the returned values
|
|
92
|
+
client-side for ones matching `{prefix}-\d+`, take the highest number found, add 1. If none exist
|
|
93
|
+
yet for that prefix, start at 1.
|
|
95
94
|
7. Format the new task ID **zero-padded to 3 digits** (`{prefix}-001`, `{prefix}-030`,
|
|
96
95
|
`{prefix}-037`, ...), matching the org-roam-era convention — widening
|
|
97
96
|
naturally past 3 digits if a prefix ever needs it.
|
|
@@ -181,7 +180,7 @@ or supplied directly by a caller like `/synapse-task-note`):
|
|
|
181
180
|
## Notes
|
|
182
181
|
|
|
183
182
|
```
|
|
184
|
-
4. Write it with `
|
|
183
|
+
4. Write it with `synapse vault-write <path>` (content on stdin). Task mode: path
|
|
185
184
|
`tasks/{project}/{filename}.md` (project resolved in "Resolving the
|
|
186
185
|
project folder" above). Bare mode: path `{category}/{filename}.md`
|
|
187
186
|
(category resolved above).
|
|
@@ -85,7 +85,7 @@ this command when it does. What no longer happens is arriving here merely becaus
|
|
|
85
85
|
because it is not this checkout's namespace to diff at all. Never conflate the two: a non-ancestor
|
|
86
86
|
baseline on a branch-identity match still proceeds normally, per the "One mechanical fact about
|
|
87
87
|
branches" section above.
|
|
88
|
-
- The work directory (`$SYNAPSE_WORK_DIR`, default `~/.
|
|
88
|
+
- The work directory (`$SYNAPSE_WORK_DIR`, default `~/.cache/synapse/work/{repo}@{branch}/`) ideally
|
|
89
89
|
holds the `manifest.tsv` from the original build. Without it, new paths cannot be classified as
|
|
90
90
|
auto-claimable, and clustering decisions have to be re-derived — say so rather than proceeding as if
|
|
91
91
|
nothing were missing. `synapse/{repo}@{branch}/_manifest.tsv` is the fallback copy.
|
|
@@ -29,7 +29,7 @@ No arguments — always operates on the repo and branch containing the current w
|
|
|
29
29
|
|
|
30
30
|
## Prerequisites
|
|
31
31
|
|
|
32
|
-
- Requires the `
|
|
32
|
+
- Requires the `synapse` CLI on `PATH`. If it errors (no vault configured), say so and stop —
|
|
33
33
|
same requirement `/synapse-init` has.
|
|
34
34
|
- Must be run from inside a git repository, on a named branch (not detached `HEAD`) — same
|
|
35
35
|
requirement `/synapse-init` has, since `synapse_namespace` needs a branch to key on.
|
|
@@ -6,12 +6,11 @@ description: Print a read-only report of what in Synapse Vault currently needs a
|
|
|
6
6
|
|
|
7
7
|
A read-only sweep over Synapse Vault answering one question: what currently needs a human decision?
|
|
8
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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
behave correctly.
|
|
9
|
+
to hand to someone else and not a live dashboard (a published Artifact has no route to the vault, so
|
|
10
|
+
nothing here is ever presented that way). Run it on demand, or from a scheduled `/loop`/cron
|
|
11
|
+
invocation of this same command -- never wired into `SessionStart`: some of the five categories need
|
|
12
|
+
a per-note content read, not just a frontmatter check, and unlike `Index.md` this report is a
|
|
13
|
+
periodic human check-in, not something the agent needs injected every session to behave correctly.
|
|
15
14
|
|
|
16
15
|
Scoped to `designs/`/`tasks/` only -- the two folders `/synapse-design-note`/`/synapse-note --task`
|
|
17
16
|
structurally require, so every Synapse install has them in the same shape. The free-form taxonomy
|
|
@@ -27,87 +26,104 @@ periodic look on their own schedule -- this report doesn't cover it.
|
|
|
27
26
|
|
|
28
27
|
## Prerequisites
|
|
29
28
|
|
|
30
|
-
Requires the `
|
|
31
|
-
|
|
29
|
+
Requires the `synapse` CLI on `PATH`. Backend-agnostic -- every query below goes through `synapse
|
|
30
|
+
vault-search`, so it works the same whether the vault is Obsidian-backed or plain-disk-backed. If the
|
|
31
|
+
CLI errors (no vault configured), say so and stop -- there is no other fallback.
|
|
32
32
|
|
|
33
33
|
## Producing the report
|
|
34
34
|
|
|
35
|
-
Run
|
|
36
|
-
|
|
35
|
+
Run each query below via `synapse vault-search --fields <fields>`, with the JsonLogic query on stdin.
|
|
36
|
+
`--fields` asks for exactly the columns the query needs back -- no separate read per match required,
|
|
37
|
+
since a matched row already carries them.
|
|
37
38
|
|
|
38
39
|
**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
|
+
frontmatter (unlike task notes) -- a content match, scoped to `designs/`. Needs only the title:
|
|
40
41
|
|
|
41
42
|
```
|
|
43
|
+
synapse vault-search --fields frontmatter.title <<'EOF'
|
|
42
44
|
{"and": [
|
|
43
45
|
{"glob": ["designs/*", {"var": "path"}]},
|
|
44
46
|
{"regexp": ["## Status\\nDiscussing", {"var": "content"}]}
|
|
45
47
|
]}
|
|
48
|
+
EOF
|
|
46
49
|
```
|
|
47
50
|
|
|
48
51
|
**2. `Ready` design notes with no compiled task yet.** `/synapse-task-note`'s own "Linking back"
|
|
49
52
|
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
|
|
53
|
+
"`Ready` and missing that line" is a direct signal, not fuzzy title-matching against `tasks/`. Needs
|
|
54
|
+
only the title:
|
|
51
55
|
|
|
52
56
|
```
|
|
57
|
+
synapse vault-search --fields frontmatter.title <<'EOF'
|
|
53
58
|
{"and": [
|
|
54
59
|
{"glob": ["designs/*", {"var": "path"}]},
|
|
55
60
|
{"regexp": ["## Status\\nReady", {"var": "content"}]},
|
|
56
61
|
{"!": [{"regexp": ["Compiled task:", {"var": "content"}]}]}
|
|
57
62
|
]}
|
|
63
|
+
EOF
|
|
58
64
|
```
|
|
59
65
|
|
|
60
66
|
**3. Design notes (any status) with a non-empty `## Open Questions`.** Match the heading followed by
|
|
61
67
|
at least one bullet -- a heading with nothing under it (fully pruned, per the Ready-gate convention
|
|
62
68
|
`/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
|
|
69
|
+
each line in the composed report also shows *which* status the note is currently in, and whether it
|
|
70
|
+
has a compiled task note -- request `content` too, and pull both directly out of the returned text
|
|
71
|
+
rather than reading the note again:
|
|
64
72
|
|
|
65
73
|
```
|
|
74
|
+
synapse vault-search --fields frontmatter.title,content <<'EOF'
|
|
66
75
|
{"and": [
|
|
67
76
|
{"glob": ["designs/*", {"var": "path"}]},
|
|
68
77
|
{"regexp": ["## Open Questions\\n- ", {"var": "content"}]}
|
|
69
78
|
]}
|
|
79
|
+
EOF
|
|
70
80
|
```
|
|
71
81
|
|
|
72
|
-
`regexp`
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
Do not chain further `and`/`regexp` conditions onto this query to sort matches by status -- compound
|
|
83
|
+
`regexp` conditions against `content` have been observed to return results that are logically
|
|
84
|
+
impossible (a query with strictly more AND-ed conditions returning *more* matches than one with
|
|
85
|
+
fewer), so any query beyond a single `glob` + single `regexp` pair is unverified and not to be
|
|
86
|
+
trusted here. Instead, take two things from each row's `content` column directly, no second call
|
|
87
|
+
needed for either: the line following `## Status` (normally `Discussing`/`Ready`/`Reference`, but a
|
|
88
|
+
note written before the three-word convention can carry free text instead, e.g. `Superseded by
|
|
89
|
+
[[...]]` -- report that verbatim rather than forcing it into a bucket, surfacing an odd note beats
|
|
90
|
+
losing it) and the target of a `> Compiled task: [[...]]` line, if present -- that's the compiled
|
|
91
|
+
task, parsed straight out of the wikilink text rather than resolved through any vault-wide link
|
|
92
|
+
index.
|
|
82
93
|
|
|
83
94
|
**4. Open task notes with at least one unchecked item.** Task notes carry `status:` in frontmatter,
|
|
84
|
-
unlike design notes -- filter there first
|
|
95
|
+
unlike design notes -- filter there first, and request `content` to count `- [ ]` lines directly from
|
|
96
|
+
the row rather than reading each match again:
|
|
85
97
|
|
|
86
98
|
```
|
|
99
|
+
synapse vault-search --fields frontmatter.title,content <<'EOF'
|
|
87
100
|
{"in": [{"var": "frontmatter.status"}, ["TODO", "IN-PROGRESS"]]}
|
|
101
|
+
EOF
|
|
88
102
|
```
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
dropping it, since that state itself is worth a human noticing.
|
|
104
|
+
A match with zero unchecked lines (a checklist that's fully checked but hasn't been promoted to
|
|
105
|
+
`REVIEW` yet) is still worth surfacing -- report it under this section with its count shown as 0,
|
|
106
|
+
rather than silently dropping it, since that state itself is worth a human noticing.
|
|
94
107
|
|
|
95
|
-
**5. Task notes stuck in `REVIEW`.** Frontmatter-only, no body
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
**5. Task notes stuck in `REVIEW`.** Frontmatter-only, no body needed -- a fully-checked checklist
|
|
109
|
+
waiting specifically on human sign-off, since `synapse-task` deliberately never promotes a note past
|
|
110
|
+
`REVIEW` on its own:
|
|
98
111
|
|
|
99
112
|
```
|
|
113
|
+
synapse vault-search --fields frontmatter.title <<'EOF'
|
|
100
114
|
{"==": [{"var": "frontmatter.status"}, "REVIEW"]}
|
|
115
|
+
EOF
|
|
101
116
|
```
|
|
102
117
|
|
|
103
118
|
## Composing the report
|
|
104
119
|
|
|
105
120
|
One section per category, in the order above. Each line names the note (title, or filename if no
|
|
106
121
|
`title` frontmatter) plus the one identifying detail that category needs. The Open Questions section
|
|
107
|
-
is the one place a note's status also
|
|
108
|
-
implies
|
|
109
|
-
|
|
110
|
-
status
|
|
122
|
+
is the one place a note's status and compiled-task link also belong on the line -- every other
|
|
123
|
+
section's heading already implies status (the "Discussing" section only ever holds `Discussing`
|
|
124
|
+
notes) and compiled-task-ness (the "Ready, not yet compiled" section only ever holds notes without
|
|
125
|
+
one), but Open Questions spans every status and both compiled and uncompiled notes, so put the status
|
|
126
|
+
first, before the title, so it's the first thing scanned:
|
|
111
127
|
|
|
112
128
|
```
|
|
113
129
|
## Discussing
|
|
@@ -117,7 +133,7 @@ status first, before the title, so it's the first thing scanned:
|
|
|
117
133
|
- {title}
|
|
118
134
|
|
|
119
135
|
## Open questions
|
|
120
|
-
- **{status}** — {title}
|
|
136
|
+
- **{status}** — {title} — {compiled task title, or "not compiled"}
|
|
121
137
|
|
|
122
138
|
## In progress (unchecked items)
|
|
123
139
|
- {title} ({N} unchecked)
|
|
@@ -136,9 +152,8 @@ go dig for.
|
|
|
136
152
|
|
|
137
153
|
## Constraints
|
|
138
154
|
|
|
139
|
-
- Read-only end to end. Never calls `
|
|
140
|
-
|
|
141
|
-
special-case around.
|
|
155
|
+
- Read-only end to end. Never calls `vault-write`/`vault-patch` -- if a step here ever seems to need
|
|
156
|
+
one, that step is out of scope, not a case to special-case around.
|
|
142
157
|
- 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 `
|
|
158
|
+
category above is a plain mechanical query already reachable through `synapse vault-search`.
|
|
144
159
|
- Scoped to `designs/`/`tasks/` only -- never `inbox/`/`research/`/`scratchpad/`.
|
|
@@ -89,12 +89,19 @@ duplicate that scaffolding here, just supply its inputs:
|
|
|
89
89
|
|
|
90
90
|
## Linking back
|
|
91
91
|
|
|
92
|
-
After creation, patch the design note: add `> Compiled task: [[{task note title}]]` near the top
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
After creation, patch the design note: add `> Compiled task: [[{task note title}]]` near the top —
|
|
93
|
+
right after the `# {title}` heading, before `## Status`:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
printf '> Compiled task: [[%s]]\n\n' "{task note title}" | \
|
|
97
|
+
synapse vault-patch "{design note path}" --heading "{design note title}" --prepend
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
A small annotation line, same idea as any other cross-reference you'd drop near a note's title. This
|
|
101
|
+
is a one-time link; the design note's `Status` itself does not change and does not need to track the
|
|
102
|
+
task's progress — Obsidian's own backlinks panel (or a plain `synapse vault-search-text` for the task
|
|
103
|
+
note's title, on a disk-backed vault) already surfaces the live task note from the design note, and
|
|
104
|
+
the task's `status:` frontmatter is the authoritative progress signal from here on.
|
|
98
105
|
|
|
99
106
|
## Confirm
|
|
100
107
|
|
|
@@ -89,7 +89,7 @@ instead of a silent edit:
|
|
|
89
89
|
local git history to say *why* it's broken instead of leaving that to guesswork — the vault is
|
|
90
90
|
usually a git repo (`db-sync` auto-commits every agent-driven edit into it, opt-in per vault, same
|
|
91
91
|
precondition as that hook). Resolve the vault's filesystem path the same way `synapse.conf`
|
|
92
|
-
already does (`
|
|
92
|
+
already does (`SYNAPSE_VAULT_DIR`), skip this sub-step entirely if `{vault}/.git` doesn't exist,
|
|
93
93
|
and never let a missing/unreachable git repo block the rest of the finding — worst case it's
|
|
94
94
|
reported with no history context, same as today.
|
|
95
95
|
- `git -C {vault} log --all --diff-filter=A --name-only --pretty=format: -- "**/{target}.md"` — a
|
package/harness/codex/hooks.json
CHANGED
|
@@ -29,9 +29,9 @@ There is no flag syntax here — read intent from how the user asks:
|
|
|
29
29
|
|
|
30
30
|
## Prerequisites
|
|
31
31
|
|
|
32
|
-
- Requires the `
|
|
33
|
-
there is no
|
|
34
|
-
- If `designs/` doesn't exist in the vault yet, `
|
|
32
|
+
- Requires the `synapse` CLI on `PATH`. If it errors (no vault configured), say so and stop —
|
|
33
|
+
there is no other fallback for this skill.
|
|
34
|
+
- If `designs/` doesn't exist in the vault yet, `synapse vault-write` creates it implicitly on
|
|
35
35
|
first write — but add a `designs/` entry to the vault's index note's folder layout in the same
|
|
36
36
|
action (per the Synapse Vault folder-layout rule: a new top-level folder must never fall behind the
|
|
37
37
|
index).
|
|
@@ -41,7 +41,7 @@ There is no flag syntax here — read intent from how the user asks:
|
|
|
41
41
|
Every design note is tagged with the project it belongs to — both in the title
|
|
42
42
|
(`{PROJECT} — {Topic}`) and as `project: {prefix}` in frontmatter (the same short prefix the
|
|
43
43
|
synapse-note skill's task mode uses for task IDs) — so a flat `designs/` folder still reads clearly,
|
|
44
|
-
and both note kinds can be filtered together via `
|
|
44
|
+
and both note kinds can be filtered together via `synapse vault-search`.
|
|
45
45
|
|
|
46
46
|
Same resolution the synapse-note skill uses for a missing task ID (its "Resolving a missing task
|
|
47
47
|
ID"), reading the same file:
|
|
@@ -66,15 +66,22 @@ unrelated contexts (e.g. personal vs. work) never end up in the same place.
|
|
|
66
66
|
## Handling intent
|
|
67
67
|
|
|
68
68
|
**No specific signal:**
|
|
69
|
-
1. Check for incomplete design notes: search `
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
1. Check for incomplete design notes: `synapse vault-search --fields frontmatter.title` with the
|
|
70
|
+
query below on stdin — one call, no separate read needed:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
{"and": [
|
|
74
|
+
{"glob": ["designs/*", {"var": "path"}]},
|
|
75
|
+
{"regexp": ["## Status\\nDiscussing", {"var": "content"}]}
|
|
76
|
+
]}
|
|
77
|
+
```
|
|
72
78
|
2. If found: show a short state summary — title and current section — and offer to resume.
|
|
73
79
|
3. If none: ask "What are we designing?"
|
|
74
80
|
|
|
75
81
|
**With a topic:**
|
|
76
|
-
1. Search first — `
|
|
77
|
-
Synapse Vault rule: link/reuse over duplicate). Also check for an
|
|
82
|
+
1. Search first — `synapse vault-search-text "{topic}"` for the topic text, then filter matches to
|
|
83
|
+
`designs/` paths (per the Synapse Vault rule: link/reuse over duplicate). Also check for an
|
|
84
|
+
obvious title match.
|
|
78
85
|
2. If found with `Status: Discussing` → ask "Resume this design?" or "Start fresh?"
|
|
79
86
|
3. If found with `Status: Ready` → ask "Already marked Ready. Reopen to revise, or start a new note?"
|
|
80
87
|
4. If found with `Status: Reference` → ask "This concluded as Reference (no implementation intended).
|
|
@@ -89,8 +96,9 @@ unrelated contexts (e.g. personal vs. work) never end up in the same place.
|
|
|
89
96
|
4. None → "No incomplete design note found. Tell me what you'd like to design and I'll start one."
|
|
90
97
|
|
|
91
98
|
**Listing design notes:**
|
|
92
|
-
1. `
|
|
93
|
-
|
|
99
|
+
1. `synapse vault-search --fields frontmatter.title,content` with `{"glob": ["designs/*", {"var":
|
|
100
|
+
"path"}]}` on stdin — every design note regardless of status, title and `## Status` line both in
|
|
101
|
+
the same row.
|
|
94
102
|
2. None found → "No design notes yet. Tell me what you'd like to design and I'll start one."
|
|
95
103
|
3. Group into **Active** (`Discussing`, `Ready`) and **Closed** (`Reference`) — active first, title
|
|
96
104
|
and status in backticks, not bold.
|
|
@@ -23,7 +23,7 @@ directory.
|
|
|
23
23
|
|
|
24
24
|
## Prerequisites
|
|
25
25
|
|
|
26
|
-
- Requires the `
|
|
26
|
+
- Requires the `synapse` CLI on `PATH`. If it errors (no vault configured), say so and stop.
|
|
27
27
|
- Must be run from inside a git repository. Synapse assumes git throughout (source hashing uses
|
|
28
28
|
`git hash-object`, file enumeration uses `git ls-files`) — if `git rev-parse --show-toplevel`
|
|
29
29
|
fails, stop and say this only works inside a git repo.
|
|
@@ -65,8 +65,8 @@ Every step below needs the same three facts, resolved once up front:
|
|
|
65
65
|
|
|
66
66
|
## Already initialized?
|
|
67
67
|
|
|
68
|
-
Check whether `synapse/{repo}@{branch}/Index.md` exists
|
|
69
|
-
|
|
68
|
+
Check whether `synapse/{repo}@{branch}/Index.md` exists: `synapse vault-read
|
|
69
|
+
"synapse/{repo}@{branch}/Index.md"` and see whether it succeeds or reports "no such note".
|
|
70
70
|
|
|
71
71
|
- **Doesn't exist** → this is a first-time build. Go to "First-time build" below.
|
|
72
72
|
- **Exists, `remote` frontmatter matches** the resolved remote/path → this namespace already
|
|
@@ -97,7 +97,7 @@ yours and cannot be scripted because what counts as signal differs per codebase.
|
|
|
97
97
|
(hash, digest, `## Sources` mirror, PUT), `synapse push-nodes`, `synapse build-index`,
|
|
98
98
|
`synapse build-project-index`.
|
|
99
99
|
|
|
100
|
-
**The work directory** defaults to `~/.
|
|
100
|
+
**The work directory** defaults to `~/.cache/synapse/work/{repo}@{branch}/`, created on demand, and
|
|
101
101
|
holds `manifest.tsv`, `all.txt`, `lists/`, the authored `b-NN.md` bodies and the coverage files. Override with `$SYNAPSE_WORK_DIR` if you need to. Two things never to do: point it
|
|
102
102
|
at the repo (`synapse` runs from inside the repo, so its working files would land in the user's
|
|
103
103
|
checkout) or at the vault (Obsidian would index a file list that runs to six figures of lines).
|
|
@@ -44,7 +44,7 @@ proceeding.
|
|
|
44
44
|
|
|
45
45
|
## List mode
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Run `synapse vault-search --fields frontmatter.task_id,frontmatter.status,frontmatter.title` with the JsonLogic query `{"var": "frontmatter.task_id"}` on stdin — one call returns every file that has a `task_id` set, each row already carrying `task_id`/`status`/`title` together, no second query or extra read needed.
|
|
48
48
|
|
|
49
49
|
Categorize:
|
|
50
50
|
- **Prefixed notes**: `task_id` matches `{prefix}-\d+`. Group by the distinct prefix found (whatever
|
|
@@ -61,8 +61,8 @@ Do not modify any files in list mode.
|
|
|
61
61
|
|
|
62
62
|
The user's search text is the query — whatever they named after asking to search, quotes stripped.
|
|
63
63
|
|
|
64
|
-
1. Run `
|
|
65
|
-
2. If the query looks like it's targeting metadata specifically (a tag, a task ID, a status value) rather than free text, also run `
|
|
64
|
+
1. Run `synapse vault-search-text "{query}"` — this gives full-text relevance-ranked matches with context, the closest equivalent to a title/body search.
|
|
65
|
+
2. If the query looks like it's targeting metadata specifically (a tag, a task ID, a status value) rather than free text, also run `synapse vault-search --fields frontmatter.title` with an appropriate JsonLogic filter on stdin (e.g. `{"==": [{"var": "frontmatter.task_id"}, "proj-032"]}`).
|
|
66
66
|
3. Report matches as `{title} — {file path relative to vault root}`, deduped across both. If nothing matches, say so plainly — the caller (agent or user) needs a clear "no existing note" signal to proceed with task-mode-less creation.
|
|
67
67
|
|
|
68
68
|
Do not modify any files in search mode.
|
|
@@ -72,8 +72,8 @@ Do not modify any files in search mode.
|
|
|
72
72
|
Triggered when task mode is requested but the title doesn't match `{prefix}-\d+`.
|
|
73
73
|
|
|
74
74
|
The known project/prefix pairs live in a plain local file named `synapse-projects.conf` (one
|
|
75
|
-
`project-name=prefix` line each) — read/appended as a plain text file, not the `
|
|
76
|
-
|
|
75
|
+
`project-name=prefix` line each) — read/appended as a plain text file, not the `vault-*` subcommands,
|
|
76
|
+
since it's outside the vault. It is deliberately **not** part of the portable
|
|
77
77
|
Synapse package and never copied between machines, so contexts that shouldn't mix (e.g.
|
|
78
78
|
personal vs. work projects) never end up in the same file. It's self-managed — this skill appends
|
|
79
79
|
newly resolved pairs to it — but also plain text, so the user can add, fix, or remove a line by hand
|
|
@@ -95,8 +95,8 @@ unchanged for anyone who has never touched an XDG config directory.
|
|
|
95
95
|
2. Check the resolved `synapse-projects.conf` for a line whose project name matches (loosely —
|
|
96
96
|
case/whitespace-insensitive). If found, use that prefix directly — no need to ask.
|
|
97
97
|
3. If the file doesn't have it yet, fall back to deducing from the vault itself (useful the first
|
|
98
|
-
time this runs, or for a project whose notes predate this file): `
|
|
99
|
-
project/repo name across existing notes, and/or `
|
|
98
|
+
time this runs, or for a project whose notes predate this file): `synapse vault-search-text` for
|
|
99
|
+
the project/repo name across existing notes, and/or `synapse vault-search` on
|
|
100
100
|
`{"var": "frontmatter.task_id"}` to see which prefixes exist, then check whether any of those
|
|
101
101
|
prefixed notes reference this project. If exactly one prefix confidently matches, use it.
|
|
102
102
|
4. If nothing confidently matches (new project, or an ambiguous/multiple match), ask the user
|
|
@@ -105,9 +105,9 @@ unchanged for anyone who has never touched an XDG config directory.
|
|
|
105
105
|
5. Whenever step 3 or step 4 resolves a pair not already in the conf file (including a fresh
|
|
106
106
|
`project-name=prefix` line matching what was just deduced or asked), append it — so the next task
|
|
107
107
|
for this project resolves from step 2 without a search or a question.
|
|
108
|
-
6. Once the prefix is known, find the next number: run
|
|
109
|
-
`
|
|
110
|
-
filter the returned
|
|
108
|
+
6. Once the prefix is known, find the next number: run `synapse vault-search --fields
|
|
109
|
+
frontmatter.task_id` with `{"var": "frontmatter.task_id"}` on stdin,
|
|
110
|
+
filter the returned values client-side for ones matching
|
|
111
111
|
`{prefix}-\d+`, take the highest number found, add 1. If none exist yet
|
|
112
112
|
for that prefix, start at 1.
|
|
113
113
|
7. Format the new task ID **zero-padded to 3 digits** (`{prefix}-001`, `{prefix}-030`,
|
|
@@ -197,7 +197,7 @@ or supplied directly by a caller like the synapse-task-note skill):
|
|
|
197
197
|
## Notes
|
|
198
198
|
|
|
199
199
|
```
|
|
200
|
-
4. Write it with `
|
|
200
|
+
4. Write it with `synapse vault-write <path>` (content on stdin). Task mode: path
|
|
201
201
|
`tasks/{project}/{filename}.md` (project resolved in "Resolving the
|
|
202
202
|
project folder" above). Bare mode: path `{category}/{filename}.md`
|
|
203
203
|
(category resolved above).
|
|
@@ -85,7 +85,7 @@ this skill when it does. What no longer happens is arriving here merely because
|
|
|
85
85
|
because it is not this checkout's namespace to diff at all. Never conflate the two: a non-ancestor
|
|
86
86
|
baseline on a branch-identity match still proceeds normally, per the "One mechanical fact about
|
|
87
87
|
branches" section above.
|
|
88
|
-
- The work directory (`$SYNAPSE_WORK_DIR`, default `~/.
|
|
88
|
+
- The work directory (`$SYNAPSE_WORK_DIR`, default `~/.cache/synapse/work/{repo}@{branch}/`) ideally
|
|
89
89
|
holds the `manifest.tsv` from the original build. Without it, new paths cannot be classified as
|
|
90
90
|
auto-claimable, and clustering decisions have to be re-derived — say so rather than proceeding as if
|
|
91
91
|
nothing were missing. `synapse/{repo}@{branch}/_manifest.tsv` is the fallback copy.
|