@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
|
@@ -17,7 +17,10 @@ tags: [api, architecture, decisions]
|
|
|
17
17
|
|
|
18
18
|
# API Design Decisions
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
The API team evaluated multiple approaches for the public API during Q1. After
|
|
21
|
+
prototyping both REST and GraphQL, the team chose REST due to broader ecosystem
|
|
22
|
+
support and simpler caching semantics. This note captures the key decisions and
|
|
23
|
+
their rationale, along with open questions still to resolve.
|
|
21
24
|
|
|
22
25
|
## Observations
|
|
23
26
|
- [decision] Use REST over GraphQL for simplicity #api
|
|
@@ -45,17 +48,20 @@ permalink: custom-path # optional — auto-generated from title if omitted
|
|
|
45
48
|
|
|
46
49
|
- The `title` must match the `# Heading` in the body
|
|
47
50
|
- Tags are searchable and help with discovery
|
|
48
|
-
- Custom `type` values (Task, Meeting, Person, etc.) work with the schema system
|
|
49
|
-
- The `permalink` is auto-generated from the title
|
|
51
|
+
- Custom `type` values (Task, Meeting, Person, etc.) work with the schema system. See the **memory-schema** skill for defining schemas, validating notes against them, and detecting drift.
|
|
52
|
+
- The `permalink` is auto-generated from the `title` and `directory`. For example, title "API Design Decisions" in directory "specs" produces permalink `specs/api-design-decisions` and memory URL `memory://specs/api-design-decisions`. If no directory is specified, the permalink is just the kebab-cased title. Permalinks stay stable across file moves. You rarely need to set one manually.
|
|
53
|
+
|
|
54
|
+
> **Note:** When using `write_note`, you don't write frontmatter yourself. The `title`, `tags`, `note_type`, and `metadata` are separate parameters — Basic Memory generates the frontmatter automatically. Your `content` parameter is just the markdown body starting with `# Heading`.
|
|
50
55
|
|
|
51
56
|
### Body / Context
|
|
52
57
|
|
|
53
|
-
Free-form markdown between the heading and the Observations section.
|
|
54
|
-
- Background and
|
|
55
|
-
-
|
|
56
|
-
-
|
|
58
|
+
Free-form markdown between the heading and the Observations section. This is the heart of the note — write generously here:
|
|
59
|
+
- Background, motivation, and history
|
|
60
|
+
- Detailed explanation of what happened and why it matters
|
|
61
|
+
- Analysis, reasoning, and trade-offs considered
|
|
62
|
+
- Context that someone (or an AI) needs to understand this note later
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
Write complete, substantive prose. Basic Memory's search retrieves relevant chunks from note bodies, so longer, richer context makes notes more discoverable and more useful when found. Don't reduce everything to bullet points — tell the story.
|
|
59
65
|
|
|
60
66
|
## Observations
|
|
61
67
|
|
|
@@ -173,33 +179,88 @@ The first path segment is matched against known project names. If it matches, it
|
|
|
173
179
|
|
|
174
180
|
Memory URLs work with `build_context` to assemble related knowledge by traversing relations:
|
|
175
181
|
|
|
176
|
-
```
|
|
177
|
-
|
|
182
|
+
```python
|
|
183
|
+
# Get a note and its connected context
|
|
178
184
|
build_context(url="memory://api-design-decisions")
|
|
179
185
|
|
|
180
|
-
|
|
186
|
+
# Wildcard — gather all docs
|
|
181
187
|
build_context(url="memory://docs/*")
|
|
182
188
|
|
|
183
|
-
|
|
189
|
+
# Direct read by permalink
|
|
184
190
|
read_note(identifier="memory://api-design-decisions")
|
|
185
191
|
```
|
|
186
192
|
|
|
193
|
+
## Before Creating a Note
|
|
194
|
+
|
|
195
|
+
Always search Basic Memory before creating a new note. Duplicates fragment your knowledge graph — updating an existing note is almost always better than creating a second one.
|
|
196
|
+
|
|
197
|
+
### Search with Multiple Variations
|
|
198
|
+
|
|
199
|
+
A single search often misses. Try the full name, abbreviations, acronyms, and keywords:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# Searching for an entity that might already exist
|
|
203
|
+
search_notes(query="Kubernetes Migration")
|
|
204
|
+
search_notes(query="k8s migration")
|
|
205
|
+
search_notes(query="container migration")
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
For people, try full name and last name. For organizations, try the full name and common abbreviations.
|
|
209
|
+
|
|
210
|
+
### Decision Tree
|
|
211
|
+
|
|
212
|
+
- **Entity exists** → Update it with `edit_note` (append observations, add relations, find-and-replace outdated info)
|
|
213
|
+
- **Entity doesn't exist** → Create it with `write_note`
|
|
214
|
+
- **Unsure if it's the same entity** → Read the existing note first, then decide
|
|
215
|
+
|
|
216
|
+
### Granular Updates with `edit_note`
|
|
217
|
+
|
|
218
|
+
When a note already exists, make targeted edits instead of rewriting the whole file:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
# Append a new observation to an existing note
|
|
222
|
+
edit_note(
|
|
223
|
+
identifier="API Design Decisions",
|
|
224
|
+
operation="append",
|
|
225
|
+
section="Observations",
|
|
226
|
+
content="- [decision] Switched to OpenAPI 3.1 for spec generation #api"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Fix outdated information
|
|
230
|
+
edit_note(
|
|
231
|
+
identifier="API Design Decisions",
|
|
232
|
+
operation="find_replace",
|
|
233
|
+
find_text="- [status] draft",
|
|
234
|
+
content="- [status] approved"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Add a new relation
|
|
238
|
+
edit_note(
|
|
239
|
+
identifier="API Design Decisions",
|
|
240
|
+
operation="append",
|
|
241
|
+
section="Relations",
|
|
242
|
+
content="- depends_on [[Rate Limiter]]"
|
|
243
|
+
)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
This preserves existing content and keeps the edit history clean.
|
|
247
|
+
|
|
187
248
|
## Writing Notes with Tools
|
|
188
249
|
|
|
189
250
|
### Creating a Note
|
|
190
251
|
|
|
191
|
-
```
|
|
252
|
+
```python
|
|
192
253
|
write_note(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
tags: [api, architecture]
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
# API Design Decisions
|
|
254
|
+
title="API Design Decisions",
|
|
255
|
+
directory="architecture",
|
|
256
|
+
tags=["api", "architecture"],
|
|
257
|
+
content="""# API Design Decisions
|
|
201
258
|
|
|
202
|
-
|
|
259
|
+
The API team evaluated REST and GraphQL during Q1 planning. After prototyping
|
|
260
|
+
both approaches, we chose REST for the public API — broader ecosystem support,
|
|
261
|
+
simpler caching with HTTP semantics, and a lower learning curve for external
|
|
262
|
+
consumers. GraphQL remains an option for internal services where query
|
|
263
|
+
flexibility matters more.
|
|
203
264
|
|
|
204
265
|
## Observations
|
|
205
266
|
- [decision] Use REST for public API #api
|
|
@@ -207,37 +268,52 @@ Context about the API design process.
|
|
|
207
268
|
|
|
208
269
|
## Relations
|
|
209
270
|
- implements [[API Specification]]
|
|
210
|
-
- relates_to [[Backend Architecture]]"
|
|
271
|
+
- relates_to [[Backend Architecture]]"""
|
|
211
272
|
)
|
|
212
273
|
```
|
|
213
274
|
|
|
275
|
+
Basic Memory auto-generates frontmatter (including the permalink and memory URL) from the parameters. This note would get permalink `architecture/api-design-decisions` and be addressable at `memory://architecture/api-design-decisions`.
|
|
276
|
+
|
|
214
277
|
### Editing an Existing Note
|
|
215
278
|
|
|
216
279
|
Use `edit_note` to append, prepend, or find-and-replace within a note:
|
|
217
280
|
|
|
218
|
-
```
|
|
219
|
-
|
|
281
|
+
```python
|
|
282
|
+
# Append new observations
|
|
220
283
|
edit_note(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
284
|
+
identifier="API Design Decisions",
|
|
285
|
+
operation="append",
|
|
286
|
+
section="Observations",
|
|
287
|
+
content="- [decision] Use OpenAPI 3.1 for spec generation #api"
|
|
225
288
|
)
|
|
226
289
|
|
|
227
|
-
|
|
290
|
+
# Add a new relation
|
|
228
291
|
edit_note(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
292
|
+
identifier="API Design Decisions",
|
|
293
|
+
operation="append",
|
|
294
|
+
section="Relations",
|
|
295
|
+
content="- depends_on [[Rate Limiter]]"
|
|
233
296
|
)
|
|
234
297
|
```
|
|
235
298
|
|
|
299
|
+
### Moving a Note
|
|
300
|
+
|
|
301
|
+
Use `move_note` to reorganize notes into different directories:
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
move_note(
|
|
305
|
+
identifier="API Design Decisions",
|
|
306
|
+
destination_path="archive/api-design-decisions.md"
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The permalink stays the same after a move, so all `[[wiki-links]]` and `memory://` URLs continue to resolve.
|
|
311
|
+
|
|
236
312
|
## Best Practices
|
|
237
313
|
|
|
238
314
|
1. **Start with context.** Before listing observations, explain *why* this note exists. Future-you (or your AI collaborator) will thank you.
|
|
239
315
|
|
|
240
|
-
2. **
|
|
316
|
+
2. **Favor completeness.** Write rich, substantive notes. Basic Memory's search pulls relevant chunks from note bodies, so longer notes with more context are *more* discoverable, not less. Use prose in the body to tell the full story — the background, the reasoning, the nuance. Then distill key facts into `[category] content` observations for structured queries. Both matter: prose gives meaning, observations give precision.
|
|
241
317
|
|
|
242
318
|
3. **Build incrementally.** Add to existing notes rather than creating duplicates. Use `edit_note` to append new observations or relations as you learn more.
|
|
243
319
|
|
|
@@ -19,10 +19,20 @@ Inspired by sleep-time compute — the idea that memory formation happens best *
|
|
|
19
19
|
|
|
20
20
|
### 1. Gather Recent Material
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
Find what changed recently, then read the relevant files:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
# Find recently modified notes — use json format for the complete list
|
|
26
|
+
# (text format truncates to ~5 items in the summary)
|
|
27
|
+
recent_activity(timeframe="2d", output_format="json")
|
|
28
|
+
|
|
29
|
+
# Read specific daily notes
|
|
30
|
+
read_note(identifier="memory/2026-02-27")
|
|
31
|
+
read_note(identifier="memory/2026-02-26")
|
|
32
|
+
|
|
33
|
+
# Check active tasks
|
|
34
|
+
search_notes(note_types=["task"], status="active")
|
|
35
|
+
```
|
|
26
36
|
|
|
27
37
|
### 2. Evaluate What Matters
|
|
28
38
|
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory-research
|
|
3
|
+
description: "Research an external subject using web search, synthesize findings into a structured Basic Memory entity. Use when asked to research a company, person, technology, or topic — or when a bare name or URL is provided that implies a research request."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Research
|
|
7
|
+
|
|
8
|
+
Research an external subject, synthesize what you find, and create a structured Basic Memory entity — with the user's approval.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
**Explicit triggers:**
|
|
13
|
+
- "Research [subject]"
|
|
14
|
+
- "Look up [subject]"
|
|
15
|
+
- "What do you know about [subject]?"
|
|
16
|
+
- "Evaluate [subject]"
|
|
17
|
+
|
|
18
|
+
**Implicit triggers (also activate this skill):**
|
|
19
|
+
- A bare name: "Terraform"
|
|
20
|
+
- A URL: "https://example.com"
|
|
21
|
+
- A name with context: "Acme Corp — saw them at the conference"
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
### Step 1: Web Research
|
|
26
|
+
|
|
27
|
+
Search for current information across multiple sources. Aim for 3-5 searches to build a well-rounded picture:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
[subject name] site
|
|
31
|
+
[subject name] overview
|
|
32
|
+
[subject name] news [current year]
|
|
33
|
+
[subject name] [relevant domain keywords]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**What to gather by entity type:**
|
|
37
|
+
|
|
38
|
+
| Entity Type | Key Information |
|
|
39
|
+
|-------------|----------------|
|
|
40
|
+
| **Organization** | What they do, products/services, stage (startup/growth/public), funding, leadership, headquarters, employee count, notable partnerships or contracts |
|
|
41
|
+
| **Person** | Current role, organization, background, expertise, notable work, public presence |
|
|
42
|
+
| **Technology** | What it does, who maintains it, maturity, ecosystem, alternatives, adoption |
|
|
43
|
+
| **Topic/Domain** | Definition, current state, key players, trends, relevance to user's context |
|
|
44
|
+
|
|
45
|
+
### Step 2: Check Existing Knowledge
|
|
46
|
+
|
|
47
|
+
Before proposing a new entity, search Basic Memory:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
search_notes(query="Acme Corp")
|
|
51
|
+
search_notes(query="acme")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Try name variations — full name, abbreviation, acronym, domain name.
|
|
55
|
+
|
|
56
|
+
If the entity already exists:
|
|
57
|
+
- Report what you found in Basic Memory alongside your web research
|
|
58
|
+
- Offer to update the existing note with new information
|
|
59
|
+
- Use `edit_note` to append new observations or update outdated ones
|
|
60
|
+
|
|
61
|
+
If the entity doesn't exist, proceed to evaluation.
|
|
62
|
+
|
|
63
|
+
### Step 3: Evaluate and Summarize
|
|
64
|
+
|
|
65
|
+
Present your findings in a structured summary. Include all relevant information organized by section:
|
|
66
|
+
|
|
67
|
+
```markdown
|
|
68
|
+
## [Subject Name]
|
|
69
|
+
|
|
70
|
+
**Type:** [Organization / Person / Technology / Topic]
|
|
71
|
+
|
|
72
|
+
**Summary:** [2-4 sentences: what this is, why it matters, key distinguishing facts]
|
|
73
|
+
|
|
74
|
+
**Key Details:**
|
|
75
|
+
- [Organized by what's relevant for the entity type]
|
|
76
|
+
- [Stage, funding, leadership for orgs]
|
|
77
|
+
- [Role, expertise, affiliations for people]
|
|
78
|
+
- [Maturity, ecosystem, alternatives for tech]
|
|
79
|
+
|
|
80
|
+
**Relevance:** [Why this matters to the user — connection to their work, domain, or interests.
|
|
81
|
+
If no obvious connection: "No specific connection identified."]
|
|
82
|
+
|
|
83
|
+
**Sources:**
|
|
84
|
+
- [URLs of key sources consulted]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Evaluation Guidelines
|
|
88
|
+
|
|
89
|
+
**Use hedging language.** Web research is a snapshot, not ground truth:
|
|
90
|
+
- "Appears to be", "Based on public information", "Estimated"
|
|
91
|
+
- "As of [date]", "According to [source]"
|
|
92
|
+
- Never state funding amounts, employee counts, or revenue as exact unless citing a primary source
|
|
93
|
+
|
|
94
|
+
**Don't fabricate.** If information isn't available, say so:
|
|
95
|
+
- "Leadership information not publicly available"
|
|
96
|
+
- "Funding details not disclosed"
|
|
97
|
+
|
|
98
|
+
**Let the user define relevance.** Don't impose a fixed evaluation framework. Instead, highlight facts and let the user draw conclusions. If the user has a specific evaluation rubric (strategic fit, buy/partner/compete, etc.), they'll tell you — apply it when asked.
|
|
99
|
+
|
|
100
|
+
### Step 4: Propose Entity Creation
|
|
101
|
+
|
|
102
|
+
After presenting the summary, ask for approval:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
Create Basic Memory entity for [Subject]?
|
|
106
|
+
Location: [suggested-folder]/[entity-name].md
|
|
107
|
+
Type: [entity type]
|
|
108
|
+
|
|
109
|
+
[yes / no / modify]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If the user provided context with their request ("saw them at the conference"), include that context in the proposed entity.
|
|
113
|
+
|
|
114
|
+
### Step 5: Create the Entity
|
|
115
|
+
|
|
116
|
+
After approval, create a structured note. Adapt the template to the entity type:
|
|
117
|
+
|
|
118
|
+
#### Organization
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
write_note(
|
|
122
|
+
title="Acme Corp",
|
|
123
|
+
directory="organizations",
|
|
124
|
+
note_type="organization",
|
|
125
|
+
tags=["organization", "relevant-tags"],
|
|
126
|
+
content="""# Acme Corp
|
|
127
|
+
|
|
128
|
+
## Overview
|
|
129
|
+
[2-3 sentence description from research]
|
|
130
|
+
|
|
131
|
+
## Products & Services
|
|
132
|
+
- [Key offerings discovered in research]
|
|
133
|
+
|
|
134
|
+
## Background
|
|
135
|
+
**Stage:** [Startup / Growth / Public]
|
|
136
|
+
**Headquarters:** [Location]
|
|
137
|
+
**Employees:** [Estimate, hedged]
|
|
138
|
+
**Leadership:** [Key people if found]
|
|
139
|
+
**Founded:** [Year if found]
|
|
140
|
+
|
|
141
|
+
## Observations
|
|
142
|
+
- [relevance] Why this entity matters in user's context
|
|
143
|
+
- [source] Researched on YYYY-MM-DD
|
|
144
|
+
- [additional observations from research findings]
|
|
145
|
+
|
|
146
|
+
## Relations
|
|
147
|
+
- [Link to related entities already in the knowledge graph]"""
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Person
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
write_note(
|
|
155
|
+
title="Jane Smith",
|
|
156
|
+
directory="people",
|
|
157
|
+
note_type="person",
|
|
158
|
+
tags=["person", "relevant-tags"],
|
|
159
|
+
content="""# Jane Smith
|
|
160
|
+
|
|
161
|
+
## Overview
|
|
162
|
+
[Current role and affiliation. Brief background.]
|
|
163
|
+
|
|
164
|
+
## Background
|
|
165
|
+
**Role:** [Title at Organization]
|
|
166
|
+
**Expertise:** [Key domains]
|
|
167
|
+
**Notable:** [Publications, talks, projects if found]
|
|
168
|
+
|
|
169
|
+
## Observations
|
|
170
|
+
- [role] Title at Organization
|
|
171
|
+
- [expertise] Key technical or domain expertise
|
|
172
|
+
- [source] Researched on YYYY-MM-DD
|
|
173
|
+
|
|
174
|
+
## Relations
|
|
175
|
+
- works_at [[Organization]]"""
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Technology
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
write_note(
|
|
183
|
+
title="Technology Name",
|
|
184
|
+
directory="concepts",
|
|
185
|
+
note_type="concept",
|
|
186
|
+
tags=["concept", "technology", "relevant-tags"],
|
|
187
|
+
content="""# Technology Name
|
|
188
|
+
|
|
189
|
+
## Overview
|
|
190
|
+
[What it is and what problem it solves]
|
|
191
|
+
|
|
192
|
+
## Key Details
|
|
193
|
+
**Maintained by:** [Organization or community]
|
|
194
|
+
**Maturity:** [Experimental / Stable / Mature]
|
|
195
|
+
**License:** [If applicable]
|
|
196
|
+
**Alternatives:** [Comparable tools or approaches]
|
|
197
|
+
|
|
198
|
+
## Observations
|
|
199
|
+
- [definition] What this technology does in one sentence
|
|
200
|
+
- [maturity] Current state and adoption level
|
|
201
|
+
- [source] Researched on YYYY-MM-DD
|
|
202
|
+
|
|
203
|
+
## Relations
|
|
204
|
+
- [Link to related concepts, tools, or projects in the knowledge graph]"""
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Adapt these templates freely. The key elements are: note_type/tags parameters, an overview, structured details, observations with categories, and relations.
|
|
209
|
+
|
|
210
|
+
### Step 6: Store Source Context
|
|
211
|
+
|
|
212
|
+
If the user provided context with their request, capture it in the entity:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
# User said: "Acme Corp — saw their demo at the conference last week"
|
|
216
|
+
edit_note(
|
|
217
|
+
identifier="Acme Corp",
|
|
218
|
+
operation="append",
|
|
219
|
+
section="Observations",
|
|
220
|
+
content="- [context] Saw their demo at conference, week of 2026-02-17"
|
|
221
|
+
)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
This context is often the most valuable part — it's the user's relationship to the entity, which web research can't provide.
|
|
225
|
+
|
|
226
|
+
## Guidelines
|
|
227
|
+
|
|
228
|
+
- **Always web search.** Don't rely on training data alone. Research should reflect current, verifiable information.
|
|
229
|
+
- **Search Basic Memory first.** Check for existing entities before creating new ones. Update rather than duplicate.
|
|
230
|
+
- **Hedge uncertain information.** Use qualifiers for estimates, unverified claims, and inferred details.
|
|
231
|
+
- **Store source URLs.** Include the URLs you consulted, either in observations or a Sources section. This enables the user to verify and dig deeper.
|
|
232
|
+
- **Get approval before creating.** Present your findings and let the user decide whether to create the entity and what to include.
|
|
233
|
+
- **Capture user context.** If the user told you *why* they're researching (met at a conference, evaluating as a vendor, etc.), that context belongs in the entity.
|
|
234
|
+
- **Don't over-research.** 3-5 web searches is usually enough. The goal is a useful knowledge graph entry, not an exhaustive report.
|
|
235
|
+
- **Link to existing knowledge.** Relate the new entity to things already in the knowledge graph. Connections compound value.
|
|
@@ -113,9 +113,9 @@ Look for clusters of notes that share structure but have no schema:
|
|
|
113
113
|
1. **Search by type**: `search_notes(query="type:Meeting")` — if many notes share a `type` but no `schema/Meeting.md` exists, it's a candidate.
|
|
114
114
|
|
|
115
115
|
2. **Infer a schema**: Use `schema_infer` to analyze existing notes and generate a suggested schema:
|
|
116
|
-
```
|
|
116
|
+
```python
|
|
117
117
|
schema_infer(noteType="Meeting")
|
|
118
|
-
schema_infer(noteType="Meeting", threshold=0.5)
|
|
118
|
+
schema_infer(noteType="Meeting", threshold=0.5) # fields in 50%+ of notes
|
|
119
119
|
```
|
|
120
120
|
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.
|
|
121
121
|
|
|
@@ -125,57 +125,57 @@ Look for clusters of notes that share structure but have no schema:
|
|
|
125
125
|
|
|
126
126
|
Write the schema note to `schema/<EntityName>`:
|
|
127
127
|
|
|
128
|
-
```
|
|
128
|
+
```python
|
|
129
129
|
write_note(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
settings:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
# Meeting
|
|
130
|
+
title="Meeting",
|
|
131
|
+
directory="schema",
|
|
132
|
+
note_type="schema",
|
|
133
|
+
metadata={
|
|
134
|
+
"entity": "Meeting",
|
|
135
|
+
"version": 1,
|
|
136
|
+
"schema": {
|
|
137
|
+
"topic": "string, what was discussed",
|
|
138
|
+
"date": "string, when it happened",
|
|
139
|
+
"attendees?(array)": "Person, who attended",
|
|
140
|
+
"decisions?(array)": "string, decisions made"
|
|
141
|
+
},
|
|
142
|
+
"settings": {"validation": "warn"}
|
|
143
|
+
},
|
|
144
|
+
content="""# Meeting
|
|
147
145
|
|
|
148
146
|
Schema for meeting notes.
|
|
149
147
|
|
|
150
148
|
## Observations
|
|
151
149
|
- [convention] Meeting notes live in memory/meetings/ or as daily entries
|
|
152
150
|
- [convention] Always include date and topic
|
|
153
|
-
- [convention] Action items should become tasks when complex"
|
|
151
|
+
- [convention] Action items should become tasks when complex"""
|
|
154
152
|
)
|
|
155
153
|
```
|
|
156
154
|
|
|
157
155
|
### Key Principles
|
|
158
156
|
|
|
159
157
|
- **Schema notes live in `schema/`** — one note per entity type
|
|
160
|
-
- **`
|
|
161
|
-
- **`entity: Meeting`** names the type it applies to
|
|
162
|
-
- **`version: 1`** — increment when making breaking changes
|
|
158
|
+
- **`note_type="schema"`** marks it as a schema definition
|
|
159
|
+
- **`entity: Meeting`** in metadata names the type it applies to
|
|
160
|
+
- **`version: 1`** in metadata — increment when making breaking changes
|
|
163
161
|
- **`settings.validation: warn`** is recommended to start — it logs issues without blocking writes
|
|
164
162
|
|
|
165
163
|
## Validating Notes
|
|
166
164
|
|
|
167
165
|
Check how well existing notes conform to their schema:
|
|
168
166
|
|
|
169
|
-
```
|
|
170
|
-
|
|
167
|
+
```python
|
|
168
|
+
# Validate all notes of a type
|
|
171
169
|
schema_validate(noteType="Meeting")
|
|
172
170
|
|
|
173
|
-
|
|
171
|
+
# Validate a single note
|
|
174
172
|
schema_validate(identifier="meetings/2026-02-10-standup")
|
|
175
173
|
```
|
|
176
174
|
|
|
175
|
+
**Important:** `schema_validate` 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).
|
|
176
|
+
|
|
177
177
|
Validation reports:
|
|
178
|
-
- **Missing required fields** — the note lacks a field the schema requires
|
|
178
|
+
- **Missing required fields** — the note lacks a field the schema requires (as an observation category)
|
|
179
179
|
- **Unknown fields** — the note has fields the schema doesn't define
|
|
180
180
|
- **Type mismatches** — a field value doesn't match the expected type
|
|
181
181
|
- **Invalid enum values** — a value isn't in the allowed set
|
|
@@ -189,7 +189,7 @@ Validation reports:
|
|
|
189
189
|
|
|
190
190
|
Over time, notes evolve and schemas lag behind. Use `schema_diff` to find divergence:
|
|
191
191
|
|
|
192
|
-
```
|
|
192
|
+
```python
|
|
193
193
|
schema_diff(noteType="Meeting")
|
|
194
194
|
```
|
|
195
195
|
|
|
@@ -204,13 +204,13 @@ When note structure changes:
|
|
|
204
204
|
|
|
205
205
|
1. **Run diff** to see current state: `schema_diff(noteType="Meeting")`
|
|
206
206
|
2. **Update the schema note** via `edit_note`:
|
|
207
|
-
```
|
|
207
|
+
```python
|
|
208
208
|
edit_note(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
209
|
+
identifier="schema/Meeting",
|
|
210
|
+
operation="find_replace",
|
|
211
|
+
find_text="version: 1",
|
|
212
|
+
content="version: 2",
|
|
213
|
+
expected_replacements=1
|
|
214
214
|
)
|
|
215
215
|
```
|
|
216
216
|
3. **Add/remove/modify fields** in the `schema:` block
|