jasper-recall 0.3.1 → 0.3.3

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 CHANGED
@@ -150,6 +150,36 @@ summarize-old --min-size 1000 # Only summarize files larger than 1000 chars
150
150
  - Rule-based summarization (no LLM required)
151
151
  - Preserves headings, bullets, dates, and key markers
152
152
 
153
+ ### doctor (v0.3.2+)
154
+
155
+ System health check and auto-repair:
156
+
157
+ ```bash
158
+ npx jasper-recall doctor # Check system health (default)
159
+ npx jasper-recall doctor --fix # Auto-repair issues
160
+ npx jasper-recall doctor --dry-run # Verbose mode, show exact commands
161
+ ```
162
+
163
+ **Default mode** (no flags):
164
+ - Shows ✅/⚠️/❌ status for all checks
165
+ - For issues found, suggests what `--fix` would do
166
+ - Example: `❌ ChromaDB not installed → run with --fix to install`
167
+
168
+ **Fix mode** (`--fix`):
169
+ - Automatically repairs fixable issues:
170
+ - Creates Python venv if missing: `python3 -m venv ~/.openclaw/rag-env`
171
+ - Installs ChromaDB: `pip install chromadb`
172
+ - Installs sentence-transformers: `pip install sentence-transformers`
173
+ - Creates required directories (chroma-db, memory)
174
+ - Runs initial index if no collections exist
175
+ - Shows what it fixed: `🔧 Installed ChromaDB via pip`
176
+ - Non-fixable issues show manual instructions: `❌ Node.js <18 — please upgrade manually`
177
+
178
+ **Dry-run mode** (`--dry-run`):
179
+ - Same as default, but more verbose
180
+ - Shows exact commands that `--fix` would run
181
+ - Example: `Would run: ~/.openclaw/rag-env/bin/pip install chromadb`
182
+
153
183
  ### sync-shared (v0.2.0+)
154
184
 
155
185
  Extract `[public]` tagged entries to shared memory:
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jasper-recall
3
- version: 0.3.0
3
+ version: 0.3.1
4
4
  description: Local RAG system for agent memory using ChromaDB and sentence-transformers. v0.3.0 adds multi-agent mesh (N agents sharing memory), OpenClaw plugin with autoRecall, and agent-specific collections. Commands: recall, index-digests, digest-sessions, privacy-check, sync-shared, serve, recall-mesh.
5
5
  ---
6
6
 
package/cli/doctor.js CHANGED
@@ -102,10 +102,20 @@ function countMemoryFiles() {
102
102
  }
103
103
  }
104
104
 
105
- function runDoctor() {
105
+ function runDoctor(options = {}) {
106
+ const { fix = false, dryRun = false } = options;
107
+ const verbose = dryRun;
108
+
106
109
  console.log('🏥 Jasper Recall Doctor\n');
107
110
 
111
+ if (fix) {
112
+ console.log('🔧 Fix mode enabled - will attempt to repair issues\n');
113
+ } else if (dryRun) {
114
+ console.log('👁️ Dry-run mode - showing what --fix would do\n');
115
+ }
116
+
108
117
  const checks = [];
118
+ const fixes = [];
109
119
 
110
120
  // Node.js version check
111
121
  const nodeResult = exec('node --version');
@@ -115,7 +125,9 @@ function runDoctor() {
115
125
  label: 'Node.js',
116
126
  status: nodeOk ? '✅' : '❌',
117
127
  value: nodeResult.success ? `v${nodeVersion}` : 'not found',
118
- ok: nodeOk
128
+ ok: nodeOk,
129
+ fixable: false,
130
+ fixMessage: 'Please upgrade Node.js manually: https://nodejs.org/'
119
131
  });
120
132
 
121
133
  // Python version check
@@ -127,7 +139,32 @@ function runDoctor() {
127
139
  label: 'Python',
128
140
  status: pythonOk ? '✅' : '❌',
129
141
  value: pythonVersion || 'not found',
130
- ok: pythonOk
142
+ ok: pythonOk,
143
+ fixable: false,
144
+ fixMessage: 'Please install Python 3: https://www.python.org/downloads/'
145
+ });
146
+
147
+ // Virtual environment check
148
+ const venvExists = fs.existsSync(VENV_PATH);
149
+ checks.push({
150
+ label: 'Venv',
151
+ status: venvExists ? '✅' : '❌',
152
+ value: venvExists ? VENV_PATH : 'not found',
153
+ ok: venvExists,
154
+ fixable: !venvExists && pythonOk,
155
+ fixMessage: !venvExists ? `create virtual environment at ${VENV_PATH}` : null,
156
+ fixCommand: `python3 -m venv ${VENV_PATH}`,
157
+ fixAction: () => {
158
+ console.log(` 🔧 Creating virtual environment...`);
159
+ const result = exec(`python3 -m venv ${VENV_PATH}`, { silent: false });
160
+ if (result.success) {
161
+ console.log(` ✅ Virtual environment created at ${VENV_PATH}`);
162
+ return true;
163
+ } else {
164
+ console.log(` ❌ Failed to create virtual environment`);
165
+ return false;
166
+ }
167
+ }
131
168
  });
