@basicmemory/openclaw-basic-memory 0.24.1 → 1.0.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/README.md +24 -12
- package/bm-client.ts +302 -112
- package/commands/cli.ts +5 -6
- package/commands/slash.ts +4 -4
- package/context-engine/basic-memory-context-engine.ts +74 -9
- package/context-engine/types.ts +21 -0
- package/dist/bm-client.d.ts +61 -12
- package/dist/bm-client.js +250 -101
- package/dist/commands/cli.d.ts +1 -1
- package/dist/commands/cli.js +5 -5
- package/dist/commands/slash.d.ts +1 -1
- package/dist/commands/slash.js +4 -3
- package/dist/context-engine/basic-memory-context-engine.d.ts +20 -1
- package/dist/context-engine/basic-memory-context-engine.js +50 -2
- package/dist/context-engine/types.d.ts +9 -0
- package/dist/context-engine/types.js +1 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +79 -69
- package/dist/memory/basic-memory-capability.d.ts +19 -0
- package/dist/memory/basic-memory-capability.js +190 -0
- package/dist/tools/build-context.d.ts +1 -1
- package/dist/tools/build-context.js +12 -4
- package/dist/tools/delete-note.d.ts +1 -1
- package/dist/tools/delete-note.js +2 -1
- package/dist/tools/edit-note.d.ts +1 -1
- package/dist/tools/edit-note.js +22 -12
- package/dist/tools/list-memory-projects.d.ts +1 -1
- package/dist/tools/list-memory-projects.js +2 -1
- package/dist/tools/list-workspaces.d.ts +1 -1
- package/dist/tools/list-workspaces.js +4 -4
- package/dist/tools/move-note.d.ts +1 -1
- package/dist/tools/move-note.js +2 -1
- package/dist/tools/read-note.d.ts +1 -1
- package/dist/tools/read-note.js +2 -1
- package/dist/tools/schema-diff.d.ts +1 -1
- package/dist/tools/schema-diff.js +2 -1
- package/dist/tools/schema-infer.d.ts +1 -1
- package/dist/tools/schema-infer.js +2 -1
- package/dist/tools/schema-validate.d.ts +1 -1
- package/dist/tools/schema-validate.js +2 -1
- package/dist/tools/search-notes.d.ts +1 -1
- package/dist/tools/search-notes.js +13 -6
- package/dist/tools/write-note.d.ts +1 -1
- package/dist/tools/write-note.js +11 -12
- package/index.ts +93 -84
- package/memory/basic-memory-capability.ts +253 -0
- package/openclaw.plugin.json +0 -2
- package/package.json +6 -5
- package/skills/manifest.json +5 -0
- package/skills/memory-agents/SKILL.md +23 -10
- package/skills/memory-capture/SKILL.md +29 -18
- package/skills/memory-comark/SKILL.md +17 -6
- package/skills/memory-continue/SKILL.md +47 -27
- package/skills/memory-curate/SKILL.md +40 -24
- package/skills/memory-defrag/SKILL.md +18 -7
- package/skills/memory-doctor/SKILL.md +93 -0
- package/skills/memory-ingest/SKILL.md +33 -17
- package/skills/memory-lifecycle/SKILL.md +35 -22
- package/skills/memory-literary-analysis/SKILL.md +112 -75
- package/skills/memory-metadata-search/SKILL.md +39 -28
- package/skills/memory-notes/SKILL.md +75 -50
- package/skills/memory-okf/SKILL.md +27 -15
- package/skills/memory-onboarding/SKILL.md +20 -9
- package/skills/memory-onboarding/references/assistant-setup.md +1 -1
- package/skills/memory-onboarding/references/conventions.md +4 -4
- package/skills/memory-onboarding/references/domain-playbooks.md +1 -1
- package/skills/memory-onboarding/references/schema-guide.md +9 -8
- package/skills/memory-quest/SKILL.md +18 -7
- package/skills/memory-reflect/SKILL.md +25 -13
- package/skills/memory-research/SKILL.md +28 -13
- package/skills/memory-schema/SKILL.md +36 -24
- package/skills/memory-tasks/SKILL.md +34 -22
- package/tools/build-context.ts +14 -5
- package/tools/delete-note.ts +2 -2
- package/tools/edit-note.ts +27 -14
- package/tools/list-memory-projects.ts +6 -2
- package/tools/list-workspaces.ts +8 -5
- package/tools/move-note.ts +2 -2
- package/tools/read-note.ts +2 -2
- package/tools/schema-diff.ts +2 -2
- package/tools/schema-infer.ts +2 -2
- package/tools/schema-validate.ts +2 -2
- package/tools/search-notes.ts +18 -7
- package/tools/write-note.ts +12 -12
- package/dist/tools/memory-provider.d.ts +0 -15
- package/dist/tools/memory-provider.js +0 -275
- package/tools/memory-provider.ts +0 -336
|
@@ -7,6 +7,17 @@ description: "Analyze a complete literary work into a structured Basic Memory kn
|
|
|
7
7
|
|
|
8
8
|
Transform a complete literary work into a structured knowledge graph. Characters, themes, chapters, locations, symbols, and literary devices become interconnected notes — searchable, validatable, and traversable.
|
|
9
9
|
|
|
10
|
+
## Project scope
|
|
11
|
+
|
|
12
|
+
If the project is unknown, discover it with `bm_project(command="list")`.
|
|
13
|
+
Pass a nonempty `project` on every note, folder, context and schema call, even
|
|
14
|
+
with one project or a qualified `memory://` URL. Use the chosen project's name,
|
|
15
|
+
`workspace/project`, or external ID; `<work-name>` below is a placeholder for it.
|
|
16
|
+
Recent activity also requires `project`. Search accepts `project` for one
|
|
17
|
+
project, `projects` for selected projects, or `all_projects=True` explicitly.
|
|
18
|
+
Discovery and bundled manuals need no project. Keep the same scope when following
|
|
19
|
+
returned paths, paginating, or recovering from a refusal.
|
|
20
|
+
|
|
10
21
|
## When to Use
|
|
11
22
|
|
|
12
23
|
- Analyzing a novel, play, poem, or non-fiction book end-to-end
|
|
@@ -28,10 +39,9 @@ Phase 5: Explore → traverse the graph, write synthesis notes
|
|
|
28
39
|
|
|
29
40
|
## Tools
|
|
30
41
|
|
|
31
|
-
Writing
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
available):
|
|
42
|
+
Writing goes through `bm_write` and `bm_edit`. MCP reads use `bm_read`, `bm_find`,
|
|
43
|
+
`bm_search`, and `bm_list`. The CLI keeps its POSIX verbs; the table and shell
|
|
44
|
+
examples below are CLI commands (prefix each verb with `bm`):
|
|
35
45
|
|
|
36
46
|
| Need | Use | Instead of |
|
|
37
47
|
|------|-----|-----------|
|
|
@@ -55,7 +65,7 @@ answer, exit 0, no warning — so learn it here rather than from a graph you tho
|
|
|
55
65
|
audited:
|
|
56
66
|
|
|
57
67
|
- **`find` pages, and the default page is 10.** Any query whose answer is "all N chapters"
|
|
58
|
-
needs `--page-size
|
|
68
|
+
needs `--page-size 100` (the maximum) and paging — see [Coverage Checks](#coverage-checks).
|
|
59
69
|
- **`--name` cannot combine with `--meta`.** The metadata search has no filename glob. Scope a
|
|
60
70
|
`--meta` query with the positional path instead: `find /characters --meta
|
|
61
71
|
'note_type=character'`. That path is matched on a directory boundary against the *file path*
|
|
@@ -64,15 +74,19 @@ audited:
|
|
|
64
74
|
`/characters` reaches everything filed under `characters/` (including `characters/major/`),
|
|
65
75
|
and never `characters-cut/`.
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
`
|
|
77
|
+
For MCP, use `bm_read(identifier="<note>", section="Observations", project="<work-name>")` or
|
|
78
|
+
`start_line`/`end_line` for slices. Use `bm_find(filters={"type": "chapter"}, fields=["chapter_number", "pov"], project="<work-name>")`
|
|
79
|
+
for metadata and `bm_search(query="doubloon", lines=2, project="<work-name>")`
|
|
80
|
+
for matching lines. `bm_list` lists one folder level; `bm_find` recurses.
|
|
81
|
+
MCP list/find/search pages cap at 100; follow `has_more` through every page.
|
|
82
|
+
CLI page sizes and flags below are separate from the MCP shapes.
|
|
69
83
|
|
|
70
84
|
## Phase 0: Setup
|
|
71
85
|
|
|
72
86
|
### Create the Project
|
|
73
87
|
|
|
74
88
|
```python
|
|
75
|
-
|
|
89
|
+
bm_project(command="create", name="<work-name>", path="~/basic-memory/<work-name>")
|
|
76
90
|
```
|
|
77
91
|
|
|
78
92
|
Use a kebab-case slug of the work's title (e.g., `great-gatsby`, `hamlet`, `beloved`).
|
|
@@ -84,7 +98,7 @@ Write 6 schema notes to `schema/`. Each schema defines the entity type's fields,
|
|
|
84
98
|
#### Character Schema
|
|
85
99
|
|
|
86
100
|
```python
|
|
87
|
-
|
|
101
|
+
bm_write(
|
|
88
102
|
title="Character",
|
|
89
103
|
directory="schema",
|
|
90
104
|
note_type="schema",
|
|
@@ -106,7 +120,8 @@ Schema for character entity notes.
|
|
|
106
120
|
## Observations
|
|
107
121
|
- [convention] Major characters in characters/major/, minor in characters/minor/
|
|
108
122
|
- [convention] Observation categories: trait, motivation, arc, quote, appearance, relationship, symbolism, fate
|
|
109
|
-
- [convention] Relations: appears_in, contrasts_with, allied_with, commands, symbolizes, associated_with"""
|
|
123
|
+
- [convention] Relations: appears_in, contrasts_with, allied_with, commands, symbolizes, associated_with""",
|
|
124
|
+
project="<work-name>",
|
|
110
125
|
)
|
|
111
126
|
```
|
|
112
127
|
|
|
@@ -115,7 +130,7 @@ Add work-specific fields as needed — e.g., `rank` for military fiction, `house
|
|
|
115
130
|
#### Theme Schema
|
|
116
131
|
|
|
117
132
|
```python
|
|
118
|
-
|
|
133
|
+
bm_write(
|
|
119
134
|
title="Theme",
|
|
120
135
|
directory="schema",
|
|
121
136
|
note_type="schema",
|
|
@@ -135,14 +150,15 @@ Schema for thematic analysis notes.
|
|
|
135
150
|
|
|
136
151
|
## Observations
|
|
137
152
|
- [convention] Observation categories: definition, manifestation, evolution, counterpoint, quote, interpretation
|
|
138
|
-
- [convention] Relations: embodied_by, contrasts_with, reinforced_by, explored_in, expressed_through"""
|
|
153
|
+
- [convention] Relations: embodied_by, contrasts_with, reinforced_by, explored_in, expressed_through""",
|
|
154
|
+
project="<work-name>",
|
|
139
155
|
)
|
|
140
156
|
```
|
|
141
157
|
|
|
142
158
|
#### Chapter Schema
|
|
143
159
|
|
|
144
160
|
```python
|
|
145
|
-
|
|
161
|
+
bm_write(
|
|
146
162
|
title="Chapter",
|
|
147
163
|
directory="schema",
|
|
148
164
|
note_type="schema",
|
|
@@ -164,14 +180,15 @@ Schema for chapter-level analysis notes.
|
|
|
164
180
|
## Observations
|
|
165
181
|
- [convention] Chapters stored in chapters/ directory
|
|
166
182
|
- [convention] Observation categories: summary, event, tone, technique, quote, significance, foreshadowing
|
|
167
|
-
- [convention] Relations: features, set_in, explores, contains, employs, follows, precedes, parallels"""
|
|
183
|
+
- [convention] Relations: features, set_in, explores, contains, employs, follows, precedes, parallels""",
|
|
184
|
+
project="<work-name>",
|
|
168
185
|
)
|
|
169
186
|
```
|
|
170
187
|
|
|
171
188
|
#### Location Schema
|
|
172
189
|
|
|
173
190
|
```python
|
|
174
|
-
|
|
191
|
+
bm_write(
|
|
175
192
|
title="Location",
|
|
176
193
|
directory="schema",
|
|
177
194
|
note_type="schema",
|
|
@@ -191,14 +208,15 @@ Schema for location and setting notes.
|
|
|
191
208
|
|
|
192
209
|
## Observations
|
|
193
210
|
- [convention] Observation categories: description, atmosphere, symbolism, significance, geography
|
|
194
|
-
- [convention] Relations: setting_for, associated_with, symbolizes, contains, part_of"""
|
|
211
|
+
- [convention] Relations: setting_for, associated_with, symbolizes, contains, part_of""",
|
|
212
|
+
project="<work-name>",
|
|
195
213
|
)
|
|
196
214
|
```
|
|
197
215
|
|
|
198
216
|
#### Symbol Schema
|
|
199
217
|
|
|
200
218
|
```python
|
|
201
|
-
|
|
219
|
+
bm_write(
|
|
202
220
|
title="Symbol",
|
|
203
221
|
directory="schema",
|
|
204
222
|
note_type="schema",
|
|
@@ -218,14 +236,15 @@ Schema for symbolic element notes.
|
|
|
218
236
|
|
|
219
237
|
## Observations
|
|
220
238
|
- [convention] Observation categories: meaning, appearance, ambiguity, interpretation, quote, evolution
|
|
221
|
-
- [convention] Relations: represents, associated_with, appears_in, contrasts_with, located_at"""
|
|
239
|
+
- [convention] Relations: represents, associated_with, appears_in, contrasts_with, located_at""",
|
|
240
|
+
project="<work-name>",
|
|
222
241
|
)
|
|
223
242
|
```
|
|
224
243
|
|
|
225
244
|
#### LiteraryDevice Schema
|
|
226
245
|
|
|
227
246
|
```python
|
|
228
|
-
|
|
247
|
+
bm_write(
|
|
229
248
|
title="LiteraryDevice",
|
|
230
249
|
directory="schema",
|
|
231
250
|
note_type="schema",
|
|
@@ -245,7 +264,8 @@ Schema for literary technique and device notes.
|
|
|
245
264
|
|
|
246
265
|
## Observations
|
|
247
266
|
- [convention] Observation categories: definition, usage, effect, example, significance
|
|
248
|
-
- [convention] Relations: used_in, characterizes, expresses, related_to"""
|
|
267
|
+
- [convention] Relations: used_in, characterizes, expresses, related_to""",
|
|
268
|
+
project="<work-name>",
|
|
249
269
|
)
|
|
250
270
|
```
|
|
251
271
|
|
|
@@ -276,7 +296,7 @@ Before processing chapters, create stub notes for major entities so `[[wiki-link
|
|
|
276
296
|
For each major character, create a stub with known metadata:
|
|
277
297
|
|
|
278
298
|
```python
|
|
279
|
-
|
|
299
|
+
bm_write(
|
|
280
300
|
title="<Character Name>",
|
|
281
301
|
directory="characters/major",
|
|
282
302
|
note_type="Character",
|
|
@@ -290,7 +310,8 @@ write_note(
|
|
|
290
310
|
|
|
291
311
|
## Relations
|
|
292
312
|
- associated_with [[<Related Character>]]
|
|
293
|
-
- appears_in [[<Key Location>]]"""
|
|
313
|
+
- appears_in [[<Key Location>]]""",
|
|
314
|
+
project="<work-name>",
|
|
294
315
|
)
|
|
295
316
|
```
|
|
296
317
|
|
|
@@ -340,14 +361,14 @@ re-reading the whole book into context:
|
|
|
340
361
|
|
|
341
362
|
```bash
|
|
342
363
|
grep -n '^CHAPTER ' ~/basic-memory/moby-dick/moby-dick.txt # heading -> line number
|
|
343
|
-
bm cat moby-dick
|
|
364
|
+
bm cat moby-dick.txt --lines 4200-4890 --project moby-dick # one chapter, not the whole text
|
|
344
365
|
```
|
|
345
366
|
|
|
346
367
|
`grep -n` here is the shell's grep on a filesystem path (this is the map-building step, and
|
|
347
|
-
it needs the file). `bm cat` then takes the *note identifier* and returns exactly that slice
|
|
348
|
-
`
|
|
349
|
-
|
|
350
|
-
|
|
368
|
+
it needs the file). `bm cat` then takes the *note identifier* and returns exactly that slice,
|
|
369
|
+
numbered, under a `Lines 4200-4890 of N` header. Pass `--project <work>` so it reads from the
|
|
370
|
+
right project. `bm head moby-dick.txt -n 40 --project moby-dick` is the cheap way to eyeball
|
|
371
|
+
the heading format before writing the grep pattern.
|
|
351
372
|
|
|
352
373
|
Store the map in the project (a note or a small JSON file) so later batches — and a resumed
|
|
353
374
|
run after context compaction — do not have to rediscover it. On a long work this is the
|
|
@@ -378,7 +399,7 @@ never work from memory or a summary; textual evidence is the entire point.
|
|
|
378
399
|
**2. Create the chapter note:**
|
|
379
400
|
|
|
380
401
|
```python
|
|
381
|
-
|
|
402
|
+
bm_write(
|
|
382
403
|
title="Chapter <N> - <Title>",
|
|
383
404
|
directory="chapters",
|
|
384
405
|
note_type="Chapter",
|
|
@@ -407,19 +428,21 @@ write_note(
|
|
|
407
428
|
- contains [[<Symbol>]]
|
|
408
429
|
- employs [[<Literary Device>]]
|
|
409
430
|
- follows [[Chapter <N-1> - <Previous Title>]]
|
|
410
|
-
- precedes [[Chapter <N+1> - <Next Title>]]"""
|
|
431
|
+
- precedes [[Chapter <N+1> - <Next Title>]]""",
|
|
432
|
+
project="<work-name>",
|
|
411
433
|
)
|
|
412
434
|
```
|
|
413
435
|
|
|
414
436
|
**3. Enrich related entities:**
|
|
415
437
|
|
|
416
438
|
```python
|
|
417
|
-
|
|
439
|
+
bm_edit(
|
|
418
440
|
identifier="characters/major/<character-slug>",
|
|
419
|
-
|
|
441
|
+
command="insert_after_section",
|
|
420
442
|
section="Observations",
|
|
421
443
|
content="""- [arc] Ch.<N>: <What happens to this character>
|
|
422
|
-
- [quote] "<Attributed quote>" (Ch.<N>)"""
|
|
444
|
+
- [quote] "<Attributed quote>" (Ch.<N>)""",
|
|
445
|
+
project="<work-name>",
|
|
423
446
|
)
|
|
424
447
|
```
|
|
425
448
|
|
|
@@ -452,7 +475,7 @@ As each chapter is processed, append observations to relevant entities:
|
|
|
452
475
|
|
|
453
476
|
### Adding Prose and Interpretation
|
|
454
477
|
|
|
455
|
-
After the structured observations are in place, consider adding interpretive prose to major entity notes. Insert 2-4 paragraphs of critical essay before the Observations section with `
|
|
478
|
+
After the structured observations are in place, consider adding interpretive prose to major entity notes. Insert 2-4 paragraphs of critical essay before the Observations section with `bm_edit(command="insert_before_section", section="Observations", content=..., project="<work-name>")`. This prose should:
|
|
456
479
|
|
|
457
480
|
- Argue for a reading of the character, theme, or symbol — not just describe it
|
|
458
481
|
- Connect the entity to the work's larger concerns and to literary tradition
|
|
@@ -470,17 +493,17 @@ After all chapters are processed:
|
|
|
470
493
|
Do not re-read every note to decide what is thin. Query for it:
|
|
471
494
|
|
|
472
495
|
```bash
|
|
473
|
-
bm find --meta 'note_type=chapter' --fields chapter_number,pov,setting --page-size
|
|
474
|
-
bm find --meta 'note_type=character' --fields role,status --page-size
|
|
475
|
-
bm find --meta 'chapter_number>100' --fields pov --page-size
|
|
496
|
+
bm find --meta 'note_type=chapter' --fields chapter_number,pov,setting --page-size 100
|
|
497
|
+
bm find --meta 'note_type=character' --fields role,status --page-size 100 # who is still a stub
|
|
498
|
+
bm find --meta 'chapter_number>100' --fields pov --page-size 100 # late-book POV drift
|
|
476
499
|
```
|
|
477
500
|
|
|
478
|
-
A field a note never set comes back as
|
|
501
|
+
A field a note never set comes back as `None` (`null` under `--json`), so rows with
|
|
479
502
|
blanks are the work queue. This turns "audit the graph" from a read of every note into one
|
|
480
503
|
call per question.
|
|
481
504
|
|
|
482
|
-
`
|
|
483
|
-
type predicates are normalized the same way, so either spelling matches. `--page-size
|
|
505
|
+
`bm_write` snake-cases `note_type` (`Chapter` is stored as `chapter`), and `--meta`
|
|
506
|
+
type predicates are normalized the same way, so either spelling matches. `--page-size 100`
|
|
484
507
|
is not decoration: without it these return the first 10 rows and the work queue looks ten
|
|
485
508
|
items long.
|
|
486
509
|
|
|
@@ -497,7 +520,7 @@ Add `parallels` and `contrasts_with` relations between structurally similar chap
|
|
|
497
520
|
Create synthesis notes in `analysis/`:
|
|
498
521
|
|
|
499
522
|
```python
|
|
500
|
-
|
|
523
|
+
bm_write(
|
|
501
524
|
title="Narrative Structure",
|
|
502
525
|
directory="analysis",
|
|
503
526
|
note_type="note",
|
|
@@ -515,7 +538,8 @@ Analysis of the work's narrative architecture.
|
|
|
515
538
|
- analyzes [[<Protagonist>]]
|
|
516
539
|
- analyzes [[<Key Character>]]
|
|
517
540
|
- explores [[<Central Theme>]]
|
|
518
|
-
..."""
|
|
541
|
+
...""",
|
|
542
|
+
project="<work-name>",
|
|
519
543
|
)
|
|
520
544
|
```
|
|
521
545
|
|
|
@@ -533,23 +557,23 @@ During chapter processing, new minor characters, locations, and symbols will eme
|
|
|
533
557
|
|
|
534
558
|
```python
|
|
535
559
|
# Validate each entity type
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
560
|
+
bm_schema(command="validate", note_type="Character", project="<work-name>")
|
|
561
|
+
bm_schema(command="validate", note_type="Theme", project="<work-name>")
|
|
562
|
+
bm_schema(command="validate", note_type="Chapter", project="<work-name>")
|
|
563
|
+
bm_schema(command="validate", note_type="Location", project="<work-name>")
|
|
564
|
+
bm_schema(command="validate", note_type="Symbol", project="<work-name>")
|
|
565
|
+
bm_schema(command="validate", note_type="LiteraryDevice", project="<work-name>")
|
|
542
566
|
```
|
|
543
567
|
|
|
544
568
|
### Drift Detection
|
|
545
569
|
|
|
546
570
|
```python
|
|
547
|
-
|
|
571
|
+
bm_schema(command="diff", note_type="Character", project="<work-name>")
|
|
548
572
|
# ... for each type
|
|
549
573
|
```
|
|
550
574
|
|
|
551
575
|
Fix issues found — common fixes:
|
|
552
|
-
- Missing required observation categories → add them via `
|
|
576
|
+
- Missing required observation categories → add them via `bm_edit`
|
|
553
577
|
- Enum values outside allowed set → correct metadata
|
|
554
578
|
- Fields in notes but not schema → add as optional to schema if legitimate
|
|
555
579
|
|
|
@@ -558,14 +582,14 @@ Fix issues found — common fixes:
|
|
|
558
582
|
Schema validation proves notes match their shape. These prove the graph is *complete*:
|
|
559
583
|
|
|
560
584
|
```bash
|
|
561
|
-
bm find --meta 'note_type=chapter' --fields chapter_number --page-size
|
|
562
|
-
bm find --meta 'note_type=chapter' --fields pov,setting --page-size
|
|
563
|
-
bm find /characters --meta 'note_type=character' --fields role --page-size
|
|
585
|
+
bm find --meta 'note_type=chapter' --fields chapter_number --page-size 100 # every chapter present?
|
|
586
|
+
bm find --meta 'note_type=chapter' --fields pov,setting --page-size 100 # missing context?
|
|
587
|
+
bm find /characters --meta 'note_type=character' --fields role --page-size 100 # inventory vs. seed list
|
|
564
588
|
```
|
|
565
589
|
|
|
566
590
|
**A coverage check that pages is not a coverage check.** `bm find` defaults to
|
|
567
591
|
`--page-size 10`, so the un-sized form of the first query "proves" a 138-chapter work has 10
|
|
568
|
-
chapters.
|
|
592
|
+
chapters. 100 is the maximum page size; past that, iterate with `--page 2`, `--page 3`, … .
|
|
569
593
|
Scope a `--meta` query with the positional path (`/characters`), never `--name` — the two
|
|
570
594
|
options are mutually exclusive, because the metadata search has no filename glob. The
|
|
571
595
|
positional path scopes by the *file path* a note is indexed under, matched on a directory
|
|
@@ -573,12 +597,10 @@ boundary: `/characters` admits `characters/major/ahab.md` but never `characters-
|
|
|
573
597
|
not a permalink match, so a note that pins its own `permalink:` is still found where its file
|
|
574
598
|
lives.
|
|
575
599
|
|
|
576
|
-
Read the count off the
|
|
577
|
-
|
|
578
|
-
truncated the answer — that
|
|
579
|
-
|
|
580
|
-
carries `total` and `has_more`; with `--plain` the rows go to stdout and a has-more notice
|
|
581
|
-
goes to stderr. For scripts, read `has_more` from `--json`.
|
|
600
|
+
Read the count off the result, not off the rows you can see. The text result opens with the
|
|
601
|
+
total (`138 notes under '/' match:`) and ends with `More results available.` when the page
|
|
602
|
+
truncated the answer — that line appearing is the check *failing*, whatever the visible rows
|
|
603
|
+
say. For scripts, read `total` and `has_more` from `--json`.
|
|
582
604
|
|
|
583
605
|
For the sequence gap — the failure that a count alone cannot catch — take the numbers from
|
|
584
606
|
`--json`, which carries `total`, `total_is_exact`, and `has_more`:
|
|
@@ -587,7 +609,7 @@ For the sequence gap — the failure that a count alone cannot catch — take th
|
|
|
587
609
|
expected=138; page=1; rows='[]'
|
|
588
610
|
while :; do
|
|
589
611
|
resp=$(bm find --meta 'note_type=chapter' --fields chapter_number \
|
|
590
|
-
--page-size
|
|
612
|
+
--page-size 100 --page $page --project <work> --json)
|
|
591
613
|
rows=$(jq -n --argjson acc "$rows" --argjson r "$resp" \
|
|
592
614
|
'$acc + [$r.results[] | {title, n: .fields.chapter_number}]')
|
|
593
615
|
[ "$(jq -r '.has_more' <<<"$resp")" = "true" ] || break
|
|
@@ -603,9 +625,9 @@ jq -n --argjson rows "$rows" --argjson expected "$expected" '
|
|
|
603
625
|
out_of_range: ($n | map(select(. < 1 or . > $expected)) | unique) }'
|
|
604
626
|
```
|
|
605
627
|
|
|
606
|
-
The loop is not ceremony. `--page-size` caps at
|
|
607
|
-
with more than
|
|
608
|
-
than accumulating them, which reports chapters 1-
|
|
628
|
+
The loop is not ceremony. `--page-size` caps at 100, so a single call cannot inventory a work
|
|
629
|
+
with more than 100 chapters — and rerunning it with `--page 2` *replaces* the numbers rather
|
|
630
|
+
than accumulating them, which reports chapters 1-100 as missing on a corpus that is complete.
|
|
609
631
|
Walk until `has_more` is false and check the union.
|
|
610
632
|
|
|
611
633
|
`--fields` returns YAML-native types, so `chapter_number: 63` comes back as `63`. The
|
|
@@ -643,7 +665,7 @@ bm orphans # entities with no relations in the graph
|
|
|
643
665
|
```
|
|
644
666
|
|
|
645
667
|
`orphans` finds notes with no relations. It does not find a `[[target]]` that resolves to
|
|
646
|
-
nothing — a misspelled or renamed entity name. Check a chapter's links with `bm tool
|
|
668
|
+
nothing — a misspelled or renamed entity name. Check a chapter's links with `bm tool context memory://chapters/<slug> --depth 1
|
|
647
669
|
--project <work> --json` and look for relations whose `to_entity_id` is `null`; fix the
|
|
648
670
|
spelling or add the alias, then re-run the chapter.
|
|
649
671
|
|
|
@@ -655,18 +677,32 @@ orphans behind has made the graph worse.
|
|
|
655
677
|
With the graph complete, traverse it to find what the chapter-by-chapter pass could not see:
|
|
656
678
|
|
|
657
679
|
```bash
|
|
658
|
-
bm
|
|
659
|
-
bm find --meta 'note_type=theme' --fields prevalence --page-size 200 # thematic weight
|
|
680
|
+
bm find --meta 'note_type=theme' --fields prevalence --page-size 100 # thematic weight
|
|
660
681
|
bm grep "doubloon" --page-size 100 --project <work> # every mention of a symbol
|
|
661
682
|
```
|
|
662
683
|
|
|
663
|
-
"Which characters share the most chapters" is
|
|
664
|
-
row's `content` for `features [[...]]` — one call instead of a read
|
|
665
|
-
|
|
684
|
+
"Which characters share the most chapters" is a search for `features [[` plus a local parse
|
|
685
|
+
of each row's `content` for `features [[...]]` — one call per 100 chapters instead of a read
|
|
686
|
+
per chapter. Page until `has_more` is false, as the coverage check does; a 138-chapter work
|
|
687
|
+
needs two pages:
|
|
688
|
+
|
|
689
|
+
```bash
|
|
690
|
+
page=1; rows='[]'
|
|
691
|
+
while :; do
|
|
692
|
+
resp=$(bm grep "features [[" --page-size 100 --page $page --project <work> --json)
|
|
693
|
+
rows=$(jq -n --argjson acc "$rows" --argjson r "$resp" '$acc + $r.results')
|
|
694
|
+
[ "$(jq -r '.has_more' <<<"$resp")" = "true" ] || break
|
|
695
|
+
page=$((page + 1))
|
|
696
|
+
done
|
|
697
|
+
jq 'length' <<<"$rows" # one row per chapter that lists its cast
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Do not
|
|
701
|
+
reach for `bm tool context 'memory://characters/major/*'` here: a
|
|
666
702
|
wildcard context is capped at 100 related rows across all primaries and returns one primary
|
|
667
703
|
row per indexed observation, so it neither enumerates the cast nor walks the web.
|
|
668
|
-
`
|
|
669
|
-
`
|
|
704
|
+
`context` on a *single* note is the right tool for a different question, below.
|
|
705
|
+
`context` takes its URL as a positional argument — there is no `--url` option.
|
|
670
706
|
|
|
671
707
|
`grep` matches keywords and returns a page of 10, which quietly truncates "where does this
|
|
672
708
|
appear?" — a symbol in 40 chapters comes back as 10. For symbol tracing, raise
|
|
@@ -675,14 +711,15 @@ the default page would have dropped. `--semantic` ranks by meaning instead, whic
|
|
|
675
711
|
"what is this about?" rather than "where does this word appear?".
|
|
676
712
|
|
|
677
713
|
Two more facts about `grep` rows. Matching is case-insensitive and note-level: a hit is a
|
|
678
|
-
note, not a line,
|
|
714
|
+
note, not a line, unless you pass `-C N` for matching lines with context. And each row's
|
|
715
|
+
`content` is capped at
|
|
679
716
|
4000 characters; `content_truncated: true` marks a cut row, so a long note's tail (such as a
|
|
680
717
|
final chapter's `follows`/`precedes` relations) may be missing from a grep-driven scan.
|
|
681
718
|
When a parse depends on the end of a note, `cat` that note.
|
|
682
719
|
|
|
683
720
|
`--page-size` raises the ceiling, it does not remove it. A symbol in a long work can exceed
|
|
684
|
-
even 100, so
|
|
685
|
-
|
|
721
|
+
even 100, so walk `--page 2`, `--page 3` until `has_more` is false, as in the cast loop
|
|
722
|
+
above. A truncated symbol search fails the same silent way as an unpaginated `find`: a plausible
|
|
686
723
|
answer, exit 0, and no sign that the tail is missing.
|
|
687
724
|
|
|
688
725
|
And `grep` searches **your notes, not the source**. The `<work>.txt` is indexed as an entity,
|
|
@@ -738,7 +775,7 @@ This pipeline works for any literary text. Adjust schemas for genre:
|
|
|
738
775
|
- **Relations are your web.** Every chapter should link to characters, themes, locations, and devices. Every entity should link back to chapters where it appears.
|
|
739
776
|
- **Enrich iteratively.** Entity notes grow richer with each chapter. Don't try to write the perfect character note upfront — append as you go.
|
|
740
777
|
- **Add prose for depth.** After structured data is in place, add interpretive essays to major notes. The prose captures what observations cannot: argument, nuance, opinion, and voice.
|
|
741
|
-
- **Validate periodically.** Run `
|
|
778
|
+
- **Validate periodically.** Run `bm_schema(command="validate", project="<work-name>")` after each batch, not just at the end. Catch drift early.
|
|
742
779
|
- **Quote generously.** Literary analysis lives on textual evidence. Include significant quotes as `[quote]` observations with chapter attribution.
|
|
743
780
|
- **Review and revise.** After completing all chapters, review the full graph from an external perspective. Look for thin notes, missing connections, and gaps in coverage. The first pass is never the last.
|
|
744
781
|
- **Analysis comes last.** Synthesis notes in `analysis/` should be written after all chapters are processed, when you have the full picture.
|
|
@@ -7,6 +7,17 @@ description: "Search Basic Memory notes by custom frontmatter fields with equali
|
|
|
7
7
|
|
|
8
8
|
Find notes by their structured frontmatter fields instead of (or in addition to) free-text content. Any custom YAML key in a note's frontmatter beyond the standard set (`title`, `type`, `tags`, `permalink`, `schema`) is automatically indexed as `entity_metadata` and becomes queryable.
|
|
9
9
|
|
|
10
|
+
## Project scope
|
|
11
|
+
|
|
12
|
+
If the project is unknown, discover it with `bm_project(command="list")`.
|
|
13
|
+
Pass a nonempty `project` on every note, folder, context and schema call, even
|
|
14
|
+
with one project or a qualified `memory://` URL. Use the chosen project's name,
|
|
15
|
+
`workspace/project`, or external ID; `<project>` below is a placeholder for it.
|
|
16
|
+
Recent activity also requires `project`. Search accepts `project` for one
|
|
17
|
+
project, `projects` for selected projects, or `all_projects=True` explicitly.
|
|
18
|
+
Discovery and bundled manuals need no project. Keep the same scope when following
|
|
19
|
+
returned paths, paginating, or recovering from a refusal.
|
|
20
|
+
|
|
10
21
|
## When to Use
|
|
11
22
|
|
|
12
23
|
- **Filtering by status or priority** — find all notes with `status: draft` or `priority: high`
|
|
@@ -18,7 +29,7 @@ Find notes by their structured frontmatter fields instead of (or in addition to)
|
|
|
18
29
|
|
|
19
30
|
## The Tool
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
Use `bm_search` for ranked results or `bm_find` for file discovery with selected fields. Pass frontmatter conditions via `filters`, including tags and status. Omit `query` (or pass `None`) for filter-only searches.
|
|
22
33
|
|
|
23
34
|
## Filter Syntax
|
|
24
35
|
|
|
@@ -104,43 +115,43 @@ against the value and a comparison with null is never true.
|
|
|
104
115
|
|
|
105
116
|
Frontmatter fields say when a note was filed or reviewed. To ask what a note
|
|
106
117
|
*claims was true* on a date, use observation qualifiers (see **memory-notes**)
|
|
107
|
-
and `
|
|
108
|
-
`
|
|
118
|
+
and `bm_search(valid_at="2026-07-01", project="<project>")` or
|
|
119
|
+
`bm_search(valid_overlaps="[2026-07-01,2026-08-01)", project="<project>")`.
|
|
109
120
|
|
|
110
|
-
## Using `
|
|
121
|
+
## Using `bm_search` with Metadata
|
|
111
122
|
|
|
112
|
-
Pass `
|
|
123
|
+
Pass `filters` to `bm_search`. Omit `query` for filter-only searches, or combine text and filters together.
|
|
113
124
|
|
|
114
125
|
```python
|
|
115
126
|
# Filter-only — find all notes with a given status
|
|
116
|
-
|
|
127
|
+
bm_search(filters={"status": "in-progress"}, project="<project>")
|
|
117
128
|
|
|
118
129
|
# Filter-only — high-priority specs in a specific project
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
bm_search(
|
|
131
|
+
filters={"type": "spec", "priority": {"$in": ["high", "critical"]}},
|
|
121
132
|
project="research",
|
|
122
133
|
page_size=10,
|
|
123
134
|
)
|
|
124
135
|
|
|
125
136
|
# Filter-only — notes with confidence above a threshold
|
|
126
|
-
|
|
137
|
+
bm_search(filters={"confidence": {"$gt": 0.7}}, project="<project>")
|
|
127
138
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
# Tags and status use the same filter syntax
|
|
140
|
+
bm_search(filters={"status": "active"}, project="<project>")
|
|
141
|
+
bm_search(filters={"tags": ["security", "oauth"]}, project="<project>")
|
|
131
142
|
|
|
132
143
|
# Text search narrowed by metadata
|
|
133
|
-
|
|
144
|
+
bm_search("authentication", filters={"status": "draft"}, project="<project>")
|
|
134
145
|
|
|
135
|
-
# Mix text,
|
|
136
|
-
|
|
146
|
+
# Mix text, tags, and an advanced filter
|
|
147
|
+
bm_search(
|
|
137
148
|
"oauth flow",
|
|
138
|
-
tags
|
|
139
|
-
|
|
149
|
+
filters={"tags": ["security"], "confidence": {"$gt": 0.7}},
|
|
150
|
+
project="<project>",
|
|
140
151
|
)
|
|
141
152
|
```
|
|
142
153
|
|
|
143
|
-
|
|
154
|
+
Use one `filters` object for all frontmatter conditions; its keys combine with AND.
|
|
144
155
|
|
|
145
156
|
## Tag Search Shorthand
|
|
146
157
|
|
|
@@ -148,11 +159,11 @@ The `tag:` prefix in a query converts to a tag filter automatically:
|
|
|
148
159
|
|
|
149
160
|
```python
|
|
150
161
|
# These are equivalent:
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
bm_search("tag:tier1", project="<project>")
|
|
163
|
+
bm_search(filters={"tags": ["tier1"]}, project="<project>")
|
|
153
164
|
|
|
154
165
|
# Multiple tags (comma or space separated) — all must match:
|
|
155
|
-
|
|
166
|
+
bm_search("tag:tier1,alpha", project="<project>")
|
|
156
167
|
```
|
|
157
168
|
|
|
158
169
|
## Example: Custom Frontmatter in Practice
|
|
@@ -183,19 +194,19 @@ Queries that find it:
|
|
|
183
194
|
|
|
184
195
|
```python
|
|
185
196
|
# By status and type
|
|
186
|
-
|
|
197
|
+
bm_search(filters={"status": "in-progress", "type": "spec"}, project="<project>")
|
|
187
198
|
|
|
188
199
|
# By numeric threshold
|
|
189
|
-
|
|
200
|
+
bm_search(filters={"confidence": {"$gt": 0.7}}, project="<project>")
|
|
190
201
|
|
|
191
202
|
# By priority set
|
|
192
|
-
|
|
203
|
+
bm_search(filters={"priority": {"$in": ["high", "critical"]}}, project="<project>")
|
|
193
204
|
|
|
194
205
|
# By tag shorthand
|
|
195
|
-
|
|
206
|
+
bm_search("tag:security", project="<project>")
|
|
196
207
|
|
|
197
208
|
# Combined text + metadata
|
|
198
|
-
|
|
209
|
+
bm_search("OAuth", filters={"status": "in-progress"}, project="<project>")
|
|
199
210
|
```
|
|
200
211
|
|
|
201
212
|
## Guidelines
|
|
@@ -204,6 +215,6 @@ search_notes("OAuth", metadata_filters={"status": "in-progress"})
|
|
|
204
215
|
- **Use text search for content queries.** If you're looking for notes *about* something, text search is better. Combine both when you need precision.
|
|
205
216
|
- **Custom fields are free.** Any YAML key you put in frontmatter becomes queryable — no schema or configuration required.
|
|
206
217
|
- **Multiple filters are AND.** `{"status": "active", "priority": "high"}` requires both conditions.
|
|
207
|
-
- **Omit `query` for filter-only searches.** `
|
|
218
|
+
- **Omit `query` for filter-only searches.** `bm_search(filters={"status": "active"}, project="<project>")` works without a text query.
|
|
208
219
|
- **Dot notation for nesting.** Access nested YAML structures with dots: `{"schema.version": "2"}` queries the `version` key inside a `schema` object.
|
|
209
|
-
- **
|
|
220
|
+
- **Use the same filters for file discovery.** `bm_find(filters={"status": "active"}, fields=["title", "priority"], project="<project>")` returns selected frontmatter values without reading each note.
|