@yamo/memory-mesh 3.1.4 → 3.2.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/bin/memory_mesh.js +107 -186
- package/package.json +5 -2
package/bin/memory_mesh.js
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* YAMO MemoryMesh CLI -
|
|
4
|
+
* YAMO MemoryMesh CLI - Singularity Edition (v3.2.0)
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* State-of-the-art interface for semantic memory orchestration.
|
|
7
|
+
* Features: Interactive progress, beautiful formatting, and bulk ingestion.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Command } from 'commander';
|
|
11
11
|
import { MemoryMesh } from '../lib/memory/index.js';
|
|
12
12
|
import { createLogger } from '../lib/utils/logger.js';
|
|
13
|
+
import pc from 'picocolors';
|
|
14
|
+
import cliProgress from 'cli-progress';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { glob } from 'glob';
|
|
13
18
|
|
|
14
|
-
const logger = createLogger('memory-mesh-cli');
|
|
15
19
|
const program = new Command();
|
|
16
20
|
|
|
17
21
|
program
|
|
18
22
|
.name('memory-mesh')
|
|
19
|
-
.description('
|
|
20
|
-
.version('3.
|
|
23
|
+
.description('YAMO Semantic Subconscious - Protocol-Native CLI')
|
|
24
|
+
.version('3.2.0');
|
|
25
|
+
|
|
26
|
+
// Helper for beautiful logging
|
|
27
|
+
const ui = {
|
|
28
|
+
info: (msg) => console.log(`${pc.blue('ℹ')} ${pc.white(msg)}`),
|
|
29
|
+
success: (msg) => console.log(`${pc.green('✔')} ${pc.green(msg)}`),
|
|
30
|
+
warn: (msg) => console.log(`${pc.yellow('⚠')} ${pc.yellow(msg)}`),
|
|
31
|
+
error: (msg) => console.error(`${pc.red('✖')} ${pc.red(msg)}`),
|
|
32
|
+
header: (msg) => console.log(`\n${pc.bold(pc.cyan('── ' + msg + ' ' + '─'.repeat(50 - msg.length - 4)))}\n`)
|
|
33
|
+
};
|
|
21
34
|
|
|
22
35
|
// 1. Store/Ingest Command
|
|
23
36
|
program
|
|
@@ -28,83 +41,89 @@ program
|
|
|
28
41
|
.option('-t, --type <type>', 'Memory type (e.g., insight, decision, error)', 'event')
|
|
29
42
|
.option('-r, --rationale <text>', 'The constitutional rationale for this memory')
|
|
30
43
|
.option('-h, --hypothesis <text>', 'The associated hypothesis')
|
|
31
|
-
.option('--metadata <json>', 'Additional metadata (as flat flags or optional JSON)', '{}')
|
|
32
44
|
.action(async (options) => {
|
|
33
45
|
const mesh = new MemoryMesh();
|
|
34
46
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (options.type) metadata.type = options.type;
|
|
43
|
-
if (options.rationale) metadata.rationale = options.rationale;
|
|
44
|
-
if (options.hypothesis) metadata.hypothesis = options.hypothesis;
|
|
47
|
+
ui.info(`Ingesting into subconscious...`);
|
|
48
|
+
const metadata = {
|
|
49
|
+
type: options.type,
|
|
50
|
+
rationale: options.rationale,
|
|
51
|
+
hypothesis: options.hypothesis,
|
|
52
|
+
source: 'cli-manual'
|
|
53
|
+
};
|
|
45
54
|
|
|
46
55
|
const record = await mesh.add(options.content, metadata);
|
|
47
|
-
|
|
56
|
+
ui.success(`Ingested record ${pc.bold(record.id)}`);
|
|
48
57
|
} catch (err) {
|
|
49
|
-
|
|
58
|
+
ui.error(`Ingestion failed: ${err.message}`);
|
|
50
59
|
process.exit(1);
|
|
51
60
|
} finally {
|
|
52
61
|
await mesh.close();
|
|
53
62
|
}
|
|
54
63
|
});
|
|
55
64
|
|
|
56
|
-
// 2. Directory Ingest
|
|
65
|
+
// 2. Pull Command (Smart Directory Ingest)
|
|
57
66
|
program
|
|
58
|
-
.command('
|
|
59
|
-
.
|
|
60
|
-
.
|
|
61
|
-
.
|
|
62
|
-
.option('-e, --extension <ext>', 'Filter by file extension', '')
|
|
67
|
+
.command('pull')
|
|
68
|
+
.description('Smart recursive repository/directory ingestion')
|
|
69
|
+
.argument('<path>', 'Directory path to pull')
|
|
70
|
+
.option('-e, --extension <ext>', 'File extensions (comma-separated)', '.yamo,.md')
|
|
63
71
|
.option('-t, --type <type>', 'Memory type', 'documentation')
|
|
64
|
-
.option('-r, --recursive', 'Ingest subdirectories', true)
|
|
65
72
|
.action(async (dirPath, options) => {
|
|
66
73
|
const mesh = new MemoryMesh();
|
|
67
74
|
try {
|
|
75
|
+
ui.header(`Pulling Wisdom: ${dirPath}`);
|
|
76
|
+
|
|
68
77
|
const absolutePath = path.resolve(dirPath);
|
|
69
78
|
if (!fs.existsSync(absolutePath)) {
|
|
70
79
|
throw new Error(`Directory not found: ${absolutePath}`);
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
};
|
|
82
|
+
// 1. Discover files
|
|
83
|
+
const extensions = options.extension.split(',').map(e => e.trim());
|
|
84
|
+
const pattern = `**/*{${extensions.join(',')}}`;
|
|
85
|
+
|
|
86
|
+
ui.info(`Scanning for ${pc.cyan(extensions.join(' ')) } files...`);
|
|
87
|
+
|
|
88
|
+
const files = await glob(pattern, { cwd: absolutePath, absolute: true, nodir: true });
|
|
89
|
+
|
|
90
|
+
if (files.length === 0) {
|
|
91
|
+
ui.warn('No matching files found.');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
process.stdout.write(`[MemoryMesh] Found ${files.length} files to ingest...\n`);
|
|
95
|
+
ui.info(`Found ${pc.bold(files.length)} files. Starting bulk ingestion...`);
|
|
90
96
|
|
|
97
|
+
// 2. Initialize Progress Bar
|
|
98
|
+
const bar = new cliProgress.SingleBar({
|
|
99
|
+
format: `${pc.cyan('Ingesting')} |${pc.cyan('{bar}')}| {percentage}% | {value}/{total} Files | {file}`,
|
|
100
|
+
barCompleteChar: '\u2588',
|
|
101
|
+
barIncompleteChar: '\u2591',
|
|
102
|
+
hideCursor: true
|
|
103
|
+
}, cliProgress.Presets.shades_classic);
|
|
104
|
+
|
|
105
|
+
bar.start(files.length, 0, { file: 'Initializing...' });
|
|
106
|
+
|
|
107
|
+
// 3. Process
|
|
91
108
|
for (const file of files) {
|
|
109
|
+
const relativeName = path.relative(absolutePath, file);
|
|
110
|
+
bar.update(files.indexOf(file) + 1, { file: relativeName });
|
|
111
|
+
|
|
92
112
|
const content = fs.readFileSync(file, 'utf-8');
|
|
93
113
|
if (!content.trim()) continue;
|
|
94
114
|
|
|
95
|
-
|
|
96
|
-
source:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const record = await mesh.add(content, metadata);
|
|
102
|
-
process.stdout.write(` ✓ Ingested: ${metadata.source} (${record.id})\n`);
|
|
115
|
+
await mesh.add(content, {
|
|
116
|
+
source: relativeName,
|
|
117
|
+
type: options.type,
|
|
118
|
+
ingest_method: 'smart-pull'
|
|
119
|
+
});
|
|
103
120
|
}
|
|
104
121
|
|
|
105
|
-
|
|
122
|
+
bar.stop();
|
|
123
|
+
ui.success(`Successfully distilled ${pc.bold(files.length)} files into memory.`);
|
|
124
|
+
|
|
106
125
|
} catch (err) {
|
|
107
|
-
|
|
126
|
+
ui.error(`Pull failed: ${err.message}`);
|
|
108
127
|
process.exit(1);
|
|
109
128
|
} finally {
|
|
110
129
|
await mesh.close();
|
|
@@ -114,159 +133,61 @@ program
|
|
|
114
133
|
// 3. Search Command
|
|
115
134
|
program
|
|
116
135
|
.command('search')
|
|
117
|
-
.description('Perform semantic recall')
|
|
118
|
-
.argument('<query>', 'The semantic
|
|
119
|
-
.option('-l, --limit <number>', 'Number of results', '
|
|
120
|
-
.option('-f, --filter <string>', 'LanceDB SQL-style filter')
|
|
136
|
+
.description('Perform high-fidelity semantic recall')
|
|
137
|
+
.argument('<query>', 'The semantic query')
|
|
138
|
+
.option('-l, --limit <number>', 'Number of results', '5')
|
|
121
139
|
.action(async (query, options) => {
|
|
122
140
|
const mesh = new MemoryMesh();
|
|
123
141
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
ui.info(`Searching subconscious for "${pc.italic(query)}"...`);
|
|
143
|
+
const results = await mesh.search(query, { limit: parseInt(options.limit) });
|
|
144
|
+
|
|
145
|
+
if (results.length === 0) {
|
|
146
|
+
ui.warn('No relevant memories found.');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ui.header(`Recalled ${results.length} Memories`);
|
|
151
|
+
|
|
152
|
+
results.forEach((res, i) => {
|
|
153
|
+
const meta = typeof res.metadata === 'string' ? JSON.parse(res.metadata) : res.metadata;
|
|
154
|
+
const scoreColor = res.score > 0.8 ? pc.green : (res.score > 0.5 ? pc.yellow : pc.red);
|
|
155
|
+
|
|
156
|
+
console.log(`${pc.bold(pc.cyan('Memory ' + (i + 1)))} [Rel: ${scoreColor(res.score.toFixed(2))}]`);
|
|
157
|
+
console.log(`${pc.dim('ID: ' + res.id)} | ${pc.dim('Type: ' + (meta.type || 'event'))}`);
|
|
158
|
+
console.log(`${pc.white(res.content.substring(0, 300))}${res.content.length > 300 ? '...' : ''}`);
|
|
159
|
+
console.log(pc.dim('─'.repeat(40)));
|
|
127
160
|
});
|
|
128
161
|
|
|
129
|
-
process.stdout.write(`[MemoryMesh] Found ${results.length} matches.\n`);
|
|
130
|
-
process.stdout.write(mesh.formatResults(results));
|
|
131
|
-
process.stdout.write('\n');
|
|
132
162
|
} catch (err) {
|
|
133
|
-
|
|
163
|
+
ui.error(`Search failed: ${err.message}`);
|
|
134
164
|
process.exit(1);
|
|
135
165
|
} finally {
|
|
136
166
|
await mesh.close();
|
|
137
167
|
}
|
|
138
168
|
});
|
|
139
169
|
|
|
140
|
-
//
|
|
170
|
+
// 4. Stats Command
|
|
141
171
|
program
|
|
142
172
|
.command('stats')
|
|
143
|
-
.description('
|
|
173
|
+
.description('Subconscious health and database metrics')
|
|
144
174
|
.action(async () => {
|
|
145
175
|
const mesh = new MemoryMesh();
|
|
146
176
|
try {
|
|
147
177
|
const stats = await mesh.stats();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
console.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// 4. Get Command
|
|
160
|
-
program
|
|
161
|
-
.command('get')
|
|
162
|
-
.description('Retrieve a single memory by ID')
|
|
163
|
-
.requiredOption('--id <id>', 'Memory record ID')
|
|
164
|
-
.action(async (options) => {
|
|
165
|
-
const mesh = new MemoryMesh();
|
|
166
|
-
try {
|
|
167
|
-
await mesh.init();
|
|
168
|
-
const record = await mesh.get(options.id);
|
|
169
|
-
if (!record) {
|
|
170
|
-
process.stdout.write(`[MemoryMesh] No record found with id: ${options.id}\n`);
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
const meta = typeof record.metadata === 'string' ? JSON.parse(record.metadata) : record.metadata;
|
|
174
|
-
process.stdout.write(`[MemoryMesh] id: ${record.id}\n`);
|
|
175
|
-
process.stdout.write(`[MemoryMesh] content: ${record.content}\n`);
|
|
176
|
-
process.stdout.write(`[MemoryMesh] type: ${meta?.type ?? 'unknown'}\n`);
|
|
177
|
-
process.stdout.write(`[MemoryMesh] created_at: ${record.created_at}\n`);
|
|
178
|
-
process.stdout.write(`[MemoryMesh] metadata: ${JSON.stringify(meta, null, 2)}\n`);
|
|
179
|
-
} catch (err) {
|
|
180
|
-
console.error(`❌ Error: ${err.message}`);
|
|
181
|
-
process.exit(1);
|
|
182
|
-
} finally {
|
|
183
|
-
await mesh.close();
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
// 5. Delete Command
|
|
188
|
-
program
|
|
189
|
-
.command('delete')
|
|
190
|
-
.description('Delete a memory by ID')
|
|
191
|
-
.requiredOption('--id <id>', 'Memory record ID to delete')
|
|
192
|
-
.action(async (options) => {
|
|
193
|
-
const mesh = new MemoryMesh();
|
|
194
|
-
try {
|
|
195
|
-
await mesh.init();
|
|
196
|
-
await mesh.delete(options.id);
|
|
197
|
-
process.stdout.write(`[MemoryMesh] Deleted record ${options.id}\n`);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
console.error(`❌ Error: ${err.message}`);
|
|
200
|
-
process.exit(1);
|
|
201
|
-
} finally {
|
|
202
|
-
await mesh.close();
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// 6. Reflect Command
|
|
207
|
-
program
|
|
208
|
-
.command('reflect')
|
|
209
|
-
.description('Query distilled lessons from memory (wisdom distillation)')
|
|
210
|
-
.option('--topic <text>', 'Topic or query to reflect on', '')
|
|
211
|
-
.option('--lookback <n>', 'Limit results to this many lessons', '10')
|
|
212
|
-
.action(async (options) => {
|
|
213
|
-
const mesh = new MemoryMesh();
|
|
214
|
-
try {
|
|
215
|
-
await mesh.init();
|
|
216
|
-
const query = options.topic || 'lessons learned patterns errors fixes';
|
|
217
|
-
const limit = parseInt(options.lookback) || 10;
|
|
218
|
-
const lessons = await mesh.queryLessons(query, { limit });
|
|
219
|
-
if (lessons.length === 0) {
|
|
220
|
-
process.stdout.write(`[MemoryMesh] No lessons found${options.topic ? ` for topic: ${options.topic}` : ''}.\n`);
|
|
221
|
-
} else {
|
|
222
|
-
process.stdout.write(`[MemoryMesh] Reflecting on ${lessons.length} lesson(s):\n\n`);
|
|
223
|
-
for (const lesson of lessons) {
|
|
224
|
-
process.stdout.write(` scope: ${lesson.applicableScope}\n`);
|
|
225
|
-
process.stdout.write(` rule: ${lesson.preventativeRule}\n`);
|
|
226
|
-
process.stdout.write(` confidence: ${lesson.ruleConfidence}\n`);
|
|
227
|
-
process.stdout.write('\n');
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
} catch (err) {
|
|
231
|
-
console.error(`❌ Error: ${err.message}`);
|
|
232
|
-
process.exit(1);
|
|
233
|
-
} finally {
|
|
234
|
-
await mesh.close();
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// S-MORA command (RFC-0012)
|
|
239
|
-
program
|
|
240
|
-
.command('smora')
|
|
241
|
-
.description('S-MORA enhanced retrieval: HyDE-Lite + multi-channel + heritage-aware reranking (RFC-0012)')
|
|
242
|
-
.argument('<query>', 'The retrieval query')
|
|
243
|
-
.option('-l, --limit <n>', 'Max results to return', '10')
|
|
244
|
-
.option('--no-hyde', 'Disable HyDE-Lite query expansion (Layer 1)')
|
|
245
|
-
.option('--intent <items>', 'Session intent chain for heritage bonus (comma-separated)', '')
|
|
246
|
-
.option('--json', 'Output raw JSON response')
|
|
247
|
-
.action(async (query, options) => {
|
|
248
|
-
const mesh = new MemoryMesh();
|
|
249
|
-
try {
|
|
250
|
-
await mesh.init();
|
|
251
|
-
const sessionIntent = options.intent
|
|
252
|
-
? options.intent.split(',').map((s) => s.trim()).filter(Boolean)
|
|
253
|
-
: [];
|
|
254
|
-
const resp = await mesh.smora(query, {
|
|
255
|
-
limit: parseInt(options.limit) || 10,
|
|
256
|
-
enableHyDE: options.hyde !== false,
|
|
257
|
-
sessionIntent,
|
|
258
|
-
});
|
|
259
|
-
if (options.json) {
|
|
260
|
-
process.stdout.write(JSON.stringify(resp, null, 2) + '\n');
|
|
261
|
-
} else {
|
|
262
|
-
const p = resp.pipeline;
|
|
263
|
-
process.stdout.write(`[S-MORA] ${resp.results.length} result(s) | HyDE:${p.queryExpanded} heritage:${p.heritageAware} latency:${p.latencyMs}ms\n\n`);
|
|
264
|
-
for (const r of resp.results) {
|
|
265
|
-
process.stdout.write(` [${r.rrfRank}] score:${r.score.toFixed(3)} | ${r.content.slice(0, 100)}${r.content.length > 100 ? '...' : ''}\n`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
178
|
+
ui.header('MemoryMesh Subconscious Status');
|
|
179
|
+
|
|
180
|
+
const statusColor = stats.isConnected ? pc.green : pc.red;
|
|
181
|
+
|
|
182
|
+
console.log(`${pc.bold('Status:')} ${statusColor(stats.isConnected ? 'CONNECTED' : 'DISCONNECTED')}`);
|
|
183
|
+
console.log(`${pc.bold('Memories:')} ${pc.cyan(stats.count)} entries`);
|
|
184
|
+
console.log(`${pc.bold('Skills:')} ${pc.cyan(stats.totalSkills)} synthesized`);
|
|
185
|
+
console.log(`${pc.bold('Engine:')} LanceDB (Vector Index)`);
|
|
186
|
+
console.log(`${pc.bold('Model:')} ${pc.dim(stats.embedding.primary?.modelName || 'Unknown')}`);
|
|
187
|
+
console.log(`${pc.bold('Path:')} ${pc.dim(stats.uri)}`);
|
|
188
|
+
|
|
268
189
|
} catch (err) {
|
|
269
|
-
|
|
190
|
+
ui.error(`Stats failed: ${err.message}`);
|
|
270
191
|
process.exit(1);
|
|
271
192
|
} finally {
|
|
272
193
|
await mesh.close();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yamo/memory-mesh",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Portable semantic memory system with Layer 0 Scrubber for YAMO agents (v3 Singularity Edition)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/memory/index.js",
|
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
"commander": "^14.0.3",
|
|
30
30
|
"onnxruntime-node": "^1.18.0",
|
|
31
31
|
"pino": "^10.3.1",
|
|
32
|
-
"pino-pretty": "^13.1.3"
|
|
32
|
+
"pino-pretty": "^13.1.3",
|
|
33
|
+
"cli-progress": "^3.12.0",
|
|
34
|
+
"picocolors": "^1.1.1",
|
|
35
|
+
"glob": "^11.0.1"
|
|
33
36
|
},
|
|
34
37
|
"author": "Soverane Labs",
|
|
35
38
|
"license": "MIT",
|