clawmem 0.1.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.
Files changed (50) hide show
  1. package/AGENTS.md +660 -0
  2. package/CLAUDE.md +660 -0
  3. package/LICENSE +21 -0
  4. package/README.md +993 -0
  5. package/SKILL.md +717 -0
  6. package/bin/clawmem +75 -0
  7. package/package.json +72 -0
  8. package/src/amem.ts +797 -0
  9. package/src/beads.ts +263 -0
  10. package/src/clawmem.ts +1849 -0
  11. package/src/collections.ts +405 -0
  12. package/src/config.ts +178 -0
  13. package/src/consolidation.ts +123 -0
  14. package/src/directory-context.ts +248 -0
  15. package/src/errors.ts +41 -0
  16. package/src/formatter.ts +427 -0
  17. package/src/graph-traversal.ts +247 -0
  18. package/src/hooks/context-surfacing.ts +317 -0
  19. package/src/hooks/curator-nudge.ts +89 -0
  20. package/src/hooks/decision-extractor.ts +639 -0
  21. package/src/hooks/feedback-loop.ts +214 -0
  22. package/src/hooks/handoff-generator.ts +345 -0
  23. package/src/hooks/postcompact-inject.ts +226 -0
  24. package/src/hooks/precompact-extract.ts +314 -0
  25. package/src/hooks/pretool-inject.ts +79 -0
  26. package/src/hooks/session-bootstrap.ts +324 -0
  27. package/src/hooks/staleness-check.ts +130 -0
  28. package/src/hooks.ts +367 -0
  29. package/src/indexer.ts +327 -0
  30. package/src/intent.ts +294 -0
  31. package/src/limits.ts +26 -0
  32. package/src/llm.ts +1175 -0
  33. package/src/mcp.ts +2138 -0
  34. package/src/memory.ts +336 -0
  35. package/src/mmr.ts +93 -0
  36. package/src/observer.ts +269 -0
  37. package/src/openclaw/engine.ts +283 -0
  38. package/src/openclaw/index.ts +221 -0
  39. package/src/openclaw/plugin.json +83 -0
  40. package/src/openclaw/shell.ts +207 -0
  41. package/src/openclaw/tools.ts +304 -0
  42. package/src/profile.ts +346 -0
  43. package/src/promptguard.ts +218 -0
  44. package/src/retrieval-gate.ts +106 -0
  45. package/src/search-utils.ts +127 -0
  46. package/src/server.ts +783 -0
  47. package/src/splitter.ts +325 -0
  48. package/src/store.ts +4062 -0
  49. package/src/validation.ts +67 -0
  50. package/src/watcher.ts +58 -0