132
169
 
133
170
  // ChromaDB check
@@ -140,7 +177,21 @@ function runDoctor() {
140
177
  label: 'ChromaDB',
141
178
  status: chromaOk ? '✅' : '❌',
142
179
  value: chromaVersion ? `installed (${chromaVersion})` : 'not installed',
143
- ok: chromaOk
180
+ ok: chromaOk,
181
+ fixable: !chromaOk && venvExists,
182
+ fixMessage: !chromaOk ? 'install chromadb via pip' : null,
183
+ fixCommand: `${pipPath} install chromadb`,
184
+ fixAction: () => {
185
+ console.log(` 🔧 Installing ChromaDB...`);
186
+ const result = exec(`${pipPath} install chromadb`, { silent: false });
187
+ if (result.success) {
188
+ console.log(` ✅ ChromaDB installed successfully`);
189
+ return true;
190
+ } else {
191
+ console.log(` ❌ Failed to install ChromaDB`);
192
+ return false;
193
+ }
194
+ }
144
195
  });
145
196
 
146
197
  // Sentence-transformers check
@@ -152,16 +203,21 @@ function runDoctor() {
152
203
  label: 'Transformers',
153
204
  status: transformersOk ? '✅' : '❌',
154
205
  value: transformersVersion ? 'sentence-transformers installed' : 'not installed',
155
- ok: transformersOk
156
- });
157
-
158
- // Virtual environment check
159
- const venvExists = fs.existsSync(VENV_PATH);
160
- checks.push({
161
- label: 'Venv',
162
- status: venvExists ? '✅' : '❌',
163
- value: venvExists ? VENV_PATH : 'not found',
164
- ok: venvExists
206
+ ok: transformersOk,
207
+ fixable: !transformersOk && venvExists,
208
+ fixMessage: !transformersOk ? 'install sentence-transformers via pip' : null,
209
+ fixCommand: `${pipPath} install sentence-transformers`,
210
+ fixAction: () => {
211
+ console.log(` 🔧 Installing sentence-transformers...`);
212
+ const result = exec(`${pipPath} install sentence-transformers`, { silent: false });
213
+ if (result.success) {
214
+ console.log(` ✅ sentence-transformers installed successfully`);
215
+ return true;
216
+ } else {
217
+ console.log(` ❌ Failed to install sentence-transformers`);
218
+ return false;
219
+ }
220
+ }
165
221
  });
166
222
 
167
223
  // ChromaDB directory check
