@basicmemory/openclaw-basic-memory 0.2.3 → 0.23.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 +49 -19
- package/bm-client.ts +55 -21
- package/commands/cli.ts +1 -2
- package/commands/skills.ts +71 -10
- package/commands/slash.ts +4 -2
- package/config.ts +0 -16
- package/context-engine/basic-memory-context-engine.ts +22 -90
- package/dist/bm-client.d.ts +179 -0
- package/dist/bm-client.js +582 -0
- package/dist/commands/cli.d.ts +4 -0
- package/dist/commands/cli.js +116 -0
- package/dist/commands/skills.d.ts +2 -0
- package/dist/commands/skills.js +86 -0
- package/dist/commands/slash.d.ts +3 -0
- package/dist/commands/slash.js +92 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +99 -0
- package/dist/context-engine/basic-memory-context-engine.d.ts +34 -0
- package/dist/context-engine/basic-memory-context-engine.js +176 -0
- package/dist/hooks/capture.d.ts +24 -0
- package/dist/hooks/capture.js +88 -0
- package/dist/hooks/recall.d.ts +24 -0
- package/dist/hooks/recall.js +56 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +135 -0
- package/dist/logger.d.ts +13 -0
- package/dist/logger.js +34 -0
- package/dist/schema/conversation-schema.d.ts +5 -0
- package/dist/schema/conversation-schema.js +33 -0
- package/dist/schema/task-schema.d.ts +5 -0
- package/dist/schema/task-schema.js +34 -0
- package/dist/tools/build-context.d.ts +3 -0
- package/dist/tools/build-context.js +100 -0
- package/dist/tools/delete-note.d.ts +3 -0
- package/dist/tools/delete-note.js +52 -0
- package/dist/tools/edit-note.d.ts +3 -0
- package/dist/tools/edit-note.js +81 -0
- package/dist/tools/list-memory-projects.d.ts +3 -0
- package/dist/tools/list-memory-projects.js +86 -0
- package/dist/tools/list-workspaces.d.ts +3 -0
- package/dist/tools/list-workspaces.js +64 -0
- package/dist/tools/memory-provider.d.ts +15 -0
- package/dist/tools/memory-provider.js +274 -0
- package/dist/tools/move-note.d.ts +3 -0
- package/dist/tools/move-note.js +56 -0
- package/dist/tools/read-note.d.ts +3 -0
- package/dist/tools/read-note.js +57 -0
- package/dist/tools/schema-diff.d.ts +3 -0
- package/dist/tools/schema-diff.js +81 -0
- package/dist/tools/schema-infer.d.ts +3 -0
- package/dist/tools/schema-infer.js +66 -0
- package/dist/tools/schema-validate.d.ts +3 -0
- package/dist/tools/schema-validate.js +73 -0
- package/dist/tools/search-notes.d.ts +3 -0
- package/dist/tools/search-notes.js +97 -0
- package/dist/tools/write-note.d.ts +3 -0
- package/dist/tools/write-note.js +80 -0
- package/index.ts +27 -17
- package/openclaw.plugin.json +27 -20
- package/package.json +27 -8
- package/scripts/fetch-skills.ts +62 -67
- package/scripts/setup-bm.sh +27 -10
- package/skills/manifest.json +25 -0
- package/skills/memory-capture/SKILL.md +275 -0
- package/skills/memory-ci-capture/SKILL.md +56 -0
- package/skills/memory-continue/SKILL.md +148 -0
- package/skills/memory-curate/SKILL.md +243 -0
- package/skills/memory-literary-analysis/SKILL.md +15 -12
- package/skills/memory-notes/SKILL.md +22 -6
- package/skills/memory-onboarding/SKILL.md +142 -0
- package/skills/memory-onboarding/evals/evals.json +29 -0
- package/skills/memory-onboarding/references/assistant-setup.md +64 -0
- package/skills/memory-onboarding/references/conventions.md +118 -0
- package/skills/memory-onboarding/references/domain-playbooks.md +159 -0
- package/skills/memory-onboarding/references/schema-guide.md +136 -0
- package/skills/memory-schema/SKILL.md +7 -5
- package/skills/memory-tasks/SKILL.md +1 -1
- package/tools/build-context.ts +1 -1
- package/tools/delete-note.ts +1 -1
- package/tools/edit-note.ts +1 -1
- package/tools/list-memory-projects.ts +3 -2
- package/tools/list-workspaces.ts +1 -1
- package/tools/memory-provider.ts +1 -1
- package/tools/move-note.ts +1 -1
- package/tools/read-note.ts +1 -1
- package/tools/schema-diff.ts +1 -1
- package/tools/schema-infer.ts +1 -1
- package/tools/schema-validate.ts +1 -1
- package/tools/search-notes.ts +1 -1
- package/tools/write-note.ts +1 -1
- package/context-engine/basic-memory-context-engine.test.ts +0 -398
package/README.md
CHANGED
|
@@ -18,6 +18,14 @@ Over time, your agent builds a knowledge base that grows with you. Context that
|
|
|
18
18
|
|
|
19
19
|
Learn more: [basicmemory.com](https://basicmemory.com) · [GitHub](https://github.com/basicmachines-co/basic-memory) · [Docs](https://docs.basicmemory.com)
|
|
20
20
|
|
|
21
|
+
Source now lives in the canonical [`basic-memory`](https://github.com/basicmachines-co/basic-memory) repository under `integrations/openclaw/`. The npm package remains `@basicmemory/openclaw-basic-memory`.
|
|
22
|
+
|
|
23
|
+
Maintainer check from the monorepo root:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
just package-check-openclaw
|
|
27
|
+
```
|
|
28
|
+
|
|
21
29
|
## What this plugin does
|
|
22
30
|
|
|
23
31
|
This plugin connects Basic Memory to OpenClaw so your agent can:
|
|
@@ -45,6 +53,7 @@ Then install the plugin:
|
|
|
45
53
|
|
|
46
54
|
```bash
|
|
47
55
|
openclaw plugins install @basicmemory/openclaw-basic-memory
|
|
56
|
+
openclaw plugins enable openclaw-basic-memory --slot memory
|
|
48
57
|
openclaw gateway restart
|
|
49
58
|
```
|
|
50
59
|
|
|
@@ -54,7 +63,8 @@ Verify:
|
|
|
54
63
|
|
|
55
64
|
```bash
|
|
56
65
|
openclaw plugins list
|
|
57
|
-
openclaw plugins
|
|
66
|
+
openclaw plugins inspect openclaw-basic-memory --json
|
|
67
|
+
openclaw plugins doctor
|
|
58
68
|
```
|
|
59
69
|
|
|
60
70
|
## Configuration
|
|
@@ -63,8 +73,15 @@ openclaw plugins info openclaw-basic-memory
|
|
|
63
73
|
|
|
64
74
|
```json5
|
|
65
75
|
{
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
plugins: {
|
|
77
|
+
entries: {
|
|
78
|
+
"openclaw-basic-memory": {
|
|
79
|
+
enabled: true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
slots: {
|
|
83
|
+
memory: "openclaw-basic-memory"
|
|
84
|
+
}
|
|
68
85
|
}
|
|
69
86
|
}
|
|
70
87
|
```
|
|
@@ -75,16 +92,23 @@ This uses sensible defaults: auto-generated project name, maps to your workspace
|
|
|
75
92
|
|
|
76
93
|
```json5
|
|
77
94
|
{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
plugins: {
|
|
96
|
+
entries: {
|
|
97
|
+
"openclaw-basic-memory": {
|
|
98
|
+
enabled: true,
|
|
99
|
+
config: {
|
|
100
|
+
project: "my-agent", // BM project name (default: "openclaw-{hostname}")
|
|
101
|
+
projectPath: ".", // Project directory (default: workspace root)
|
|
102
|
+
memoryDir: "memory/", // Where task notes live
|
|
103
|
+
memoryFile: "MEMORY.md", // Working memory file
|
|
104
|
+
autoCapture: true, // Record conversations as daily notes
|
|
105
|
+
autoRecall: true, // Inject active tasks + recent activity at session start
|
|
106
|
+
debug: false // Verbose logging
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
slots: {
|
|
111
|
+
memory: "openclaw-basic-memory"
|
|
88
112
|
}
|
|
89
113
|
}
|
|
90
114
|
}
|
|
@@ -172,21 +196,27 @@ openclaw basic-memory status
|
|
|
172
196
|
|
|
173
197
|
## Bundled skills
|
|
174
198
|
|
|
175
|
-
|
|
199
|
+
Ten skills ship with the plugin — no installation needed:
|
|
176
200
|
|
|
177
|
-
- **memory-tasks** — structured task tracking that survives context compaction
|
|
178
|
-
- **memory-reflect** — periodic consolidation of recent notes into durable memory
|
|
179
201
|
- **memory-defrag** — cleanup and reorganization of memory files
|
|
180
|
-
- **memory-
|
|
202
|
+
- **memory-ingest** — import existing material into Basic Memory
|
|
203
|
+
- **memory-lifecycle** — manage note/project lifecycle workflows
|
|
204
|
+
- **memory-literary-analysis** — analyze texts and reading notes
|
|
181
205
|
- **memory-metadata-search** — query notes by frontmatter fields
|
|
182
206
|
- **memory-notes** — guidance for writing well-structured notes
|
|
207
|
+
- **memory-reflect** — periodic consolidation of recent notes into durable memory
|
|
208
|
+
- **memory-research** — research synthesis into durable notes
|
|
209
|
+
- **memory-schema** — schema lifecycle (infer, create, validate, diff)
|
|
210
|
+
- **memory-tasks** — structured task tracking that survives context compaction
|
|
183
211
|
|
|
184
212
|
### Updating skills
|
|
185
213
|
|
|
186
214
|
```bash
|
|
187
|
-
|
|
215
|
+
bun run fetch-skills
|
|
188
216
|
```
|
|
189
217
|
|
|
218
|
+
In the monorepo, this copies from the canonical top-level [`skills/`](../../skills) directory into the generated `integrations/openclaw/skills/` package bundle.
|
|
219
|
+
|
|
190
220
|
## Task notes
|
|
191
221
|
|
|
192
222
|
The plugin works well with structured task notes in `memory/tasks/`:
|
|
@@ -238,7 +268,7 @@ Cloud extends local-first — still plain Markdown, still yours. [Start a free t
|
|
|
238
268
|
- [Security](./SECURITY.md) — how auto-installation and data handling work
|
|
239
269
|
- [Development](./DEVELOPMENT.md) — contributing, tests, publishing
|
|
240
270
|
- [Basic Memory docs](https://docs.basicmemory.com)
|
|
241
|
-
- [Issues](https://github.com/basicmachines-co/
|
|
271
|
+
- [Issues](https://github.com/basicmachines-co/basic-memory/issues)
|
|
242
272
|
|
|
243
273
|
## Telemetry
|
|
244
274
|
|
package/bm-client.ts
CHANGED
|
@@ -100,6 +100,7 @@ export interface ProjectListResult {
|
|
|
100
100
|
is_default?: boolean
|
|
101
101
|
isDefault?: boolean
|
|
102
102
|
workspace_name?: string | null
|
|
103
|
+
workspace_slug?: string | null
|
|
103
104
|
workspace_type?: string | null
|
|
104
105
|
workspace_tenant_id?: string | null
|
|
105
106
|
}
|
|
@@ -198,7 +199,7 @@ function isRecoverableConnectionError(err: unknown): boolean {
|
|
|
198
199
|
)
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
function
|
|
202
|
+
function isNoteNotFoundError(err: unknown): boolean {
|
|
202
203
|
const msg = getErrorMessage(err).toLowerCase()
|
|
203
204
|
return (
|
|
204
205
|
msg.includes("entity not found") ||
|
|
@@ -213,6 +214,22 @@ function asString(value: unknown): string | null {
|
|
|
213
214
|
return typeof value === "string" ? value : null
|
|
214
215
|
}
|
|
215
216
|
|
|
217
|
+
function projectMatchesWorkspace(
|
|
218
|
+
project: ProjectListResult,
|
|
219
|
+
workspace: string,
|
|
220
|
+
): boolean {
|
|
221
|
+
const requested = workspace.trim().toLowerCase()
|
|
222
|
+
if (!requested) return true
|
|
223
|
+
|
|
224
|
+
return [
|
|
225
|
+
project.workspace_name,
|
|
226
|
+
project.workspace_slug,
|
|
227
|
+
project.workspace_tenant_id,
|
|
228
|
+
].some(
|
|
229
|
+
(value) => typeof value === "string" && value.toLowerCase() === requested,
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
216
233
|
export class BmClient {
|
|
217
234
|
private bmPath: string
|
|
218
235
|
private project: string
|
|
@@ -465,6 +482,10 @@ export class BmClient {
|
|
|
465
482
|
return structuredPayload
|
|
466
483
|
}
|
|
467
484
|
|
|
485
|
+
private routedProject(project?: string): string {
|
|
486
|
+
return project ?? this.project
|
|
487
|
+
}
|
|
488
|
+
|
|
468
489
|
async ensureProject(projectPath: string): Promise<void> {
|
|
469
490
|
const payload = await this.callTool("create_memory_project", {
|
|
470
491
|
project_name: this.project,
|
|
@@ -491,13 +512,18 @@ export class BmClient {
|
|
|
491
512
|
}
|
|
492
513
|
|
|
493
514
|
async listProjects(workspace?: string): Promise<ProjectListResult[]> {
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const payload = await this.callTool("list_memory_projects", args)
|
|
515
|
+
const payload = await this.callTool("list_memory_projects", {
|
|
516
|
+
output_format: "json",
|
|
517
|
+
})
|
|
498
518
|
|
|
499
519
|
if (isRecord(payload) && Array.isArray(payload.projects)) {
|
|
500
|
-
|
|
520
|
+
const projects = payload.projects as ProjectListResult[]
|
|
521
|
+
if (workspace) {
|
|
522
|
+
return projects.filter((project) =>
|
|
523
|
+
projectMatchesWorkspace(project, workspace),
|
|
524
|
+
)
|
|
525
|
+
}
|
|
526
|
+
return projects
|
|
501
527
|
}
|
|
502
528
|
|
|
503
529
|
throw new Error("invalid list_memory_projects response")
|
|
@@ -519,9 +545,9 @@ export class BmClient {
|
|
|
519
545
|
page: 1,
|
|
520
546
|
page_size: limit,
|
|
521
547
|
output_format: "json",
|
|
548
|
+
project: this.routedProject(project),
|
|
522
549
|
}
|
|
523
550
|
if (query) args.query = query
|
|
524
|
-
if (project) args.project = project
|
|
525
551
|
if (metadata?.filters) args.metadata_filters = metadata.filters
|
|
526
552
|
if (metadata?.tags) args.tags = metadata.tags
|
|
527
553
|
if (metadata?.status) args.status = metadata.status
|
|
@@ -546,8 +572,8 @@ export class BmClient {
|
|
|
546
572
|
identifier,
|
|
547
573
|
include_frontmatter: options.includeFrontmatter === true,
|
|
548
574
|
output_format: "json",
|
|
575
|
+
project: this.routedProject(project),
|
|
549
576
|
}
|
|
550
|
-
if (project) args.project = project
|
|
551
577
|
|
|
552
578
|
const payload = await this.callTool("read_note", args)
|
|
553
579
|
|
|
@@ -585,8 +611,8 @@ export class BmClient {
|
|
|
585
611
|
content,
|
|
586
612
|
directory: folder,
|
|
587
613
|
output_format: "json",
|
|
614
|
+
project: this.routedProject(project),
|
|
588
615
|
}
|
|
589
|
-
if (project) args.project = project
|
|
590
616
|
if (overwrite !== undefined) args.overwrite = overwrite
|
|
591
617
|
|
|
592
618
|
const payload = await this.callTool("write_note", args)
|
|
@@ -632,8 +658,8 @@ export class BmClient {
|
|
|
632
658
|
url,
|
|
633
659
|
depth,
|
|
634
660
|
output_format: "json",
|
|
661
|
+
project: this.routedProject(project),
|
|
635
662
|
}
|
|
636
|
-
if (project) args.project = project
|
|
637
663
|
|
|
638
664
|
const payload = await this.callTool("build_context", args)
|
|
639
665
|
|
|
@@ -651,8 +677,8 @@ export class BmClient {
|
|
|
651
677
|
const args: Record<string, unknown> = {
|
|
652
678
|
timeframe,
|
|
653
679
|
output_format: "json",
|
|
680
|
+
project: this.routedProject(project),
|
|
654
681
|
}
|
|
655
|
-
if (project) args.project = project
|
|
656
682
|
|
|
657
683
|
const payload = await this.callTool("recent_activity", args)
|
|
658
684
|
|
|
@@ -675,12 +701,12 @@ export class BmClient {
|
|
|
675
701
|
operation,
|
|
676
702
|
content,
|
|
677
703
|
output_format: "json",
|
|
704
|
+
project: this.routedProject(project),
|
|
678
705
|
}
|
|
679
706
|
if (options.find_text) args.find_text = options.find_text
|
|
680
707
|
if (options.section) args.section = options.section
|
|
681
708
|
if (options.expected_replacements != null)
|
|
682
709
|
args.expected_replacements = options.expected_replacements
|
|
683
|
-
if (project) args.project = project
|
|
684
710
|
|
|
685
711
|
const payload = await this.callTool("edit_note", args)
|
|
686
712
|
|
|
@@ -712,8 +738,8 @@ export class BmClient {
|
|
|
712
738
|
const args: Record<string, unknown> = {
|
|
713
739
|
identifier,
|
|
714
740
|
output_format: "json",
|
|
741
|
+
project: this.routedProject(project),
|
|
715
742
|
}
|
|
716
|
-
if (project) args.project = project
|
|
717
743
|
|
|
718
744
|
const payload = await this.callTool("delete_note", args)
|
|
719
745
|
|
|
@@ -741,8 +767,8 @@ export class BmClient {
|
|
|
741
767
|
identifier,
|
|
742
768
|
destination_folder: newFolder,
|
|
743
769
|
output_format: "json",
|
|
770
|
+
project: this.routedProject(project),
|
|
744
771
|
}
|
|
745
|
-
if (project) args.project = project
|
|
746
772
|
|
|
747
773
|
const payload = await this.callTool("move_note", args)
|
|
748
774
|
|
|
@@ -770,10 +796,12 @@ export class BmClient {
|
|
|
770
796
|
identifier?: string,
|
|
771
797
|
project?: string,
|
|
772
798
|
): Promise<SchemaValidationResult> {
|
|
773
|
-
const args: Record<string, unknown> = {
|
|
799
|
+
const args: Record<string, unknown> = {
|
|
800
|
+
output_format: "json",
|
|
801
|
+
project: this.routedProject(project),
|
|
802
|
+
}
|
|
774
803
|
if (noteType) args.note_type = noteType
|
|
775
804
|
if (identifier) args.identifier = identifier
|
|
776
|
-
if (project) args.project = project
|
|
777
805
|
|
|
778
806
|
const payload = await this.callTool("schema_validate", args)
|
|
779
807
|
|
|
@@ -793,8 +821,8 @@ export class BmClient {
|
|
|
793
821
|
note_type: noteType,
|
|
794
822
|
threshold,
|
|
795
823
|
output_format: "json",
|
|
824
|
+
project: this.routedProject(project),
|
|
796
825
|
}
|
|
797
|
-
if (project) args.project = project
|
|
798
826
|
|
|
799
827
|
const payload = await this.callTool("schema_infer", args)
|
|
800
828
|
|
|
@@ -812,8 +840,8 @@ export class BmClient {
|
|
|
812
840
|
const args: Record<string, unknown> = {
|
|
813
841
|
note_type: noteType,
|
|
814
842
|
output_format: "json",
|
|
843
|
+
project: this.routedProject(project),
|
|
815
844
|
}
|
|
816
|
-
if (project) args.project = project
|
|
817
845
|
|
|
818
846
|
const payload = await this.callTool("schema_diff", args)
|
|
819
847
|
|
|
@@ -845,13 +873,19 @@ export class BmClient {
|
|
|
845
873
|
"---",
|
|
846
874
|
].join("\n")
|
|
847
875
|
|
|
848
|
-
// Try append first — if it fails for ANY reason, fall through to create
|
|
849
876
|
try {
|
|
850
877
|
await this.editNote(title, "append", entry)
|
|
851
878
|
log.debug(`appended conversation to: ${title}`)
|
|
852
879
|
return
|
|
853
880
|
} catch (err) {
|
|
854
|
-
|
|
881
|
+
if (!isNoteNotFoundError(err)) {
|
|
882
|
+
log.error(`conversation append failed: ${getErrorMessage(err)}`, err)
|
|
883
|
+
throw err
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
log.debug(
|
|
887
|
+
`conversation note missing, will create: ${getErrorMessage(err)}`,
|
|
888
|
+
)
|
|
855
889
|
}
|
|
856
890
|
|
|
857
891
|
// Create the note with frontmatter and first entry
|
|
@@ -868,7 +902,7 @@ export class BmClient {
|
|
|
868
902
|
].join("\n")
|
|
869
903
|
|
|
870
904
|
try {
|
|
871
|
-
await this.writeNote(title, content, "conversations"
|
|
905
|
+
await this.writeNote(title, content, "conversations")
|
|
872
906
|
log.debug(`created conversation note: ${title}`)
|
|
873
907
|
} catch (err) {
|
|
874
908
|
log.error("conversation index failed", err)
|
package/commands/cli.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
2
2
|
import type { BmClient } from "../bm-client.ts"
|
|
3
3
|
import type { BasicMemoryConfig } from "../config.ts"
|
|
4
4
|
import { log } from "../logger.ts"
|
|
@@ -167,7 +167,6 @@ export function registerCli(
|
|
|
167
167
|
console.log(`Memory dir: ${cfg.memoryDir}`)
|
|
168
168
|
console.log(`Memory file: ${cfg.memoryFile}`)
|
|
169
169
|
console.log(`Auto-capture: ${cfg.autoCapture}`)
|
|
170
|
-
console.log(`Cloud: ${cfg.cloud ? cfg.cloud.url : "disabled"}`)
|
|
171
170
|
console.log(`Debug: ${cfg.debug}`)
|
|
172
171
|
})
|
|
173
172
|
},
|
package/commands/skills.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs"
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs"
|
|
2
2
|
import { dirname, resolve } from "node:path"
|
|
3
3
|
import { fileURLToPath } from "node:url"
|
|
4
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
|
|
4
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
5
5
|
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
-
const SKILLS_DIR = resolve(__dirname, "..", "skills")
|
|
8
|
-
const MANIFEST_PATH = resolve(SKILLS_DIR, "manifest.json")
|
|
9
7
|
|
|
10
8
|
interface ManifestEntry {
|
|
11
9
|
dir: string
|
|
@@ -13,9 +11,22 @@ interface ManifestEntry {
|
|
|
13
11
|
description: string
|
|
14
12
|
}
|
|
15
13
|
|
|
16
|
-
function
|
|
14
|
+
function resolveSkillsDir(api: OpenClawPluginApi): string {
|
|
15
|
+
if (api.resolvePath) {
|
|
16
|
+
return api.resolvePath("skills")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const sourceRootSkills = resolve(__dirname, "..", "skills")
|
|
20
|
+
if (existsSync(sourceRootSkills)) {
|
|
21
|
+
return sourceRootSkills
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return resolve(__dirname, "..", "..", "skills")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadManifest(skillsDir: string): ManifestEntry[] {
|
|
17
28
|
try {
|
|
18
|
-
const raw = readFileSync(
|
|
29
|
+
const raw = readFileSync(resolve(skillsDir, "manifest.json"), "utf-8")
|
|
19
30
|
return JSON.parse(raw) as ManifestEntry[]
|
|
20
31
|
} catch {
|
|
21
32
|
throw new Error(
|
|
@@ -24,16 +35,66 @@ function loadManifest(): ManifestEntry[] {
|
|
|
24
35
|
}
|
|
25
36
|
}
|
|
26
37
|
|
|
27
|
-
function loadSkill(dir: string): string {
|
|
28
|
-
return readFileSync(resolve(
|
|
38
|
+
function loadSkill(skillsDir: string, dir: string): string {
|
|
39
|
+
return readFileSync(resolve(skillsDir, dir, "SKILL.md"), "utf-8")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* List a skill's bundled resource files (references/, assets/, ...) so the
|
|
44
|
+
* command can tell the assistant where they live on disk. SKILL.md workflows
|
|
45
|
+
* reference these by relative path; without the mapping, bundled commands
|
|
46
|
+
* would direct the assistant to files it was never given. evals/ is excluded
|
|
47
|
+
* as test fixtures.
|
|
48
|
+
*/
|
|
49
|
+
function listResourceFiles(skillsDir: string, dir: string): string[] {
|
|
50
|
+
const root = resolve(skillsDir, dir)
|
|
51
|
+
const files: string[] = []
|
|
52
|
+
const walk = (rel: string) => {
|
|
53
|
+
for (const entry of readdirSync(resolve(root, rel), {
|
|
54
|
+
withFileTypes: true,
|
|
55
|
+
})) {
|
|
56
|
+
const relPath = rel ? `${rel}/${entry.name}` : entry.name
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
if (relPath !== "evals") walk(relPath)
|
|
59
|
+
} else if (relPath !== "SKILL.md") {
|
|
60
|
+
files.push(relPath)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
walk("")
|
|
65
|
+
return files.sort()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resourceSection(
|
|
69
|
+
skillsDir: string,
|
|
70
|
+
dir: string,
|
|
71
|
+
files: string[],
|
|
72
|
+
): string {
|
|
73
|
+
if (files.length === 0) return ""
|
|
74
|
+
const lines = files
|
|
75
|
+
.map((f) => `- \`${f}\` → ${resolve(skillsDir, dir, f)}`)
|
|
76
|
+
.join("\n")
|
|
77
|
+
return (
|
|
78
|
+
"\n\n## Bundled resource files\n\n" +
|
|
79
|
+
"This skill references companion files by relative path. They are installed at:\n\n" +
|
|
80
|
+
`${lines}\n\n` +
|
|
81
|
+
"When the workflow directs you to read one of these files, read it from the absolute path listed above."
|
|
82
|
+
)
|
|
29
83
|
}
|
|
30
84
|
|
|
31
85
|
export function registerSkillCommands(api: OpenClawPluginApi): void {
|
|
32
|
-
const
|
|
86
|
+
const skillsDir = resolveSkillsDir(api)
|
|
87
|
+
const manifest = loadManifest(skillsDir)
|
|
33
88
|
|
|
34
89
|
for (const entry of manifest) {
|
|
35
90
|
const commandName = entry.dir.replace(/^memory-/, "")
|
|
36
|
-
const content =
|
|
91
|
+
const content =
|
|
92
|
+
loadSkill(skillsDir, entry.dir) +
|
|
93
|
+
resourceSection(
|
|
94
|
+
skillsDir,
|
|
95
|
+
entry.dir,
|
|
96
|
+
listResourceFiles(skillsDir, entry.dir),
|
|
97
|
+
)
|
|
37
98
|
|
|
38
99
|
api.registerCommand({
|
|
39
100
|
name: commandName,
|
package/commands/slash.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync } from "node:child_process"
|
|
2
2
|
import { dirname, resolve } from "node:path"
|
|
3
3
|
import { fileURLToPath } from "node:url"
|
|
4
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
|
|
4
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
|
|
5
5
|
import type { BmClient } from "../bm-client.ts"
|
|
6
6
|
import { log } from "../logger.ts"
|
|
7
7
|
|
|
@@ -16,7 +16,9 @@ export function registerCommands(
|
|
|
16
16
|
description: "Install or update the Basic Memory CLI (requires uv)",
|
|
17
17
|
requireAuth: true,
|
|
18
18
|
handler: async () => {
|
|
19
|
-
const scriptPath =
|
|
19
|
+
const scriptPath = api.resolvePath
|
|
20
|
+
? api.resolvePath("scripts/setup-bm.sh")
|
|
21
|
+
: resolve(__dirname, "..", "scripts", "setup-bm.sh")
|
|
20
22
|
log.info(`/bm-setup: running ${scriptPath}`)
|
|
21
23
|
|
|
22
24
|
try {
|
package/config.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { homedir, hostname } from "node:os"
|
|
2
2
|
import { isAbsolute, resolve } from "node:path"
|
|
3
3
|
|
|
4
|
-
export type CloudConfig = {
|
|
5
|
-
url: string
|
|
6
|
-
api_key: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
4
|
export type BasicMemoryConfig = {
|
|
10
5
|
project: string
|
|
11
6
|
bmPath: string
|
|
@@ -17,7 +12,6 @@ export type BasicMemoryConfig = {
|
|
|
17
12
|
autoRecall: boolean
|
|
18
13
|
recallPrompt: string
|
|
19
14
|
debug: boolean
|
|
20
|
-
cloud?: CloudConfig
|
|
21
15
|
}
|
|
22
16
|
|
|
23
17
|
const ALLOWED_KEYS = [
|
|
@@ -36,7 +30,6 @@ const ALLOWED_KEYS = [
|
|
|
36
30
|
"recallPrompt",
|
|
37
31
|
"recall_prompt",
|
|
38
32
|
"debug",
|
|
39
|
-
"cloud",
|
|
40
33
|
]
|
|
41
34
|
|
|
42
35
|
function assertAllowedKeys(
|
|
@@ -98,14 +91,6 @@ export function parseConfig(raw: unknown): BasicMemoryConfig {
|
|
|
98
91
|
? (cfg.memory_file as string)
|
|
99
92
|
: "MEMORY.md"
|
|
100
93
|
|
|
101
|
-
let cloud: CloudConfig | undefined
|
|
102
|
-
if (cfg.cloud && typeof cfg.cloud === "object" && !Array.isArray(cfg.cloud)) {
|
|
103
|
-
const c = cfg.cloud as Record<string, unknown>
|
|
104
|
-
if (typeof c.url === "string" && typeof c.api_key === "string") {
|
|
105
|
-
cloud = { url: c.url, api_key: c.api_key }
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
94
|
return {
|
|
110
95
|
project:
|
|
111
96
|
typeof cfg.project === "string" && cfg.project.length > 0
|
|
@@ -143,7 +128,6 @@ export function parseConfig(raw: unknown): BasicMemoryConfig {
|
|
|
143
128
|
? (cfg.recall_prompt as string)
|
|
144
129
|
: "Check for active tasks and recent activity. Summarize anything relevant to the current session.",
|
|
145
130
|
debug: typeof cfg.debug === "boolean" ? cfg.debug : false,
|
|
146
|
-
cloud,
|
|
147
131
|
}
|
|
148
132
|
}
|
|
149
133
|
|