@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
|
@@ -13,6 +13,17 @@ month, and puts it where someone will look for it.
|
|
|
13
13
|
Inspired by sleep-time compute: memory forms best between working sessions,
|
|
14
14
|
not during them.
|
|
15
15
|
|
|
16
|
+
## Project scope
|
|
17
|
+
|
|
18
|
+
If the project is unknown, discover it with `bm_project(command="list")`.
|
|
19
|
+
Pass a nonempty `project` on every note, folder, context and schema call, even
|
|
20
|
+
with one project or a qualified `memory://` URL. Use the chosen project's name,
|
|
21
|
+
`workspace/project`, or external ID; `<project>` below is a placeholder for it.
|
|
22
|
+
Recent activity also requires `project`. Search accepts `project` for one
|
|
23
|
+
project, `projects` for selected projects, or `all_projects=True` explicitly.
|
|
24
|
+
Discovery and bundled manuals need no project. Keep the same scope when following
|
|
25
|
+
returned paths, paginating, or recovering from a refusal.
|
|
26
|
+
|
|
16
27
|
## When to Run
|
|
17
28
|
|
|
18
29
|
- **Scheduled**: once or twice a day, or on a heartbeat
|
|
@@ -26,23 +37,23 @@ not during them.
|
|
|
26
37
|
```python
|
|
27
38
|
# The last reflection is the checkpoint. Titles carry date and time, so the
|
|
28
39
|
# newest sorts first.
|
|
29
|
-
|
|
40
|
+
bm_list(path="reflections", sort="title_desc", page_size=1, project="<project>")
|
|
30
41
|
|
|
31
42
|
# Everything changed since that date (json gives one row per note). With no
|
|
32
|
-
# reflection yet, use
|
|
33
|
-
|
|
34
|
-
# Ask for page=2, 3, ... until
|
|
43
|
+
# reflection yet, use since="7d".
|
|
44
|
+
bm_project(command="recent", since="2026-10-13", items=["note"], output_format="json", page_size=50, project="<project>")
|
|
45
|
+
# Ask for page=2, 3, ... until has_more is false.
|
|
35
46
|
|
|
36
47
|
# Read each changed note in full before judging it
|
|
37
|
-
|
|
48
|
+
bm_read(identifier="meetings/2026-10-14-launch-sync", project="<project>")
|
|
38
49
|
```
|
|
39
50
|
|
|
40
51
|
Timeframes count whole days, so the window can overlap the last reflection.
|
|
41
52
|
Skip notes it lists as `reviewed`, and notes you wrote in an earlier
|
|
42
53
|
reflection, unless someone edited them since.
|
|
43
54
|
|
|
44
|
-
`
|
|
45
|
-
than that, use `
|
|
55
|
+
`bm_project(command="recent", project="<project>")` looks back at most a year. If the last reflection is older
|
|
56
|
+
than that, use `since="365d"` and record in this reflection that changes
|
|
46
57
|
before that window were not reviewed.
|
|
47
58
|
|
|
48
59
|
### 2. Decide what lasts
|
|
@@ -65,8 +76,8 @@ For each item, ask:
|
|
|
65
76
|
Find the durable note for each item before writing anything:
|
|
66
77
|
|
|
67
78
|
```python
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
bm_search(query="launch checklist owner", project="<project>")
|
|
80
|
+
bm_context(identifier="memory://projects/launch", project="<project>")
|
|
70
81
|
```
|
|
71
82
|
|
|
72
83
|
- **Say when it holds.** A fact with a date gets a qualifier (see
|
|
@@ -91,19 +102,20 @@ build_context(url="memory://projects/launch")
|
|
|
91
102
|
checksum to the edit, so the edit applies only to the revision you read.
|
|
92
103
|
|
|
93
104
|
```python
|
|
94
|
-
note =
|
|
95
|
-
|
|
105
|
+
note = bm_read(identifier="Launch Plan", output_format="json", project="<project>")
|
|
106
|
+
bm_edit(
|
|
96
107
|
identifier="Launch Plan",
|
|
97
|
-
|
|
108
|
+
command="replace_section",
|
|
98
109
|
section="## Owners",
|
|
99
110
|
content="- [owner] Maya owns the launch checklist",
|
|
100
111
|
expected_checksum=note["checksum"],
|
|
112
|
+
project="<project>",
|
|
101
113
|
)
|
|
102
114
|
```
|
|
103
115
|
|
|
104
116
|
If the edit reports a checksum conflict, someone changed the note after you
|
|
105
117
|
read it: read it again and decide again from the new text.
|
|
106
|
-
- **The topic has no note**: create one with `
|
|
118
|
+
- **The topic has no note**: create one with `bm_write`, following
|
|
107
119
|
**memory-notes**: a clear title, observations with categories, and relations.
|
|
108
120
|
- **Link back**: every consolidated fact names its source, e.g.
|
|
109
121
|
`- derived_from [[2026-10-14 Launch Sync]]`, so a reader can check it.
|
|
@@ -7,6 +7,17 @@ description: "Research an external subject on the web and synthesize it into a s
|
|
|
7
7
|
|
|
8
8
|
Research an external subject, synthesize what you find, and create a structured Basic Memory entity — with the user's approval.
|
|
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
|
## Workflow
|
|
11
22
|
|
|
12
23
|
### Step 1: Web Research
|
|
@@ -36,8 +47,8 @@ Search for current information across multiple sources. Aim for 3-5 searches to
|
|
|
36
47
|
Before proposing a new entity, search Basic Memory:
|
|
37
48
|
|
|
38
49
|
```python
|
|
39
|
-
|
|
40
|
-
|
|
50
|
+
bm_search(query="Acme Corp", project="<project>")
|
|
51
|
+
bm_search(query="acme", project="<project>")
|
|
41
52
|
```
|
|
42
53
|
|
|
43
54
|
Try name variations — full name, abbreviation, acronym, domain name.
|
|
@@ -45,7 +56,7 @@ Try name variations — full name, abbreviation, acronym, domain name.
|
|
|
45
56
|
If the entity already exists:
|
|
46
57
|
- Report what you found in Basic Memory alongside your web research
|
|
47
58
|
- Offer to update the existing note with new information
|
|
48
|
-
- Use `
|
|
59
|
+
- Use `bm_edit` to append new observations or update outdated ones
|
|
49
60
|
|
|
50
61
|
If the entity doesn't exist, proceed to evaluation.
|
|
51
62
|
|
|
@@ -109,7 +120,7 @@ After approval, create a structured note. Adapt the template to the entity type:
|
|
|
109
120
|
#### Organization
|
|
110
121
|
|
|
111
122
|
```python
|
|
112
|
-
|
|
123
|
+
bm_write(
|
|
113
124
|
title="Acme Corp",
|
|
114
125
|
directory="organizations",
|
|
115
126
|
note_type="organization",
|
|
@@ -135,14 +146,15 @@ write_note(
|
|
|
135
146
|
- [additional observations from research findings]
|
|
136
147
|
|
|
137
148
|
## Relations
|
|
138
|
-
- [Link to related entities already in the knowledge graph]"""
|
|
149
|
+
- [Link to related entities already in the knowledge graph]""",
|
|
150
|
+
project="<project>",
|
|
139
151
|
)
|
|
140
152
|
```
|
|
141
153
|
|
|
142
154
|
#### Person
|
|
143
155
|
|
|
144
156
|
```python
|
|
145
|
-
|
|
157
|
+
bm_write(
|
|
146
158
|
title="Jane Smith",
|
|
147
159
|
directory="people",
|
|
148
160
|
note_type="person",
|
|
@@ -163,14 +175,15 @@ write_note(
|
|
|
163
175
|
- [source] Researched on YYYY-MM-DD
|
|
164
176
|
|
|
165
177
|
## Relations
|
|
166
|
-
- works_at [[Organization]]"""
|
|
178
|
+
- works_at [[Organization]]""",
|
|
179
|
+
project="<project>",
|
|
167
180
|
)
|
|
168
181
|
```
|
|
169
182
|
|
|
170
183
|
#### Technology
|
|
171
184
|
|
|
172
185
|
```python
|
|
173
|
-
|
|
186
|
+
bm_write(
|
|
174
187
|
title="Technology Name",
|
|
175
188
|
directory="concepts",
|
|
176
189
|
note_type="concept",
|
|
@@ -192,13 +205,14 @@ write_note(
|
|
|
192
205
|
- [source] Researched on YYYY-MM-DD
|
|
193
206
|
|
|
194
207
|
## Relations
|
|
195
|
-
- [Link to related concepts, tools, or projects in the knowledge graph]"""
|
|
208
|
+
- [Link to related concepts, tools, or projects in the knowledge graph]""",
|
|
209
|
+
project="<project>",
|
|
196
210
|
)
|
|
197
211
|
```
|
|
198
212
|
|
|
199
213
|
Adapt these templates freely. The key elements are: note_type/tags parameters, an overview, structured details, observations with categories, and relations.
|
|
200
214
|
|
|
201
|
-
If `
|
|
215
|
+
If `bm_write` reports similar existing notes, check whether one of them is this
|
|
202
216
|
entity before keeping the new note; merge or link instead of duplicating.
|
|
203
217
|
|
|
204
218
|
### Step 6: Store Source Context
|
|
@@ -207,11 +221,12 @@ If the user provided context with their request, capture it in the entity:
|
|
|
207
221
|
|
|
208
222
|
```python
|
|
209
223
|
# User said: "Acme Corp — saw their demo at the conference last week"
|
|
210
|
-
|
|
224
|
+
bm_edit(
|
|
211
225
|
identifier="Acme Corp",
|
|
212
|
-
|
|
226
|
+
command="insert_after_section",
|
|
213
227
|
section="Observations",
|
|
214
|
-
content="- [context] Saw their demo at conference, week of 2026-02-17"
|
|
228
|
+
content="- [context] Saw their demo at conference, week of 2026-02-17",
|
|
229
|
+
project="<project>",
|
|
215
230
|
)
|
|
216
231
|
```
|
|
217
232
|
|
|
@@ -7,6 +7,17 @@ description: "Manage Basic Memory schemas: find unschemaed notes, infer and edit
|
|
|
7
7
|
|
|
8
8
|
Manage structured note types using Basic Memory's Picoschema system. Schemas define what fields a note type should have, making notes uniform, queryable, and validatable.
|
|
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
|
- **New note type emerging** — you notice several notes share the same structure (meetings, people, decisions)
|
|
@@ -112,12 +123,12 @@ settings:
|
|
|
112
123
|
|
|
113
124
|
Look for clusters of notes that share structure but have no schema:
|
|
114
125
|
|
|
115
|
-
1. **Search by type**: `
|
|
126
|
+
1. **Search by type**: `bm_search(note_types=["meeting"], project="<project>")` — if many notes share a `type` but no `schema/Meeting.md` exists, it's a candidate.
|
|
116
127
|
|
|
117
|
-
2. **Infer a schema**: Use `
|
|
128
|
+
2. **Infer a schema**: Use `bm_schema(command="infer", project="<project>")` to analyze existing notes and generate a suggested schema:
|
|
118
129
|
```python
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
bm_schema(command="infer", note_type="Meeting", project="<project>")
|
|
131
|
+
bm_schema(command="infer", note_type="Meeting", threshold=0.5, project="<project>") # fields in 50%+ of notes
|
|
121
132
|
```
|
|
122
133
|
The threshold (0.0–1.0) controls how common a field must be to be included. Default is usually fine; lower it to catch rarer fields.
|
|
123
134
|
|
|
@@ -128,7 +139,7 @@ Look for clusters of notes that share structure but have no schema:
|
|
|
128
139
|
Write the schema note to `schema/<EntityName>`:
|
|
129
140
|
|
|
130
141
|
```python
|
|
131
|
-
|
|
142
|
+
bm_write(
|
|
132
143
|
title="Meeting",
|
|
133
144
|
directory="schema",
|
|
134
145
|
note_type="schema",
|
|
@@ -150,7 +161,8 @@ Schema for meeting notes.
|
|
|
150
161
|
## Observations
|
|
151
162
|
- [convention] Meeting notes live in memory/meetings/ or as daily entries
|
|
152
163
|
- [convention] Always include date and topic
|
|
153
|
-
- [convention] Action items should become tasks when complex"""
|
|
164
|
+
- [convention] Action items should become tasks when complex""",
|
|
165
|
+
project="<project>",
|
|
154
166
|
)
|
|
155
167
|
```
|
|
156
168
|
|
|
@@ -168,13 +180,13 @@ Check how well existing notes conform to their schema:
|
|
|
168
180
|
|
|
169
181
|
```python
|
|
170
182
|
# Validate all notes of a type
|
|
171
|
-
|
|
183
|
+
bm_schema(command="validate", note_type="Meeting", project="<project>")
|
|
172
184
|
|
|
173
185
|
# Validate a single note
|
|
174
|
-
|
|
186
|
+
bm_schema(command="validate", identifier="meetings/2026-02-10-standup", project="<project>")
|
|
175
187
|
```
|
|
176
188
|
|
|
177
|
-
**Important:** `
|
|
189
|
+
**Important:** `bm_schema(command="validate", project="<project>")` checks for schema fields as **observation categories** in the note body — e.g., a `status` field expects `- [status] active` as an observation. Fields stored only in frontmatter metadata won't satisfy validation. To pass cleanly, include schema fields as both frontmatter values (for metadata search) and observations (for schema validation).
|
|
178
190
|
|
|
179
191
|
Validation reports **missing required fields** (as observation categories, or relations
|
|
180
192
|
for entity-reference fields) and **invalid enum values**. Undeclared observation categories
|
|
@@ -188,10 +200,10 @@ type-checked.
|
|
|
188
200
|
|
|
189
201
|
## Detecting Drift
|
|
190
202
|
|
|
191
|
-
Over time, notes evolve and schemas lag behind. Use `
|
|
203
|
+
Over time, notes evolve and schemas lag behind. Use `bm_schema(command="diff", project="<project>")` to find divergence:
|
|
192
204
|
|
|
193
205
|
```python
|
|
194
|
-
|
|
206
|
+
bm_schema(command="diff", note_type="Meeting", project="<project>")
|
|
195
207
|
```
|
|
196
208
|
|
|
197
209
|
Diff reports:
|
|
@@ -203,15 +215,14 @@ Diff reports:
|
|
|
203
215
|
|
|
204
216
|
When note structure changes:
|
|
205
217
|
|
|
206
|
-
1. **Run diff** to see current state: `
|
|
207
|
-
2. **Update the schema note** via `
|
|
218
|
+
1. **Run diff** to see current state: `bm_schema(command="diff", note_type="Meeting", project="<project>")`
|
|
219
|
+
2. **Update the schema note** via `bm_edit` with the `metadata` parameter. Top-level
|
|
208
220
|
keys are replaced whole, so pass the complete updated `schema` map along with the new
|
|
209
221
|
`version`:
|
|
210
222
|
```python
|
|
211
|
-
|
|
223
|
+
bm_edit(
|
|
212
224
|
identifier="schema/Meeting",
|
|
213
|
-
|
|
214
|
-
content="",
|
|
225
|
+
command="set_metadata",
|
|
215
226
|
metadata={
|
|
216
227
|
"version": 2,
|
|
217
228
|
"schema": {
|
|
@@ -221,10 +232,11 @@ When note structure changes:
|
|
|
221
232
|
"decisions?(array)": "string, decisions made",
|
|
222
233
|
"location?": "string, where it happened"
|
|
223
234
|
}
|
|
224
|
-
}
|
|
235
|
+
},
|
|
236
|
+
project="<project>",
|
|
225
237
|
)
|
|
226
238
|
```
|
|
227
|
-
3. **Re-validate** to confirm existing notes still pass: `
|
|
239
|
+
3. **Re-validate** to confirm existing notes still pass: `bm_schema(command="validate", note_type="Meeting", project="<project>")`
|
|
228
240
|
4. **Fix outliers** — update notes that don't conform to the new schema
|
|
229
241
|
|
|
230
242
|
### Evolution Guidelines
|
|
@@ -238,10 +250,10 @@ When note structure changes:
|
|
|
238
250
|
## Workflow Summary
|
|
239
251
|
|
|
240
252
|
```
|
|
241
|
-
1. Notice repeated note structure → infer schema (
|
|
242
|
-
2. Review + create schema note → write to schema/ (
|
|
243
|
-
3. Validate existing notes → check conformance (
|
|
244
|
-
4. Fix outliers → edit non-conforming notes (
|
|
245
|
-
5. Periodically check drift → detect divergence (
|
|
246
|
-
6. Evolve schema as needed → update schema note (
|
|
253
|
+
1. Notice repeated note structure → infer schema (bm_schema(command="infer", project="<project>"))
|
|
254
|
+
2. Review + create schema note → write to schema/ (bm_write)
|
|
255
|
+
3. Validate existing notes → check conformance (bm_schema(command="validate", project="<project>"))
|
|
256
|
+
4. Fix outliers → edit non-conforming notes (bm_edit)
|
|
257
|
+
5. Periodically check drift → detect divergence (bm_schema(command="diff", project="<project>"))
|
|
258
|
+
6. Evolve schema as needed → update schema note (bm_edit)
|
|
247
259
|
```
|
|
@@ -7,6 +7,17 @@ description: "Task management via Basic Memory schemas: create, track, and resum
|
|
|
7
7
|
|
|
8
8
|
Manage work-in-progress using Basic Memory's schema system. Tasks are just notes with `type: Task` — they live in the knowledge graph, validate against a schema, and survive context compaction.
|
|
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
|
- **Starting multi-step work** (3+ steps, or anything that might outlast the context window)
|
|
@@ -42,10 +53,10 @@ settings:
|
|
|
42
53
|
|
|
43
54
|
## Creating a Task
|
|
44
55
|
|
|
45
|
-
When work qualifies, create a task note. Use `
|
|
56
|
+
When work qualifies, create a task note. Use `bm_write` with `note_type="Task"` and put queryable fields in `metadata`:
|
|
46
57
|
|
|
47
58
|
```python
|
|
48
|
-
|
|
59
|
+
bm_write(
|
|
49
60
|
title="Descriptive task name",
|
|
50
61
|
directory="tasks",
|
|
51
62
|
note_type="Task",
|
|
@@ -75,18 +86,19 @@ What future-you needs to pick up this work. Include:
|
|
|
75
86
|
- Key file paths and repos involved
|
|
76
87
|
- Decisions already made and why
|
|
77
88
|
- What was tried and what worked/didn't
|
|
78
|
-
- Where to look for related context"""
|
|
89
|
+
- Where to look for related context""",
|
|
90
|
+
project="<project>",
|
|
79
91
|
)
|
|
80
92
|
```
|
|
81
93
|
|
|
82
|
-
**Why both frontmatter and observations?** Fields in `metadata` (stored as frontmatter) power `
|
|
94
|
+
**Why both frontmatter and observations?** Fields in `metadata` (stored as frontmatter) power `bm_search` with `filters`. Fields as observations (`- [status] active`) power `bm_schema(command="validate", project="<project>")`. Include queryable fields in both places for full coverage.
|
|
83
95
|
|
|
84
96
|
### Key Principles
|
|
85
97
|
|
|
86
98
|
- **Steps are concrete and checkable** — "Implement X in file Y", not "figure out stuff"
|
|
87
99
|
- **Context is for post-amnesia resumption** — Write it as if explaining to a smart person who knows nothing about what you've been doing
|
|
88
100
|
- **Relations link to other entities** — `parent_task [[Other Task]]`, `related_to [[Some Note]]`
|
|
89
|
-
- **Note types are normalized to snake_case** — `
|
|
101
|
+
- **Note types are normalized to snake_case** — `bm_write(note_type="Task", project="<project>")` stores `type: task`, and `note_types=["Task"]` or `note_types=["task"]` both match it.
|
|
90
102
|
|
|
91
103
|
## Resuming After Compaction
|
|
92
104
|
|
|
@@ -94,7 +106,7 @@ On session start or after compaction:
|
|
|
94
106
|
|
|
95
107
|
1. **Search for active tasks:**
|
|
96
108
|
```python
|
|
97
|
-
|
|
109
|
+
bm_search(note_types=["task"], filters={"status": "active"}, project="<project>")
|
|
98
110
|
```
|
|
99
111
|
|
|
100
112
|
2. **Read the task note** to get full context
|
|
@@ -122,15 +134,15 @@ edit. Two sessions often update the same task, for example in a pre-compaction
|
|
|
122
134
|
flush; a revision conflict means the other one wrote first, so re-read before
|
|
123
135
|
writing.
|
|
124
136
|
|
|
125
|
-
Update frontmatter too, with the `metadata` parameter (
|
|
137
|
+
Update frontmatter too, with `command="set_metadata"` and the `metadata` parameter (the body stays unchanged):
|
|
126
138
|
```python
|
|
127
|
-
task =
|
|
128
|
-
|
|
139
|
+
task = bm_read(identifier="tasks/descriptive-task-name", output_format="json", project="<project>")
|
|
140
|
+
bm_edit(
|
|
129
141
|
identifier="tasks/descriptive-task-name",
|
|
130
|
-
|
|
131
|
-
content="",
|
|
142
|
+
command="set_metadata",
|
|
132
143
|
metadata={"current_step": 3},
|
|
133
|
-
expected_checksum=task["checksum"]
|
|
144
|
+
expected_checksum=task["checksum"],
|
|
145
|
+
project="<project>",
|
|
134
146
|
)
|
|
135
147
|
```
|
|
136
148
|
|
|
@@ -148,7 +160,7 @@ Add a brief summary of what was accomplished and any follow-up needed.
|
|
|
148
160
|
|
|
149
161
|
When a compaction event is imminent:
|
|
150
162
|
|
|
151
|
-
1. Find all active tasks: `
|
|
163
|
+
1. Find all active tasks: `bm_search(note_types=["task"], filters={"status": "active"}, project="<project>")`
|
|
152
164
|
2. For each, update:
|
|
153
165
|
- `current_step` to reflect actual progress
|
|
154
166
|
- `context` with everything needed to resume
|
|
@@ -161,14 +173,14 @@ With BM's schema system, tasks are fully queryable:
|
|
|
161
173
|
|
|
162
174
|
| Query | What it finds |
|
|
163
175
|
|-------|--------------|
|
|
164
|
-
| `
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
176
|
+
| `bm_search(note_types=["task"], project="<project>")` | All tasks |
|
|
177
|
+
| `bm_search(note_types=["task"], filters={"status": "active"}, project="<project>")` | Active tasks |
|
|
178
|
+
| `bm_search(note_types=["task"], filters={"status": "blocked"}, project="<project>")` | Blocked tasks |
|
|
179
|
+
| `bm_search(note_types=["task"], filters={"assigned_to": "claude"}, project="<project>")` | My tasks |
|
|
180
|
+
| `bm_search("blockers", note_types=["task"], project="<project>")` | Tasks with blockers |
|
|
181
|
+
| `bm_search(note_types=["task"], time_kind="due", valid_overlaps="[2026-10-14,2026-10-21)", project="<project>")` | Tasks with a step due this week |
|
|
182
|
+
| `bm_schema(command="validate", note_type="Task", project="<project>")` | Validate all tasks against schema |
|
|
183
|
+
| `bm_schema(command="diff", note_type="Task", project="<project>")` | Detect drift between schema and actual task notes |
|
|
172
184
|
|
|
173
185
|
### Due Dates
|
|
174
186
|
|
|
@@ -189,4 +201,4 @@ also returns overdue items.
|
|
|
189
201
|
- **Context > steps** — Steps tell you what to do; context tells you why and how
|
|
190
202
|
- **Close finished tasks** — Don't leave completed work as `active`
|
|
191
203
|
- **Link related tasks** — Use `parent_task [[X]]` or relations to connect related work
|
|
192
|
-
- **Schema validation is your friend** — Run `
|
|
204
|
+
- **Schema validation is your friend** — Run `bm_schema(command="validate", note_type="Task", project="<project>")` periodically to catch incomplete tasks
|
package/tools/build-context.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox"
|
|
2
2
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
3
|
-
import type
|
|
3
|
+
import { type BmClient, describeToolError } from "../bm-client.ts"
|
|
4
4
|
import { log } from "../logger.ts"
|
|
5
5
|
|
|
6
6
|
export function registerContextTool(
|
|
@@ -67,8 +67,15 @@ export function registerContextTool(
|
|
|
67
67
|
const primary = result.primary_result
|
|
68
68
|
sections.push(`## ${primary.title}\n${primary.content}`)
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
// The note body already contains its observations as
|
|
71
|
+
// "- [category] text" lines, so listing them again doubled the
|
|
72
|
+
// output. Only observations missing from the shown body (for
|
|
73
|
+
// example when the server truncates a long note) are listed.
|
|
74
|
+
const unseen = (result.observations ?? []).filter(
|
|
75
|
+
(o) => !primary.content?.includes(`[${o.category}] ${o.content}`),
|
|
76
|
+
)
|
|
77
|
+
if (unseen.length > 0) {
|
|
78
|
+
const obs = unseen
|
|
72
79
|
.map((o) => `- [${o.category}] ${o.content}`)
|
|
73
80
|
.join("\n")
|
|
74
81
|
sections.push(`### Observations\n${obs}`)
|
|
@@ -78,7 +85,9 @@ export function registerContextTool(
|
|
|
78
85
|
const rels = result.related_results
|
|
79
86
|
.map((r) => {
|
|
80
87
|
if (r.type === "relation") {
|
|
81
|
-
return
|
|
88
|
+
return r.to_entity
|
|
89
|
+
? `- ${r.relation_type} → **${r.to_entity}**`
|
|
90
|
+
: `- ${r.relation_type} → **${r.to_name ?? "unknown"}** (no note yet)`
|
|
82
91
|
}
|
|
83
92
|
const label = r.relation_type
|
|
84
93
|
? `${r.relation_type} → **${r.title}**`
|
|
@@ -111,7 +120,7 @@ export function registerContextTool(
|
|
|
111
120
|
content: [
|
|
112
121
|
{
|
|
113
122
|
type: "text" as const,
|
|
114
|
-
text: `Failed to build context for "${params.url}"
|
|
123
|
+
text: `Failed to build context for "${params.url}": ${describeToolError(err)}`,
|
|
115
124
|
},
|
|
116
125
|
],
|
|
117
126
|
details: {
|
package/tools/delete-note.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox"
|
|
2
2
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
3
|
-
import type
|
|
3
|
+
import { type BmClient, describeNoteError } from "../bm-client.ts"
|
|
4
4
|
import { log } from "../logger.ts"
|
|
5
5
|
|
|
6
6
|
export function registerDeleteTool(
|
|
@@ -55,7 +55,7 @@ export function registerDeleteTool(
|
|
|
55
55
|
content: [
|
|
56
56
|
{
|
|
57
57
|
type: "text" as const,
|
|
58
|
-
text: `Failed to delete "${params.identifier}"
|
|
58
|
+
text: `Failed to delete "${params.identifier}": ${describeNoteError(err)}`,
|
|
59
59
|
},
|
|
60
60
|
],
|
|
61
61
|
details: {
|
package/tools/edit-note.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox"
|
|
2
2
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
type BmClient,
|
|
5
|
+
describeNoteError,
|
|
6
|
+
type EditOperation,
|
|
7
|
+
} from "../bm-client.ts"
|
|
4
8
|
import { log } from "../logger.ts"
|
|
5
9
|
|
|
6
10
|
export function registerEditTool(
|
|
@@ -13,8 +17,11 @@ export function registerEditTool(
|
|
|
13
17
|
label: "Edit Note",
|
|
14
18
|
description:
|
|
15
19
|
"Incrementally edit an existing note in the Basic Memory knowledge graph. " +
|
|
16
|
-
"Supports append, prepend, find/replace,
|
|
17
|
-
"without rewriting the entire note.
|
|
20
|
+
"Supports append, prepend, find/replace, replacing a section, and inserting " +
|
|
21
|
+
"next to a section heading, without rewriting the entire note. To add to a " +
|
|
22
|
+
"specific section (for example a new observation), use insert_after_section " +
|
|
23
|
+
"with that heading; append always adds to the very end of the note. " +
|
|
24
|
+
"The result returns the note's " +
|
|
18
25
|
"permalink and file_path: cite those exact values when confirming " +
|
|
19
26
|
"the edit — never report a save that no tool call performed.",
|
|
20
27
|
parameters: Type.Object({
|
|
@@ -27,11 +34,15 @@ export function registerEditTool(
|
|
|
27
34
|
Type.Literal("prepend"),
|
|
28
35
|
Type.Literal("find_replace"),
|
|
29
36
|
Type.Literal("replace_section"),
|
|
37
|
+
Type.Literal("insert_before_section"),
|
|
38
|
+
Type.Literal("insert_after_section"),
|
|
30
39
|
],
|
|
31
40
|
{
|
|
32
41
|
description:
|
|
33
|
-
"Edit operation: append (add to end), prepend (add to start), " +
|
|
34
|
-
"find_replace (replace matching text), replace_section (replace a heading section)"
|
|
42
|
+
"Edit operation: append (add to end of note), prepend (add to start), " +
|
|
43
|
+
"find_replace (replace matching text), replace_section (replace a heading section), " +
|
|
44
|
+
"insert_after_section (add directly below a heading, at the top of its section), " +
|
|
45
|
+
"insert_before_section (add directly above a heading, at the end of the section before it)",
|
|
35
46
|
},
|
|
36
47
|
),
|
|
37
48
|
content: Type.String({
|
|
@@ -45,7 +56,8 @@ export function registerEditTool(
|
|
|
45
56
|
section: Type.Optional(
|
|
46
57
|
Type.String({
|
|
47
58
|
description:
|
|
48
|
-
"Section heading
|
|
59
|
+
"Section heading, e.g. 'Observations' (required for replace_section, " +
|
|
60
|
+
"insert_before_section and insert_after_section; not allowed with other operations)",
|
|
49
61
|
}),
|
|
50
62
|
),
|
|
51
63
|
expected_replacements: Type.Optional(
|
|
@@ -74,7 +86,7 @@ export function registerEditTool(
|
|
|
74
86
|
_toolCallId: string,
|
|
75
87
|
params: {
|
|
76
88
|
identifier: string
|
|
77
|
-
operation:
|
|
89
|
+
operation: EditOperation
|
|
78
90
|
content: string
|
|
79
91
|
find_text?: string
|
|
80
92
|
section?: string
|
|
@@ -103,7 +115,7 @@ export function registerEditTool(
|
|
|
103
115
|
content: [
|
|
104
116
|
{
|
|
105
117
|
type: "text" as const,
|
|
106
|
-
text: `Note updated: ${note.title} (${note.permalink})`,
|
|
118
|
+
text: `Note updated: ${note.title} (${note.permalink})\nFile: ${note.file_path}`,
|
|
107
119
|
},
|
|
108
120
|
],
|
|
109
121
|
details: {
|
|
@@ -116,13 +128,14 @@ export function registerEditTool(
|
|
|
116
128
|
}
|
|
117
129
|
} catch (err) {
|
|
118
130
|
log.error("edit_note failed", err)
|
|
131
|
+
// Trigger: any edit failure, including invalid arguments and
|
|
132
|
+
// checksum conflicts, not only a missing note.
|
|
133
|
+
// Why: a blanket "may not exist" sent agents hunting for the wrong
|
|
134
|
+
// problem; the server's message says what to change.
|
|
135
|
+
// Outcome: the agent sees the server's reason verbatim.
|
|
136
|
+
const text = `Failed to edit note "${params.identifier}": ${describeNoteError(err)}`
|
|
119
137
|
return {
|
|
120
|
-
content: [
|
|
121
|
-
{
|
|
122
|
-
type: "text" as const,
|
|
123
|
-
text: `Failed to edit note "${params.identifier}". It may not exist.`,
|
|
124
|
-
},
|
|
125
|
-
],
|
|
138
|
+
content: [{ type: "text" as const, text }],
|
|
126
139
|
details: {
|
|
127
140
|
identifier: params.identifier,
|
|
128
141
|
operation: params.operation,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox"
|
|
2
2
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
type BmClient,
|
|
5
|
+
describeToolError,
|
|
6
|
+
type ProjectListResult,
|
|
7
|
+
} from "../bm-client.ts"
|
|
4
8
|
import { log } from "../logger.ts"
|
|
5
9
|
|
|
6
10
|
function normalizeProject(project: ProjectListResult) {
|
|
@@ -102,7 +106,7 @@ export function registerProjectListTool(
|
|
|
102
106
|
content: [
|
|
103
107
|
{
|
|
104
108
|
type: "text" as const,
|
|
105
|
-
text:
|
|
109
|
+
text: `Failed to list Basic Memory projects: ${describeToolError(err)}`,
|
|
106
110
|
},
|
|
107
111
|
],
|
|
108
112
|
details: {
|