@@ -171,7 +227,21 @@ function runDoctor() {
171
227
  label: 'Database',
172
228
  status: chromaExists ? '✅' : '❌',
173
229
  value: chromaExists ? `${CHROMA_PATH} (${collections} collections)` : 'not found',
174
- ok: chromaExists
230
+ ok: chromaExists,
231
+ fixable: !chromaExists,
232
+ fixMessage: !chromaExists ? `create database directory at ${CHROMA_PATH}` : null,
233
+ fixCommand: `mkdir -p ${CHROMA_PATH}`,
234
+ fixAction: () => {
235
+ console.log(` 🔧 Creating ChromaDB directory...`);
236
+ try {
237
+ fs.mkdirSync(CHROMA_PATH, { recursive: true });
238
+ console.log(` ✅ Created directory: ${CHROMA_PATH}`);
239
+ return true;
240
+ } catch (e) {
241
+ console.log(` ❌ Failed to create directory: ${e.message}`);
242
+ return false;
243
+ }
244
+ }
175
245
  });
176
246
 
177
247
  // Memory files check
@@ -181,17 +251,47 @@ function runDoctor() {
181
251
  label: 'Memory files',
182
252
  status: memoryExists ? '✅' : '⚠️',
183
253
  value: memoryExists ? `${memoryCount} files in memory/` : 'directory not found',
184
- ok: memoryExists
254
+ ok: memoryExists,
255
+ fixable: !memoryExists,
256
+ fixMessage: !memoryExists ? `create memory directory at ${MEMORY_PATH}` : null,
257
+ fixCommand: `mkdir -p ${MEMORY_PATH}`,
258
+ fixAction: () => {
259
+ console.log(` 🔧 Creating memory directory...`);
260
+ try {
261
+ fs.mkdirSync(MEMORY_PATH, { recursive: true });
262
+ console.log(` ✅ Created directory: ${MEMORY_PATH}`);
263
+ return true;
264
+ } catch (e) {
265
+ console.log(` ❌ Failed to create directory: ${e.message}`);
266
+ return false;
267
+ }
268
+ }
185
269
  });
186
270
 
187
- // Last index time
271
+ // Last index time / collections check
188
272
  const lastIndexMs = getLastIndexTime();
189
- const lastIndexOk = lastIndexMs !== null && lastIndexMs < 7 * 24 * 60 * 60 * 1000; // < 7 days
273
+ const needsIndex = collections === 0 && chromaExists;
274
+ const lastIndexOk = !needsIndex && (lastIndexMs !== null && lastIndexMs < 7 * 24 * 60 * 60 * 1000); // < 7 days
190
275
  checks.push({
191
276
  label: 'Last indexed',
192
277
  status: lastIndexMs === null ? '⚠️' : (lastIndexOk ? '✅' : '⚠️'),
193
- value: lastIndexMs === null ? 'never' : formatTime(lastIndexMs),
194
- ok: lastIndexMs !== null
278
+ value: needsIndex ? 'no collections - needs initial index' : (lastIndexMs === null ? 'never' : formatTime(lastIndexMs)),
279
+ ok: lastIndexMs !== null && !needsIndex,
280
+ fixable: needsIndex,
281
+ fixMessage: needsIndex ? 'run initial indexing with index-digests' : null,
282
+ fixCommand: 'index-digests',
283
+ fixAction: () => {
284
+ console.log(` 🔧 Running initial index...`);
285
+ const indexScript = path.join(__dirname, 'index-digests.js');
286
+ const result = exec(`node ${indexScript}`, { silent: false });
287
+ if (result.success) {
288
+ console.log(` ✅ Initial indexing complete`);
289
+ return true;
290
+ } else {
291
+ console.log(` ⚠️ Indexing may have completed with warnings`);
292
+ return true; // Don't treat warnings as failure
293
+ }
294
+ }
195
295
  });
196
296
 
197
297
  // Print results
