Skip to content

Commit 3bbafb9

Browse files
phernandezclaude
andcommitted
feat(plugins): make setup focus load-bearing, seed schemas via metadata, add smoke test
- Focus question is now load-bearing: it drives an adaptive light folder-structure suggestion and is stored. The placement step branches learn (existing project with notes) vs suggest (new/empty project) instead of only learning. - Seed schema notes via the write_note `metadata` param instead of content frontmatter. The cloud write path silently coerces nested YAML to the string '[object Object]', corrupting schema/settings (basic-memory-cloud#1000); the metadata param round-trips correctly on both local and cloud. - Surface placementConventions + captureFolder in the SessionStart brief so the output style's "follow stored placement conventions" reflex has something to follow — previously written by setup but never shown to Claude (dead config). - Add a post-settings smoke test (run the hook's recall query, confirm routing) and a restart prompt gated on outputStyle in the close. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent 4b6de80 commit 3bbafb9

2 files changed

Lines changed: 89 additions & 22 deletions

File tree

plugins/claude-code/hooks/session-start.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ default_prompt = (
109109
"Cite permalinks when referencing prior work."
110110
)
111111
recall_prompt = cfg.get("recallPrompt") or default_prompt
112+
# Placement guidance — surfaced in the brief below so the output style's "follow the
113+
# project's stored placement conventions" reflex has something concrete to follow.
114+
# Without this, setup writes them but they never reach Claude (dead config).
115+
placement_conventions = (cfg.get("placementConventions") or "").strip()
116+
capture_folder = (cfg.get("captureFolder") or "sessions").strip()
112117
113118
# --- Resolve the shared/team read set ---
114119
# secondaryProjects (read-only recall sources) + teamProjects keys (share targets,
@@ -246,6 +251,19 @@ if shared_sections:
246251
if shared_capped:
247252
lines += ["", f"_(reading the first {MAX_SHARED} shared projects; more are configured.)_"]
248253
254+
# --- Where to write (placement guidance) ---
255+
# Trigger: a primaryProject is set (so capture is actually active — pre-compact and
256+
# proactive writes land somewhere intentional). Why: the output style tells Claude to
257+
# follow the project's placement conventions, but nothing else surfaces them.
258+
# Outcome: Claude sees the capture folder + any stored conventions, so writes land
259+
# where the user expects instead of being guesswork. Bounded — conventions are a
260+
# short string by design.
261+
if primary_project:
262+
placement = ["", "## Where to write", f"- Auto-capture and checkpoints go to `{capture_folder}/`."]
263+
if placement_conventions:
264+
placement.append(f"- Placement conventions: {placement_conventions}")
265+
lines += placement
266+
249267
# --- First-run / config nudges ---
250268
if not configured:
251269
lines += ["", setup_nudge]

plugins/claude-code/skills/setup/SKILL.md

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: setup
3-
description: Set up the Basic Memory plugin for this project — a short guided interview that configures the project mapping, seeds note schemas, optionally learns the project's conventions, and enables capture reflexes. Use when the user runs /basic-memory:setup, says "set up basic memory", or asks to configure/bootstrap the plugin.
3+
description: Set up the Basic Memory plugin for this project — a short guided interview that configures the project mapping, seeds note schemas, learns or suggests placement conventions, and enables capture reflexes. Use when the user runs /basic-memory:setup, says "set up basic memory", or asks to configure/bootstrap the plugin.
44
argument-hint: (no arguments — runs an interactive interview)
55
---
66

@@ -32,8 +32,13 @@ it succeeds.
3232

3333
Ask only what you can't infer. Cover:
3434

35-
1. **Focus.** "What's this project mostly about — code, writing, research, planning,
36-
or a mix?" (Shapes folder suggestions later; one quick question.)
35+
1. **Focus / how you'll use it.** "What will this project mostly be — code/dev,
36+
research, writing, knowledge capture, planning, or a mix?" This answer is
37+
**load-bearing**, not small talk: it drives the folder structure you suggest
38+
(step 4) and is stored so the SessionStart brief can surface it, keeping capture
39+
matched to the use-case. Don't let it evaporate — if you infer it from context
40+
instead of asking, still say the use-case you assumed and the structure it
41+
implies, and let the user correct it in one word.
3742

3843
2. **Project mapping.** "Do you already have a Basic Memory project for this, or
3944
should I create one?"
@@ -60,14 +65,23 @@ Ask only what you can't infer. Cover:
6065
Keep `primaryProject` a project the user owns for their *own* capture; team
6166
projects are for reading and deliberate sharing only.
6267

63-
4. **Learn conventions** (optional). "Want me to look at your existing notes and
64-
note your conventions so I place new notes consistently?" If yes, inspect the
65-
project: `list_directory` for the folder layout, sample a few notes per folder
66-
(and, where a folder holds recurring typed notes, you may run `schema_infer` to
67-
see their shape). Summarize what you find — folder-by-topic conventions, naming
68-
style, the observation categories they favor — into 3-6 short lines and store
69-
that string as `placementConventions`. Infer from their *real* notes; don't
70-
impose a structure.
68+
4. **Placement — learn or suggest** (depends on the project's state). The goal is a
69+
short `placementConventions` string (3-6 lines) telling you where new notes go.
70+
How you get it depends on whether the project already has notes:
71+
- **Existing project with notes***learn*. Inspect it: `list_directory` for the
72+
folder layout, sample a few notes per folder (and, where a folder holds
73+
recurring typed notes, you may run `schema_infer` to see their shape).
74+
Summarize the *real* conventions — folder-by-topic layout, naming style, the
75+
observation categories they favor. Infer from their actual notes; don't impose.
76+
- **New or empty project***suggest* (there's nothing to learn yet). Propose a
77+
**light** structure that fits the focus from step 1 — 3-5 optional top-level
78+
folders, no deep taxonomy — and be explicit that it's a starting point, not a
79+
scaffold: notes work fine without it and structure can stay emergent. Don't
80+
create empty folders; folders appear as notes land in them. Let the user edit
81+
or decline in one word.
82+
Either way, keep it short and store the result as `placementConventions`. The
83+
SessionStart brief surfaces it (alongside `captureFolder`), so this is what makes
84+
your captures land where the user expects — without it, placement is guesswork.
7185

7286
5. **Schemas.** "I'll add schemas for session checkpoints, decisions, and tasks so
7387
I can find them precisely later — okay?" (See "Seed the schemas" below.)
@@ -99,12 +113,21 @@ For each one:
99113
(`search_notes` with `metadata_filters={"type": "schema"}`, or try
100114
`read_note("schemas/<name>")`). **If it exists, skip it** — never overwrite a
101115
schema the user may have customized.
102-
- Otherwise write it with `write_note`: `directory="schemas"`,
103-
`title` = the schema's title (Session / Decision / Task), `content` = the file's
104-
full contents (including its `---` frontmatter — Basic Memory merges that into the
105-
note's frontmatter, so the `type: schema` + `entity` + `schema` definition land
106-
intact and become resolvable by `schema_validate`), routed to `primaryProject`
107-
(pass it as `project`, or as `project_id` if it's an `external_id` UUID).
116+
- Otherwise write it with `write_note`, routed to `primaryProject` (pass it as
117+
`project`, or as `project_id` if it's an `external_id` UUID):
118+
- `directory="schemas"`, `note_type="schema"`, `title` = the schema's title
119+
(Session / Decision / Task).
120+
- `content` = the markdown **body only** — everything *after* the `---`
121+
frontmatter block (the `# Session` heading and the prose).
122+
- `metadata` = the schema's structured frontmatter as a **nested dict**: `entity`,
123+
`version`, the full `schema` map, and `settings` (keep its nested `frontmatter`,
124+
and pass enum values as JSON arrays, e.g. `["open","resumed","closed"]`).
125+
- **Do not** put the schema's `---` frontmatter inside `content`. On the cloud
126+
write path that nested YAML is silently coerced to the string `'[object Object]'`
127+
(basic-memory-cloud#1000), corrupting `schema`/`settings`. The `metadata` param
128+
round-trips correctly on both local and cloud. After seeding, verify one note
129+
with `read_note(..., output_format="json", include_frontmatter=true)`
130+
`schema`/`settings` must come back as nested objects, not strings.
108131

109132
### 2. Install the shared skills (if the user opted in)
110133
Run, from the project root:
@@ -130,7 +153,7 @@ Build the `basicMemory` block from the interview:
130153
"rememberFolder": "bm-remember",
131154
"recallTimeframe": "3d",
132155
"preCompactCapture": "extractive",
133-
"placementConventions": "<inferred summary, or null>",
156+
"placementConventions": "<learned or suggested summary, or null>",
134157
"teamProjects": {}
135158
},
136159
"outputStyle": "basic-memory"
@@ -145,10 +168,36 @@ valid JSON.
145168
Writing the `basicMemory` block is also what stops the SessionStart hook's first-run
146169
nudge — the config's presence is the signal that setup has run.
147170

171+
### 4. Smoke-test the wiring
172+
Before you close, prove recall actually resolves — this catches a misnamed project,
173+
missing cloud credentials, or a ref that doesn't route, while the user is still here
174+
to fix it. Run the same structured query the SessionStart hook runs, via the CLI it
175+
uses (`basic-memory` / `bm` / `uvx basic-memory`):
176+
177+
- **Primary:** `… tool search-notes --type schema --page-size 5` against
178+
`primaryProject` — use `--project-id <uuid>` for a UUID, `--project <ref>`
179+
otherwise. It should return the three schemas you just seeded.
180+
- **One shared project** (only if `secondaryProjects` is non-empty): a
181+
`--type decision --status open` query against the first ref. It just needs to
182+
return *cleanly*`0 results` is fine; an **error** means the ref doesn't route.
183+
184+
If a query errors, or the primary returns nothing, surface it and fix the project
185+
ref before closing — don't let the next session's brief come up empty.
186+
148187
## Close
149188

150189
Confirm what you did in a few lines: the project mapping, which schemas were seeded
151-
vs. already present, whether conventions were learned, and whether the output style
152-
is on. End with: *"Done — I'll use this from the next message. Run
153-
`/basic-memory:status` anytime to see what I'm tracking."* Note that the output
154-
style (if enabled) takes effect on the next session, since it's fixed at startup.
190+
vs. already present, whether placement was learned or suggested, the smoke-test
191+
result, and whether the output style is on.
192+
193+
Then handle activation based on the output style:
194+
- **Output style enabled** → it's fixed at session start, so the full capture
195+
reflexes only take effect next session. Prompt the user to **restart the session**
196+
(start a new Claude Code session) to activate them. Be precise so it doesn't read
197+
as "nothing works yet": recall is already live this session (the SessionStart
198+
hook's prompt ran), and the PreCompact checkpoint works now too — only the
199+
proactive-capture reflexes wait for the restart.
200+
- **Output style off** → no restart needed; the hooks already run.
201+
202+
End with: *"Done — I'll use this from the next message. Run `/basic-memory:status`
203+
anytime to see what I'm tracking."*

0 commit comments

Comments
 (0)