package/src/beads.ts ADDED
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Beads Integration - Dolt-backed task dependency graph
3
+ *
4
+ * Queries Beads via `bd` CLI (v0.58.0+, Dolt backend) and syncs issues to ClawMem.
5
+ * Replaces legacy JSONL parser — Dolt is now source of truth.
6
+ *
7
+ * Reference: https://github.com/steveyegge/beads
8
+ */
9
+
10
+ import { existsSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { execSync } from "node:child_process";
13
+
14
+ // =============================================================================
15
+ // Types (matches bd list --json output: IssueWithCounts)
16
+ // =============================================================================
17
+
18
+ export interface BeadsDependency {
19
+ issue_id: string;
20
+ depends_on_id: string;
21
+ type: string; // blocks, parent-child, waits-for, discovered-from, relates-to, etc.
22
+ created_at: string;
23
+ created_by?: string;
24
+ metadata?: string;
25
+ thread_id?: string;
26
+ }
27
+
28
+ export interface BeadsIssue {
29
+ id: string;
30
+ title: string;
31
+ description?: string;
32
+ notes?: string;
33
+ status: string; // open, in_progress, blocked, deferred, closed
34
+ priority: number; // 0-4
35
+ issue_type?: string; // task, bug, feature, epic, chore, decision, etc.
36
+ assignee?: string;
37
+ owner?: string;
38
+ created_at: string;
39
+ updated_at?: string;
40
+ closed_at?: string;
41
+ close_reason?: string;
42
+ external_ref?: string;
43
+ metadata?: Record<string, unknown>;
44
+ labels?: string[];
45
+ dependencies?: BeadsDependency[];
46
+ quality_score?: number;
47
+ // Computed counts from bd list --json
48
+ dependency_count?: number;
49
+ dependent_count?: number;
50
+ comment_count?: number;
51
+ parent?: string;
52
+ // Legacy compat fields (mapped from new schema)
53
+ type: string; // alias for issue_type
54
+ tags: string[]; // alias for labels
55
+ blocks: string[]; // extracted from dependencies
56
+ }
57
+
58
+ // =============================================================================
59
+ // CLI Interface
60
+ // =============================================================================
61
+
62
+ /**
63
+ * Find the `bd` binary. Checks PATH, common locations.
64
+ */
65
+ function findBdBinary(): string | null {
66
+ // Check PATH first
67
+ try {
68
+ const path = execSync("which bd 2>/dev/null", { encoding: "utf-8" }).trim();
69
+ if (path) return path;
70
+ } catch { /* not on PATH */ }
71
+
72
+ // Check common locations
73
+ const candidates = [
74
+ join(process.env.HOME || "", "go/bin/bd"),
75
+ "/usr/local/bin/bd",
76
+ ];
77
+ for (const p of candidates) {
78
+ if (existsSync(p)) return p;
79
+ }
80
+ return null;
81
+ }
82
+
83
+ /**
84
+ * Run a bd CLI command and return stdout.
85
+ * Executes in the project directory containing .beads/.
86
+ */
87
+ function runBd(projectDir: string, args: string[], timeoutMs = 10000): string | null {
88
+ const bd = findBdBinary();
89
+ if (!bd) {
90
+ console.warn("[beads] bd binary not found — cannot sync from Dolt backend");
91
+ return null;
92
+ }
93
+
94
+ try {
95
+ return execSync(`${bd} ${args.join(" ")}`, {
96
+ cwd: projectDir,
97
+ encoding: "utf-8",
98
+ timeout: timeoutMs,
99
+ env: { ...process.env },
100
+ stdio: ["pipe", "pipe", "pipe"],
101
+ });
102
+ } catch (err: any) {
103
+ console.warn(`[beads] bd ${args[0]} failed: ${err.message?.slice(0, 200)}`);
104
+ return null;
105
+ }
106
+ }
107
+
108
+ // =============================================================================
109
+ // Query Functions
110
+ // =============================================================================
111
+
112
+ /**
113
+ * Query all beads from Dolt via `bd list --json`.
114
+ * Returns parsed issues with labels and dependencies populated.
115
+ */
116
+ export function queryBeadsList(projectDir: string): BeadsIssue[] {
117
+ const output = runBd(projectDir, ["list", "--json"]);
118
+ if (!output) return [];
119
+
120
+ try {
121
+ const raw = JSON.parse(output);
122
+ if (!Array.isArray(raw)) return [];
123
+
124
+ return raw.map((item: any) => normalizeBeadsIssue(item));
125
+ } catch (err) {
126
+ console.warn(`[beads] Failed to parse bd list output: ${err}`);
127
+ return [];
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Normalize a Beads JSON issue into our BeadsIssue type.
133
+ * Maps new Dolt schema fields to legacy compat fields.
134
+ */
135
+ function normalizeBeadsIssue(raw: any): BeadsIssue {
136
+ const deps: BeadsDependency[] = raw.dependencies || [];
137
+
138
+ // Extract "blocks" relationships for legacy compat
139
+ const blocks = deps
140
+ .filter((d: BeadsDependency) => d.type === "blocks" && d.issue_id === raw.id)
141
+ .map((d: BeadsDependency) => d.depends_on_id);
142
+
143
+ return {
144
+ id: raw.id,
145
+ title: raw.title || "",
146
+ description: raw.description,
147
+ notes: raw.notes,
148
+ status: raw.status || "open",
149
+ priority: raw.priority ?? 2,
150
+ issue_type: raw.issue_type,
151
+ assignee: raw.assignee,
152
+ owner: raw.owner,
153
+ created_at: raw.created_at || new Date().toISOString(),
154
+ updated_at: raw.updated_at,
155
+ closed_at: raw.closed_at,
156
+ close_reason: raw.close_reason,
157
+ external_ref: raw.external_ref,
158
+ metadata: raw.metadata,
159
+ labels: raw.labels || [],
160
+ dependencies: deps,
161
+ quality_score: raw.quality_score,
162
+ dependency_count: raw.dependency_count,
163
+ dependent_count: raw.dependent_count,
164
+ comment_count: raw.comment_count,
165
+ parent: raw.parent,
166
+ // Legacy compat
167
+ type: raw.issue_type || "task",
168
+ tags: raw.labels || [],
169
+ blocks,
170
+ };
171
+ }
172
+
173
+ // =============================================================================
174
+ // Detection
175
+ // =============================================================================
176
+
177
+ /**
178
+ * Detect if a directory contains a Beads project (Dolt-backed).
179
+ * Returns the project directory path if found, null otherwise.
180
+ *
181
+ * Checks for .beads/ directory (Dolt backend).
182
+ * Falls back to checking .beads/beads.jsonl for legacy installations.
183
+ */
184
+ export function detectBeadsProject(cwd: string): string | null {
185
+ const beadsDir = join(cwd, ".beads");
186
+ if (existsSync(beadsDir)) return cwd;
187
+ return null;
188
+ }
189
+
190
+ // =============================================================================
191
+ // Markdown Formatting
192
+ // =============================================================================
193
+
194
+ /**
195
+ * Format a Beads issue as markdown for ClawMem indexing.
196
+ */
197
+ export function formatBeadsIssueAsMarkdown(issue: BeadsIssue): string {
198
+ const lines = [
199
+ `# ${issue.title}`,
200
+ ``,
201
+ `**ID**: ${issue.id}`,
202
+ `**Type**: ${issue.type || issue.issue_type || "task"}`,
203
+ `**Status**: ${issue.status}`,
204
+ `**Priority**: P${issue.priority}`,
205
+ ];
206
+
207
+ if (issue.assignee) lines.push(`**Assignee**: ${issue.assignee}`);
208
+ if (issue.owner) lines.push(`**Owner**: ${issue.owner}`);
209
+ if (issue.parent) lines.push(`**Parent**: ${issue.parent}`);
210
+ if (issue.tags && issue.tags.length > 0) lines.push(`**Tags**: ${issue.tags.join(", ")}`);
211
+ if (issue.labels && issue.labels.length > 0 && !issue.tags?.length) {
212
+ lines.push(`**Labels**: ${issue.labels.join(", ")}`);
213
+ }
214
+ if (issue.blocks && issue.blocks.length > 0) lines.push(`**Blocks**: ${issue.blocks.join(", ")}`);
215
+ if (issue.external_ref) lines.push(`**External Ref**: ${issue.external_ref}`);
216
+ if (issue.quality_score != null) lines.push(`**Quality Score**: ${issue.quality_score}`);
217
+
218
+ if (issue.description) {
219
+ lines.push("", "## Description", "", issue.description);
220
+ }
221
+
222
+ if (issue.notes) {
223
+ lines.push("", "## Notes", "", issue.notes);
224
+ }
225
+
226
+ // Include dependency details
227
+ if (issue.dependencies && issue.dependencies.length > 0) {
228
+ lines.push("", "## Dependencies", "");
229
+ for (const dep of issue.dependencies) {
230
+ const dir = dep.issue_id === issue.id ? `→ ${dep.depends_on_id}` : `← ${dep.issue_id}`;
231
+ lines.push(`- ${dep.type}: ${dir}`);
232
+ }
233
+ }
234
+
235
+ return lines.join("\n");
236
+ }
237
+
238
+ // =============================================================================
239
+ // Legacy Compat (parseBeadsJsonl — kept for migration, not active use)
240
+ // =============================================================================
241
+
242
+ /**
243
+ * @deprecated Use queryBeadsList() instead. Beads v0.58.0+ uses Dolt backend.
244
+ * Kept only for one-time migration of pre-Dolt installations.
245
+ */
246
+ export function parseBeadsJsonl(path: string): BeadsIssue[] {
247
+ console.warn("[beads] parseBeadsJsonl is deprecated — Beads v0.58.0+ uses Dolt backend. Use queryBeadsList() instead.");
248
+ const { readFileSync } = require("node:fs");
249
+ const content = readFileSync(path, "utf-8");
250
+ const lines = content.trim().split("\n");
251
+
252
+ return lines
253
+ .filter((line: string) => line.trim())
254
+ .map((line: string) => {
255
+ try {
256
+ const raw = JSON.parse(line);
257
+ return normalizeBeadsIssue(raw);
258
+ } catch {
259
+ return null;
260
+ }
261
+ })
262
+ .filter((issue: BeadsIssue | null): issue is BeadsIssue => issue !== null);
263
+ }