@@ -199,10 +299,66 @@ function runDoctor() {
199
299
  for (const check of checks) {
200
300
  const padding = ' '.repeat(maxLabelLength - check.label.length);
201
301
  console.log(` ${check.label}:${padding} ${check.status} ${check.value}`);
302
+
303
+ // Show fix suggestions in default/dry-run mode
304
+ if (!check.ok && !fix) {
305
+ if (check.fixable && check.fixMessage) {
306
+ if (verbose && check.fixCommand) {
307
+ console.log(` ${dryRun ? '📋' : '→'} Would run: ${check.fixCommand}`);
308
+ } else {
309
+ console.log(` → run with --fix to ${check.fixMessage}`);
310
+ }
311
+ } else if (!check.fixable && check.fixMessage) {
312
+ console.log(` ❌ ${check.fixMessage}`);
313
+ }
314
+ }
202
315
  }
203
316
 
204
317
  console.log('');
205
318
 
319
+ // Apply fixes if requested
320
+ if (fix) {
321
+ const fixableIssues = checks.filter(c => !c.ok && c.fixable && c.fixAction);
322
+
323
+ if (fixableIssues.length === 0) {
324
+ const unfixableIssues = checks.filter(c => !c.ok && !c.fixable);
325
+ if (unfixableIssues.length > 0) {
326
+ console.log('⚠️ Some issues require manual intervention:\n');
327
+ for (const issue of unfixableIssues) {
328
+ console.log(` ❌ ${issue.label}: ${issue.fixMessage}`);
329
+ }
330
+ console.log('');
331
+ }
332
+ } else {
333
+ console.log('🔧 Applying fixes...\n');
334
+
335
+ for (const issue of fixableIssues) {
336
+ const success = issue.fixAction();
337
+ fixes.push({ issue: issue.label, success });
338
+ console.log('');
339
+ }
340
+
341
+ const successCount = fixes.filter(f => f.success).length;
342
+ const failCount = fixes.filter(f => !f.success).length;
343
+
344
+ if (failCount === 0) {
345
+ console.log(`✅ All ${successCount} issue${successCount > 1 ? 's' : ''} fixed!\n`);
346
+ } else {
347
+ console.log(`⚠️ Fixed ${successCount}/${fixes.length} issues (${failCount} failed)\n`);
348
+ }
349
+
350
+ // Check for remaining unfixable issues
351
+ const unfixableIssues = checks.filter(c => !c.ok && !c.fixable);
352
+ if (unfixableIssues.length > 0) {
353
+ console.log('⚠️ Remaining issues require manual intervention:\n');
354
+ for (const issue of unfixableIssues) {
355
+ console.log(` ❌ ${issue.label}: ${issue.fixMessage}`);
356
+ }
357
+ console.log('');
358
+ }
359
+ }
360
+ }
361
+
206
362
  // Summary
207
363
  const allOk = checks.every(c => c.ok);
208
364
  if (allOk) {
@@ -210,23 +366,17 @@ function runDoctor() {
210
366
  return 0;
211
367
  } else {
212
368
  const failed = checks.filter(c => !c.ok);
213
- console.log(`⚠️ ${failed.length} issue${failed.length > 1 ? 's' : ''} detected.\n`);
214
369
 
215
- if (!pythonOk) {
216
- console.log('→ Install Python 3: https://www.python.org/downloads/');
217
- }
218
- if (!venvExists || !chromaOk || !transformersOk) {
219
- console.log('→ Run: npx jasper-recall setup');
220
- }
221
- if (!memoryExists) {
222
- console.log(`→ Create memory directory: mkdir -p ${MEMORY_PATH}`);
223
- }
224
- if (lastIndexMs === null || !lastIndexOk) {
225
- console.log('→ Index your memory: index-digests');
370
+ if (!fix) {
371
+ console.log(`⚠️ ${failed.length} issue${failed.length > 1 ? 's' : ''} detected.\n`);
372
+
373
+ const hasFixableIssues = failed.some(c => c.fixable);
374
+ if (hasFixableIssues) {
375
+ console.log('→ Run with --fix to automatically repair issues\n');
376
+ }
226
377
  }
227
378
 
228
- console.log('');
229
- return 1;
379
+ return fixes.length > 0 && fixes.every(f => f.success) ? 0 : 1;
230
380
  }
231
381
  }
232
382
 
@@ -234,5 +384,11 @@ module.exports = { runDoctor };
234
384
 
235
385
  // Allow direct execution
236
386
  if (require.main === module) {
237
- process.exit(runDoctor());
387
+ const args = process.argv.slice(2);
388
+ const options = {
389
+ fix: args.includes('--fix'),
390
+ dryRun: args.includes('--dry-run')
391
+ };
392
+
393
+ process.exit(runDoctor(options));
238
394
  }
@@ -26,6 +26,9 @@ const VENV_PATH = path.join(os.homedir(), '.openclaw', 'rag-env');
26
26
  const CHROMA_PATH = path.join(os.homedir(), '.openclaw', 'chroma-db');
27
27
  const BIN_PATH = path.join(os.homedir(), '.local', 'bin');
28
28
  const SCRIPTS_DIR = path.join(__dirname, '..', 'scripts');
29
+ const EXTENSIONS_DIR = path.join(__dirname, '..', 'extensions');
30
+ const OPENCLAW_CONFIG = path.join(os.homedir(), '.openclaw', 'openclaw.json');
31
+ const OPENCLAW_SKILLS = path.join(os.homedir(), '.openclaw', 'workspace', 'skills');
29
32
 
30
33
  function log(msg) {
31
34
  console.log(`🦊 ${msg}`);
@@ -47,6 +50,70 @@ function run(cmd, opts = {}) {
47
50
  }
48
51
  }
49
52
 
53
+ function setupOpenClawIntegration() {
54
+ log('Setting up OpenClaw integration...');
55
+
56
+ // Check if OpenClaw is installed
57
+ const openclawDir = path.join(os.homedir(), '.openclaw');
58
+ if (!fs.existsSync(openclawDir)) {
59
+ console.log(' ⚠ OpenClaw not detected (~/.openclaw not found)');
60
+ console.log(' → Skipping OpenClaw integration');
61
+ return false;
62
+ }
63
+
64
+ // Install SKILL.md to skills directory
65
+ const skillSrc = path.join(EXTENSIONS_DIR, 'openclaw-plugin', 'SKILL.md');
66
+ const skillDest = path.join(OPENCLAW_SKILLS, 'jasper-recall', 'SKILL.md');
67
+
68
+ if (fs.existsSync(skillSrc)) {
69
+ fs.mkdirSync(path.dirname(skillDest), { recursive: true });
70
+ fs.copyFileSync(skillSrc, skillDest);
71
+ console.log(` ✓ Installed SKILL.md: ${skillDest}`);
72
+ } else {
73
+ console.log(' ⚠ SKILL.md not found in package (try reinstalling)');
74
+ }
75
+
76
+ // Update openclaw.json with plugin config
77
+ if (fs.existsSync(OPENCLAW_CONFIG)) {
78
+ try {
79
+ const configRaw = fs.readFileSync(OPENCLAW_CONFIG, 'utf8');
80
+ const config = JSON.parse(configRaw);
81
+
82
+ // Initialize plugins structure if needed
83
+ if (!config.plugins) config.plugins = {};
84
+ if (!config.plugins.entries) config.plugins.entries = {};
85
+
86
+ // Check if already configured
87
+ if (config.plugins.entries['jasper-recall']) {
88
+ console.log(' ✓ Plugin already configured in openclaw.json');
89
+ } else {
90
+ // Add plugin config
91
+ config.plugins.entries['jasper-recall'] = {
92
+ enabled: true,
93
+ config: {
94
+ autoRecall: true,
95
+ minScore: 0.3,
96
+ defaultLimit: 5
97
+ }
98
+ };
99
+
100
+ // Write back with nice formatting
101
+ fs.writeFileSync(OPENCLAW_CONFIG, JSON.stringify(config, null, 2) + '\n');
102
+ console.log(' ✓ Added jasper-recall plugin to openclaw.json');
103
+ console.log(' → Restart OpenClaw gateway to activate: openclaw gateway restart');
104
+ }
105
+ } catch (e) {
106
+ console.log(` ⚠ Could not update openclaw.json: ${e.message}`);
107
+ console.log(' → Manually add plugin config (see docs)');
108
+ }
109
+ } else {
110
+ console.log(' ⚠ openclaw.json not found');
111
+ console.log(' → Create config or manually add jasper-recall plugin');
112
+ }
113
+
114
+ return true;
115
+ }
116
+
50
117
  function setup() {
51
118
  log('Jasper Recall — Setup');
52
119
  console.log('=' .repeat(40));
@@ -118,6 +185,11 @@ function setup() {
118
185
  console.log(` export PATH="$HOME/.local/bin:$PATH"`);
119
186
  }
120
187
 
188
+ console.log('');
189
+
190
+ // OpenClaw integration
191
+ setupOpenClawIntegration();
192
+
121
193
  console.log('');
122
194
  console.log('=' .repeat(40));
123
195
  log('Setup complete!');
@@ -139,6 +211,7 @@ USAGE:
139
211
  COMMANDS:
140
212
  setup Install dependencies and CLI scripts
141
213
  doctor Run system health check
214
+ Flags: --fix (auto-repair issues), --dry-run (verbose output)
142
215
  recall Search your memory (alias for the recall command)
143
216
  index Index memory files (alias for index-digests)
144
217
  digest Process session logs (alias for digest-sessions)
@@ -231,7 +304,12 @@ switch (command) {
231
304
  case 'doctor':
232
305
  // Run system health check
233
306
  const { runDoctor } = require('./doctor');
234
- process.exit(runDoctor());
307
+ const args = process.argv.slice(3);
308
+ const options = {
309
+ fix: args.includes('--fix'),
310
+ dryRun: args.includes('--dry-run')
311
+ };
312
+ process.exit(runDoctor(options));
235
313
  break;
236
314
  case 'config':
237
315
  // Configuration management
@@ -0,0 +1,204 @@
1
+ # Jasper Recall - OpenClaw Plugin
2
+
3
+ Semantic search over indexed memory using ChromaDB. Automatically injects relevant context before agent processing.
4
+
5
+ ## Features
6
+
7
+ - **`recall` tool** — Manual semantic search over memory
8
+ - **`/recall` command** — Quick lookups from chat
9
+ - **`/index` command** — Re-index memory files
10
+ - **Auto-recall** — Automatically inject relevant memories before processing
11
+
12
+ ---
13
+
14
+ ## Auto-Recall (The Magic ✨)
15
+
16
+ When `autoRecall` is enabled, jasper-recall hooks into the agent lifecycle and automatically searches your memory before every message is processed.
17
+
18
+ ### How It Works
19
+
20
+ ```
21
+ ┌─────────────────────────────────────────────────────────────┐
22
+ │ 1. Message arrives from user │
23
+ │ 2. before_agent_start hook fires │
24
+ │ 3. jasper-recall searches ChromaDB with message as query │
25
+ │ 4. Results filtered by minScore (default: 30%) │
26
+ │ 5. Relevant memories injected via prependContext │
27
+ │ 6. Agent sees memories + original message │
28
+ │ 7. Agent responds with full context │
29
+ └─────────────────────────────────────────────────────────────┘
30
+ ```
31
+
32
+ ### What Gets Injected
33
+
34
+ ```xml
35
+ <relevant-memories>
36
+ The following memories may be relevant to this conversation:
37
+ - [memory/2026-02-05.md] Worker orchestration decisions...
38
+ - [MEMORY.md] Git workflow: feature → develop → main...
39
+ - [memory/sops/codex-integration-sop.md] Codex Cloud sync...
40
+ </relevant-memories>
41
+ ```
42
+
43
+ ### What's Skipped
44
+
45
+ Auto-recall won't run for:
46
+ - Heartbeat polls (`HEARTBEAT...`)
47
+ - System prompts containing `NO_REPLY`
48
+ - Messages shorter than 10 characters
49
+
50
+ ---
51
+
52
+ ## Configuration
53
+
54
+ In `openclaw.json`:
55
+
56
+ ```json
57
+ {
58
+ "plugins": {
59
+ "entries": {
60
+ "jasper-recall": {
61
+ "enabled": true,
62
+ "config": {
63
+ "autoRecall": true,
64
+ "minScore": 0.3,
65
+ "defaultLimit": 5,
66
+ "publicOnly": false
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ### Options
75
+
76
+ | Option | Type | Default | Description |
77
+ |--------|------|---------|-------------|
78
+ | `enabled` | boolean | `true` | Enable/disable plugin |
79
+ | `autoRecall` | boolean | `false` | Auto-inject memories before processing |
80
+ | `minScore` | number | `0.3` | Minimum similarity score (0-1) for auto-recall |
81
+ | `defaultLimit` | number | `5` | Default number of results |
82
+ | `publicOnly` | boolean | `false` | Only search public memory (sandboxed agents) |
83
+
84
+ ### Score Tuning
85
+
86
+ - `minScore: 0.3` — Include loosely related memories (more context, may include noise)
87
+ - `minScore: 0.5` — Only moderately relevant (balanced)
88
+ - `minScore: 0.7` — Only highly relevant (precise, may miss useful context)
89
+
90
+ ---
91
+
92
+ ## Tools
93
+
94
+ ### `recall`
95
+
96
+ Manual semantic search over memory.
97
+
98
+ **Parameters:**
99
+ - `query` (string, required): Natural language search query
100
+ - `limit` (number, optional): Max results (default: 5)
101
+
102
+ **Example:**
103
+ ```
104
+ recall query="what did we decide about the API design" limit=3
105
+ ```
106
+
107
+ **Returns:** Formatted markdown with matching memories, scores, and sources.
108
+
109
+ ---
110
+
111
+ ## Commands
112
+
113
+ ### `/recall <query>`
114
+
115
+ Quick memory search from chat.
116
+
117
+ ```
118
+ /recall worker orchestration decisions
119
+ ```
120
+
121
+ ### `/index`
122
+
123
+ Re-index memory files into ChromaDB. Run after updating notes.
124
+
125
+ ```
126
+ /index
127
+ ```
128
+
129
+ ---
130
+
131
+ ## RPC Methods
132
+
133
+ For external integrations:
134
+
135
+ ### `recall.search`
136
+
137
+ ```json
138
+ { "query": "search terms", "limit": 5 }
139
+ ```
140
+
141
+ ### `recall.index`
142
+
143
+ Re-index memory files (no params).
144
+
145
+ ---
146
+
147
+ ## Requirements
148
+
149
+ - `recall` command in `~/.local/bin/`
150
+ - ChromaDB index at `~/.openclaw/chroma-db`
151
+ - Python venv at `~/.openclaw/rag-env`
152
+
153
+ ## Installation
154
+
155
+ ```bash
156
+ npx jasper-recall setup
157
+ ```
158
+
159
+ This sets up:
160
+ 1. Python venv with ChromaDB + sentence-transformers
161
+ 2. `recall`, `index-digests`, `digest-sessions` scripts
162
+ 3. Initial index of memory files
163
+
164
+ ---
165
+
166
+ ## When Auto-Recall Helps
167
+
168
+ ✅ **Great for:**
169
+ - Questions about past decisions ("what did we decide about X?")
170
+ - Following up on previous work ("where were we with the worker setup?")
171
+ - Context about people, preferences, projects
172
+ - Finding SOPs and procedures
173
+
174
+ ⚠️ **Less useful for:**
175
+ - Brand new topics with no memory
176
+ - Simple commands ("list files")
177
+ - Real-time data (weather, time)
178
+
179
+ ---
180
+
181
+ ## Sandboxed Agents
182
+
183
+ For agents processing untrusted input, use `publicOnly`:
184
+
185
+ ```json
186
+ {
187
+ "jasper-recall": {
188
+ "config": {
189
+ "publicOnly": true,
190
+ "autoRecall": true
191
+ }
192
+ }
193
+ }
194
+ ```
195
+
196
+ This restricts searches to `memory/shared/` and public-tagged content, preventing leakage of private memories.
197
+
198
+ ---
199
+
200
+ ## Links
201
+
202
+ - **GitHub**: https://github.com/E-x-O-Entertainment-Studios-Inc/jasper-recall
203
+ - **npm**: `npx jasper-recall setup`
204
+ - **ClawHub**: `clawhub install jasper-recall`