@basicmemory/openclaw-basic-memory 0.1.3 → 0.1.5
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 +1 -0
- package/bm-client.ts +19 -1
- package/commands/skills.ts +25 -25
- package/commands/slash.ts +32 -0
- package/openclaw.plugin.json +7 -4
- package/package.json +4 -1
- package/scripts/fetch-skills.ts +133 -0
- package/skills/manifest.json +47 -0
- package/skills/memory-defrag/SKILL.md +0 -1
- package/skills/memory-ingest/SKILL.md +309 -0
- package/skills/memory-lifecycle/SKILL.md +182 -0
- package/skills/memory-metadata-search/SKILL.md +37 -63
- package/skills/memory-notes/SKILL.md +111 -35
- package/skills/memory-reflect/SKILL.md +14 -4
- package/skills/memory-research/SKILL.md +235 -0
- package/skills/memory-schema/SKILL.md +35 -35
- package/skills/memory-tasks/SKILL.md +31 -27
- package/tools/write-note.ts +28 -3
|
@@ -42,24 +42,26 @@ settings:
|
|
|
42
42
|
|
|
43
43
|
## Creating a Task
|
|
44
44
|
|
|
45
|
-
When work qualifies, create a note in `
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
title
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
When work qualifies, create a task note. Use `write_note` with `note_type="Task"` and put queryable fields in `metadata`:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
write_note(
|
|
49
|
+
title="Descriptive task name",
|
|
50
|
+
directory="tasks",
|
|
51
|
+
note_type="Task",
|
|
52
|
+
metadata={
|
|
53
|
+
"status": "active",
|
|
54
|
+
"priority": "high",
|
|
55
|
+
"current_step": 1,
|
|
56
|
+
"steps": ["First step", "Second step", "Third step"]
|
|
57
|
+
},
|
|
58
|
+
tags=["task"],
|
|
59
|
+
content="""# Descriptive task name
|
|
57
60
|
|
|
58
61
|
## Observations
|
|
59
62
|
- [description] What needs to be done, concisely
|
|
60
63
|
- [status] active
|
|
61
|
-
- [assigned_to]
|
|
62
|
-
- [started] YYYY-MM-DD
|
|
64
|
+
- [assigned_to] claude
|
|
63
65
|
- [current_step] 1
|
|
64
66
|
|
|
65
67
|
## Steps
|
|
@@ -72,23 +74,27 @@ What future-you needs to pick up this work. Include:
|
|
|
72
74
|
- Key file paths and repos involved
|
|
73
75
|
- Decisions already made and why
|
|
74
76
|
- What was tried and what worked/didn't
|
|
75
|
-
- Where to look for related context
|
|
77
|
+
- Where to look for related context"""
|
|
78
|
+
)
|
|
76
79
|
```
|
|
77
80
|
|
|
81
|
+
**Why both frontmatter and observations?** Fields in `metadata` (stored as frontmatter) power `search_notes` with `metadata_filters`. Fields as observations (`- [status] active`) power `schema_validate`. Include queryable fields in both places for full coverage.
|
|
82
|
+
|
|
78
83
|
### Key Principles
|
|
79
84
|
|
|
80
85
|
- **Steps are concrete and checkable** — "Implement X in file Y", not "figure out stuff"
|
|
81
86
|
- **Context is for post-amnesia resumption** — Write it as if explaining to a smart person who knows nothing about what you've been doing
|
|
82
|
-
- **Use observations for BM-queryable fields** — `[status]`, `[description]`, `[assigned_to]` etc. become searchable in the knowledge graph
|
|
83
87
|
- **Relations link to other entities** — `parent_task [[Other Task]]`, `related_to [[Some Note]]`
|
|
88
|
+
- **`note_types` is case-sensitive** — `write_note(note_type="Task")` stores the type as lowercase `task` in frontmatter. Use `note_types=["task"]` (lowercase) in search queries.
|
|
84
89
|
|
|
85
90
|
## Resuming After Compaction
|
|
86
91
|
|
|
87
92
|
On session start or after compaction:
|
|
88
93
|
|
|
89
94
|
1. **Search for active tasks:**
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
```python
|
|
96
|
+
search_notes(note_types=["task"], status="active")
|
|
97
|
+
```
|
|
92
98
|
|
|
93
99
|
2. **Read the task note** to get full context
|
|
94
100
|
|
|
@@ -129,7 +135,7 @@ Add a brief summary of what was accomplished and any follow-up needed.
|
|
|
129
135
|
|
|
130
136
|
When a compaction event is imminent:
|
|
131
137
|
|
|
132
|
-
1. Find all active tasks: `search_notes("
|
|
138
|
+
1. Find all active tasks: `search_notes(note_types=["task"], status="active")`
|
|
133
139
|
2. For each, update:
|
|
134
140
|
- `current_step` to reflect actual progress
|
|
135
141
|
- `context` with everything needed to resume
|
|
@@ -142,16 +148,14 @@ With BM's schema system, tasks are fully queryable:
|
|
|
142
148
|
|
|
143
149
|
| Query | What it finds |
|
|
144
150
|
|-------|--------------|
|
|
145
|
-
| `search_notes("
|
|
146
|
-
| `search_notes("
|
|
147
|
-
| `search_notes("
|
|
148
|
-
| `search_notes("
|
|
149
|
-
| `search_notes("
|
|
151
|
+
| `search_notes(note_types=["task"])` | All tasks |
|
|
152
|
+
| `search_notes(note_types=["task"], status="active")` | Active tasks |
|
|
153
|
+
| `search_notes(note_types=["task"], status="blocked")` | Blocked tasks |
|
|
154
|
+
| `search_notes(note_types=["task"], metadata_filters={"assigned_to": "claude"})` | My tasks |
|
|
155
|
+
| `search_notes("blockers", note_types=["task"])` | Tasks with blockers |
|
|
150
156
|
| `schema_validate(noteType="Task")` | Validate all tasks against schema |
|
|
151
157
|
| `schema_diff(noteType="Task")` | Detect drift between schema and actual task notes |
|
|
152
158
|
|
|
153
|
-
> **Plugin tools vs BM CLI**: The `schema_validate`, `schema_infer`, and `schema_diff` tools are available when using the `@openclaw/basic-memory` plugin. The raw `search_notes(...)` queries also work and are useful for ad-hoc filtering. `memory_search` composites results from all sources including active task scanning.
|
|
154
|
-
|
|
155
159
|
## Guidelines
|
|
156
160
|
|
|
157
161
|
- **One task per unit of work** — Don't cram multiple projects into one task
|
|
@@ -159,4 +163,4 @@ With BM's schema system, tasks are fully queryable:
|
|
|
159
163
|
- **Context > steps** — Steps tell you what to do; context tells you why and how
|
|
160
164
|
- **Close finished tasks** — Don't leave completed work as `active`
|
|
161
165
|
- **Link related tasks** — Use `parent_task [[X]]` or relations to connect related work
|
|
162
|
-
- **Schema validation is your friend** — Run `
|
|
166
|
+
- **Schema validation is your friend** — Run `schema_validate(noteType="Task")` periodically to catch incomplete tasks
|
package/tools/write-note.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox"
|
|
2
2
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
|
|
3
3
|
import type { BmClient } from "../bm-client.ts"
|
|
4
|
+
import { NoteAlreadyExistsError } from "../bm-client.ts"
|
|
4
5
|
import { log } from "../logger.ts"
|
|
5
6
|
|
|
6
7
|
export function registerWriteTool(
|
|
@@ -12,9 +13,9 @@ export function registerWriteTool(
|
|
|
12
13
|
name: "write_note",
|
|
13
14
|
label: "Write Note",
|
|
14
15
|
description:
|
|
15
|
-
"Create
|
|
16
|
-
"
|
|
17
|
-
"
|
|
16
|
+
"Create a note in the Basic Memory knowledge graph. " +
|
|
17
|
+
"If the note already exists, returns an error by default. " +
|
|
18
|
+
"Pass overwrite=true to replace, or use edit_note for incremental updates.",
|
|
18
19
|
parameters: Type.Object({
|
|
19
20
|
title: Type.String({ description: "Note title" }),
|
|
20
21
|
content: Type.String({
|
|
@@ -26,6 +27,12 @@ export function registerWriteTool(
|
|
|
26
27
|
description: "Target project name (defaults to current project)",
|
|
27
28
|
}),
|
|
28
29
|
),
|
|
30
|
+
overwrite: Type.Optional(
|
|
31
|
+
Type.Boolean({
|
|
32
|
+
description:
|
|
33
|
+
"Set to true to replace an existing note. Defaults to false.",
|
|
34
|
+
}),
|
|
35
|
+
),
|
|
29
36
|
}),
|
|
30
37
|
async execute(
|
|
31
38
|
_toolCallId: string,
|
|
@@ -34,6 +41,7 @@ export function registerWriteTool(
|
|
|
34
41
|
content: string
|
|
35
42
|
folder: string
|
|
36
43
|
project?: string
|
|
44
|
+
overwrite?: boolean
|
|
37
45
|
},
|
|
38
46
|
) {
|
|
39
47
|
log.debug(`write_note: title=${params.title} folder=${params.folder}`)
|
|
@@ -44,6 +52,7 @@ export function registerWriteTool(
|
|
|
44
52
|
params.content,
|
|
45
53
|
params.folder,
|
|
46
54
|
params.project,
|
|
55
|
+
params.overwrite,
|
|
47
56
|
)
|
|
48
57
|
|
|
49
58
|
const msg = `Note saved: ${note.title} (${note.permalink})`
|
|
@@ -61,6 +70,22 @@ export function registerWriteTool(
|
|
|
61
70
|
},
|
|
62
71
|
}
|
|
63
72
|
} catch (err) {
|
|
73
|
+
if (err instanceof NoteAlreadyExistsError) {
|
|
74
|
+
const hint = [
|
|
75
|
+
`Note already exists: "${params.title}" (${err.permalink})`,
|
|
76
|
+
"",
|
|
77
|
+
"To update this note, use one of:",
|
|
78
|
+
` - edit_note("${err.permalink}", operation="append", content="...")`,
|
|
79
|
+
` - edit_note("${err.permalink}", operation="replace_section", section="...", content="...")`,
|
|
80
|
+
` - write_note("${params.title}", ..., overwrite=true) to fully replace`,
|
|
81
|
+
` - read_note("${err.permalink}") to inspect current content first`,
|
|
82
|
+
].join("\n")
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: "text" as const, text: hint }],
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
64
89
|
log.error("write_note failed", err)
|
|
65
90
|
return {
|
|
66
91
|
content: [
|