@crewx/memory 0.1.23-rc.6 β 0.1.23-rc.60
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/dist/cli.js +8 -8
- package/dist/cli.js.map +1 -1
- package/dist/src/engine.d.ts +18 -0
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +300 -137
- package/dist/src/engine.js.map +1 -1
- package/dist/src/mindmap.js +57 -57
- package/dist/src/mindmap.js.map +1 -1
- package/package.json +3 -3
package/dist/src/engine.js
CHANGED
|
@@ -3,18 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CATEGORIES = exports.MemoryEngine = void 0;
|
|
6
|
+
exports.CATEGORIES = exports.MemoryEngine = exports.SUMMARIZE_LOCK_STALE_MS = exports.SUMMARIZER_FAILURE_THRESHOLD = void 0;
|
|
7
7
|
exports.getMemoryConfig = getMemoryConfig;
|
|
8
8
|
exports.createMemoryEngine = createMemoryEngine;
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const os_1 = __importDefault(require("os"));
|
|
12
11
|
const child_process_1 = require("child_process");
|
|
13
12
|
const nanoid_1 = require("nanoid");
|
|
14
13
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
15
14
|
const parser_1 = require("./parser");
|
|
16
15
|
const knowledge_core_1 = require("@crewx/knowledge-core");
|
|
17
16
|
const CREWX_CLI = process.env.CREWX_CLI || 'npx crewx';
|
|
17
|
+
exports.SUMMARIZER_FAILURE_THRESHOLD = 3;
|
|
18
|
+
const SUMMARIZER_HEALTH_FILE = '.summarizer-health.json';
|
|
19
|
+
exports.SUMMARIZE_LOCK_STALE_MS = 10 * 60 * 1000;
|
|
18
20
|
function parseCrewxResponse(raw) {
|
|
19
21
|
const responseMatch = raw.match(/π Response:\s*β+\s*([\s\S]*?)(?=π Working Directory|$)/);
|
|
20
22
|
if (responseMatch?.[1]) {
|
|
@@ -103,7 +105,7 @@ class MemoryEngine {
|
|
|
103
105
|
this.summarizerTimeout =
|
|
104
106
|
config.summarizerTimeout ??
|
|
105
107
|
(yamlConfig.summarizer_timeout != null ? Number(yamlConfig.summarizer_timeout) : undefined) ??
|
|
106
|
-
|
|
108
|
+
480000;
|
|
107
109
|
this.searcherTimeout =
|
|
108
110
|
config.searcherTimeout ??
|
|
109
111
|
(yamlConfig.searcher_timeout != null ? Number(yamlConfig.searcher_timeout) : undefined) ??
|
|
@@ -121,6 +123,9 @@ class MemoryEngine {
|
|
|
121
123
|
getDirtySummaryPath(agentId) {
|
|
122
124
|
return path_1.default.join(this.getAgentDir(agentId), '.dirty-summary');
|
|
123
125
|
}
|
|
126
|
+
getSummarizeLockPath() {
|
|
127
|
+
return path_1.default.join(this.dataDir, '.summarize.lock');
|
|
128
|
+
}
|
|
124
129
|
ensureDir(dir) {
|
|
125
130
|
if (!fs_1.default.existsSync(dir)) {
|
|
126
131
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
@@ -162,6 +167,50 @@ class MemoryEngine {
|
|
|
162
167
|
this.ensureDir(this.getAgentDir(agentId));
|
|
163
168
|
fs_1.default.writeFileSync(this.getDirtySummaryPath(agentId), String(Date.now()), 'utf-8');
|
|
164
169
|
}
|
|
170
|
+
getSummarizerHealthPath() {
|
|
171
|
+
return path_1.default.join(this.dataDir, SUMMARIZER_HEALTH_FILE);
|
|
172
|
+
}
|
|
173
|
+
readSummarizerHealth() {
|
|
174
|
+
try {
|
|
175
|
+
const raw = fs_1.default.readFileSync(this.getSummarizerHealthPath(), 'utf-8');
|
|
176
|
+
const parsed = JSON.parse(raw);
|
|
177
|
+
return {
|
|
178
|
+
consecutiveFailures: typeof parsed.consecutiveFailures === 'number' ? parsed.consecutiveFailures : 0,
|
|
179
|
+
notified: Boolean(parsed.notified),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return { consecutiveFailures: 0, notified: false };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
writeSummarizerHealth(state) {
|
|
187
|
+
this.ensureDir(this.dataDir);
|
|
188
|
+
fs_1.default.writeFileSync(this.getSummarizerHealthPath(), JSON.stringify(state), 'utf-8');
|
|
189
|
+
}
|
|
190
|
+
recordSummarizerFailure() {
|
|
191
|
+
const state = this.readSummarizerHealth();
|
|
192
|
+
state.consecutiveFailures += 1;
|
|
193
|
+
if (state.consecutiveFailures >= exports.SUMMARIZER_FAILURE_THRESHOLD && !state.notified) {
|
|
194
|
+
this.notifySummarizerDegraded(state.consecutiveFailures);
|
|
195
|
+
state.notified = true;
|
|
196
|
+
}
|
|
197
|
+
this.writeSummarizerHealth(state);
|
|
198
|
+
}
|
|
199
|
+
recordSummarizerSuccess() {
|
|
200
|
+
const state = this.readSummarizerHealth();
|
|
201
|
+
if (state.consecutiveFailures === 0 && !state.notified)
|
|
202
|
+
return;
|
|
203
|
+
this.writeSummarizerHealth({ consecutiveFailures: 0, notified: false });
|
|
204
|
+
}
|
|
205
|
+
notifySummarizerDegraded(streakCount) {
|
|
206
|
+
try {
|
|
207
|
+
(0, child_process_1.execSync)(`${CREWX_CLI} notify "AI memory summarization keeps failing β falling back to mechanical summaries" --level=warning --body="callSummarizer(agent=${this.summarizerAgent}) failed ${streakCount} times in a row, so summarization switched to the fallback (mechanical) path."`, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15000 });
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
const err = e;
|
|
211
|
+
console.error(`[memory] failed to send summarizer-health notify: ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
165
214
|
loadAllEntries(agentId) {
|
|
166
215
|
const entriesDir = this.getEntriesDir(agentId);
|
|
167
216
|
if (!fs_1.default.existsSync(entriesDir))
|
|
@@ -172,7 +221,7 @@ class MemoryEngine {
|
|
|
172
221
|
const raw = fs_1.default.readFileSync(path_1.default.join(entriesDir, file), 'utf-8');
|
|
173
222
|
const { data, content } = (0, parser_1.parseFrontmatter)(raw);
|
|
174
223
|
const bodyText = content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
175
|
-
const hasBody = Boolean(bodyText) && bodyText !== '(
|
|
224
|
+
const hasBody = Boolean(bodyText) && bodyText !== '(Add details here)';
|
|
176
225
|
const entry = data;
|
|
177
226
|
entries.push({ ...entry, file, hasBody });
|
|
178
227
|
}
|
|
@@ -217,16 +266,16 @@ class MemoryEngine {
|
|
|
217
266
|
});
|
|
218
267
|
const filename = `${id}.md`;
|
|
219
268
|
const filepath = path_1.default.join(this.getEntriesDir(agentId), filename);
|
|
220
|
-
const body = options.body ?? '(
|
|
269
|
+
const body = options.body ?? '(Add details here)';
|
|
221
270
|
const content = `${frontmatter}\n\n# ${summary}\n\n${body}\n`;
|
|
222
271
|
fs_1.default.writeFileSync(filepath, content, 'utf-8');
|
|
223
|
-
console.log(`β
|
|
272
|
+
console.log(`β
Saved: data/${agentId}/entries/${filename}`);
|
|
224
273
|
console.log(` ID: ${id}`);
|
|
225
|
-
console.log(` Tags: ${tags.join(', ') || '(
|
|
274
|
+
console.log(` Tags: ${tags.join(', ') || '(none)'}`);
|
|
226
275
|
console.log(` Category: ${category}`);
|
|
227
276
|
try {
|
|
228
277
|
this.markSummaryDirty(agentId);
|
|
229
|
-
console.log(` π summary.md
|
|
278
|
+
console.log(` π summary.md update scheduled`);
|
|
230
279
|
}
|
|
231
280
|
catch (_e) {
|
|
232
281
|
}
|
|
@@ -252,11 +301,11 @@ class MemoryEngine {
|
|
|
252
301
|
}
|
|
253
302
|
const cutoff24h = new Date(Date.now() - this.shortTermHours * 60 * 60 * 1000).toISOString();
|
|
254
303
|
const shortTermEntries = entries.filter((e) => this.toEntryTimestamp(e) >= cutoff24h);
|
|
255
|
-
console.log(`# ${agentId}
|
|
304
|
+
console.log(`# ${agentId} memory summary\n`);
|
|
256
305
|
const summaryLastUpdated = fs_1.default.existsSync(summaryPath)
|
|
257
306
|
? (0, parser_1.parseFrontmatter)(fs_1.default.readFileSync(summaryPath, 'utf-8')).data.last_updated ?? '-'
|
|
258
307
|
: '-';
|
|
259
|
-
console.log(`> ${entries.length}
|
|
308
|
+
console.log(`> ${entries.length} items | topics ${topics.length} items | updated: ${summaryLastUpdated}`);
|
|
260
309
|
if (options.full) {
|
|
261
310
|
const byCat = {};
|
|
262
311
|
for (const entry of entries) {
|
|
@@ -278,7 +327,7 @@ class MemoryEngine {
|
|
|
278
327
|
return bImp - aImp;
|
|
279
328
|
return this.toEntryTimestamp(b).localeCompare(this.toEntryTimestamp(a));
|
|
280
329
|
});
|
|
281
|
-
console.log(`## π₯
|
|
330
|
+
console.log(`## π₯ last Task (24h)\n`);
|
|
282
331
|
for (const e of sortedShortTerm) {
|
|
283
332
|
const star = e.important ? 'β ' : '';
|
|
284
333
|
console.log(`- ${star}[${e.id}] ${e.summary} (${this.getPrimaryTag(e)})`);
|
|
@@ -302,7 +351,7 @@ class MemoryEngine {
|
|
|
302
351
|
const diffDays = Math.max(1, Math.floor((newest.getTime() - oldest.getTime()) / msPerDay) + 1);
|
|
303
352
|
const toMD = (d) => `${String(d.getMonth() + 1).padStart(2, '0')}/${String(d.getDate()).padStart(2, '0')}`;
|
|
304
353
|
console.log('---');
|
|
305
|
-
console.log(`π
|
|
354
|
+
console.log(`π Topic summary (${toMD(oldest)} ~ ${toMD(newest)}, ${diffDays} days)\n`);
|
|
306
355
|
}
|
|
307
356
|
}
|
|
308
357
|
console.log(summaryBody.trim());
|
|
@@ -310,7 +359,7 @@ class MemoryEngine {
|
|
|
310
359
|
else {
|
|
311
360
|
for (const tag of topics.sort()) {
|
|
312
361
|
const count = entries.filter((e) => this.getPrimaryTag(e) === tag).length;
|
|
313
|
-
console.log(`- **${tag}** (${count}
|
|
362
|
+
console.log(`- **${tag}** (${count} items)`);
|
|
314
363
|
}
|
|
315
364
|
}
|
|
316
365
|
}
|
|
@@ -343,15 +392,15 @@ class MemoryEngine {
|
|
|
343
392
|
byCategory[category] = [];
|
|
344
393
|
byCategory[category].push(entry);
|
|
345
394
|
}
|
|
346
|
-
let md = `# ${agentId}
|
|
395
|
+
let md = `# ${agentId} memory\n\n`;
|
|
347
396
|
md += `> Last Updated: ${today}\n`;
|
|
348
397
|
md += `> Total: ${entries.length} memories\n\n`;
|
|
349
|
-
md += `## π₯
|
|
398
|
+
md += `## π₯ Last ${this.recentDays} days\n\n`;
|
|
350
399
|
if (recent.length === 0) {
|
|
351
|
-
md += `(
|
|
400
|
+
md += `(none)\n\n`;
|
|
352
401
|
}
|
|
353
402
|
else {
|
|
354
|
-
md += `| ID |
|
|
403
|
+
md += `| ID | Date | Category | Summary | Details |\n`;
|
|
355
404
|
md += `|----|------|----------|------|------|\n`;
|
|
356
405
|
for (const entry of recent) {
|
|
357
406
|
const detailIcon = entry.hasBody ? 'π' : '-';
|
|
@@ -359,47 +408,43 @@ class MemoryEngine {
|
|
|
359
408
|
}
|
|
360
409
|
md += `\n`;
|
|
361
410
|
}
|
|
362
|
-
md += `## π·οΈ
|
|
411
|
+
md += `## π·οΈ By category\n\n`;
|
|
363
412
|
for (const [category, items] of Object.entries(byCategory).sort()) {
|
|
364
|
-
md += `- **${category}**: ${items.length}
|
|
413
|
+
md += `- **${category}**: ${items.length} items\n`;
|
|
365
414
|
}
|
|
366
415
|
const olderCount = entries.length - recent.length;
|
|
367
416
|
const oldestDate = entries[entries.length - 1]?.date ?? '-';
|
|
368
417
|
const newestDate = entries[0]?.date ?? '-';
|
|
369
|
-
md += `\n## π
|
|
370
|
-
md += `-
|
|
371
|
-
md += `-
|
|
418
|
+
md += `\n## π Stats\n\n`;
|
|
419
|
+
md += `- Total: ${entries.length} items (${oldestDate} ~ ${newestDate})\n`;
|
|
420
|
+
md += `- Last ${this.recentDays} days: ${recent.length} items\n`;
|
|
372
421
|
if (olderCount > 0)
|
|
373
|
-
md += `- ${this.recentDays}
|
|
422
|
+
md += `- Older than ${this.recentDays} days: ${olderCount} items\n`;
|
|
374
423
|
return md;
|
|
375
424
|
}
|
|
376
425
|
topic(agentId, topicName) {
|
|
377
426
|
const entries = this.loadAllEntries(agentId);
|
|
378
427
|
const filtered = entries.filter((e) => this.getPrimaryTag(e) === topicName);
|
|
379
428
|
if (filtered.length === 0) {
|
|
380
|
-
console.log(
|
|
429
|
+
console.log(`No memories found for topic '${topicName}'.`);
|
|
381
430
|
return;
|
|
382
431
|
}
|
|
383
|
-
console.log(`## π
|
|
432
|
+
console.log(`## π Topic: ${topicName} (${filtered.length} items)\n`);
|
|
384
433
|
for (const entry of filtered) {
|
|
385
434
|
console.log(`### [${entry.date}] ${entry.summary}`);
|
|
386
|
-
console.log(`-
|
|
387
|
-
console.log(`-
|
|
388
|
-
console.log(`-
|
|
435
|
+
console.log(`- File: data/${agentId}/entries/${entry.file}`);
|
|
436
|
+
console.log(`- Category: ${entry.category ?? '-'}`);
|
|
437
|
+
console.log(`- Tags: ${(entry.tags ?? []).join(', ') || '-'}`);
|
|
389
438
|
console.log('');
|
|
390
439
|
}
|
|
391
440
|
}
|
|
392
441
|
recent(agentId, days = 30) {
|
|
393
|
-
const
|
|
394
|
-
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
|
|
395
|
-
.toISOString()
|
|
396
|
-
.slice(0, 10);
|
|
397
|
-
const filtered = entries.filter((e) => e.date >= cutoff);
|
|
442
|
+
const filtered = this.getRecentEntries(agentId, days);
|
|
398
443
|
if (filtered.length === 0) {
|
|
399
|
-
console.log(
|
|
444
|
+
console.log(`No memories found in the last ${days} days.`);
|
|
400
445
|
return;
|
|
401
446
|
}
|
|
402
|
-
console.log(`## π₯
|
|
447
|
+
console.log(`## π₯ Last ${days} days (${filtered.length} items)\n`);
|
|
403
448
|
for (const entry of filtered) {
|
|
404
449
|
const detailIcon = entry.hasBody ? ' π' : '';
|
|
405
450
|
const timestamp = String(entry.date).includes('T')
|
|
@@ -408,10 +453,59 @@ class MemoryEngine {
|
|
|
408
453
|
console.log(`[${entry.id}] [${timestamp}] [${entry.category ?? '-'}] ${entry.summary}${detailIcon}`);
|
|
409
454
|
}
|
|
410
455
|
}
|
|
456
|
+
getRecentEntries(agentId, days = 30, opts = {}) {
|
|
457
|
+
const entries = this.loadAllEntries(agentId);
|
|
458
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
|
|
459
|
+
.toISOString()
|
|
460
|
+
.slice(0, 10);
|
|
461
|
+
const filtered = entries.filter((e) => this.resolveRecentDate(agentId, e).slice(0, 10) >= cutoff);
|
|
462
|
+
if (!opts.includeBody)
|
|
463
|
+
return filtered;
|
|
464
|
+
return filtered.map((e) => ({
|
|
465
|
+
...e,
|
|
466
|
+
body: this.readEntryBody(agentId, e.file),
|
|
467
|
+
}));
|
|
468
|
+
}
|
|
469
|
+
resolveRecentDate(agentId, entry) {
|
|
470
|
+
const updated = this.toDateString(entry.updated);
|
|
471
|
+
if (updated)
|
|
472
|
+
return updated;
|
|
473
|
+
const createdAt = this.toDateString(entry['created_at']);
|
|
474
|
+
if (createdAt)
|
|
475
|
+
return createdAt;
|
|
476
|
+
const date = this.toDateString(entry.date);
|
|
477
|
+
if (date)
|
|
478
|
+
return date;
|
|
479
|
+
try {
|
|
480
|
+
const filePath = path_1.default.join(this.getEntriesDir(agentId), entry.file);
|
|
481
|
+
return fs_1.default.statSync(filePath).mtime.toISOString();
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
return '';
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
toDateString(value) {
|
|
488
|
+
if (value &&
|
|
489
|
+
typeof value === 'object' &&
|
|
490
|
+
typeof value.toISOString === 'function') {
|
|
491
|
+
return value.toISOString();
|
|
492
|
+
}
|
|
493
|
+
return typeof value === 'string' ? value : '';
|
|
494
|
+
}
|
|
495
|
+
readEntryBody(agentId, file) {
|
|
496
|
+
try {
|
|
497
|
+
const raw = fs_1.default.readFileSync(path_1.default.join(this.getEntriesDir(agentId), file), 'utf-8');
|
|
498
|
+
const { content } = (0, parser_1.parseFrontmatter)(raw);
|
|
499
|
+
return content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
return '';
|
|
503
|
+
}
|
|
504
|
+
}
|
|
411
505
|
find(agentId, keyword) {
|
|
412
506
|
const entriesDir = this.getEntriesDir(agentId);
|
|
413
507
|
if (!fs_1.default.existsSync(entriesDir)) {
|
|
414
|
-
console.log('
|
|
508
|
+
console.log('No memories found.');
|
|
415
509
|
return;
|
|
416
510
|
}
|
|
417
511
|
const files = fs_1.default.readdirSync(entriesDir).filter((f) => f.endsWith('.md'));
|
|
@@ -423,18 +517,18 @@ class MemoryEngine {
|
|
|
423
517
|
entries.push({ ...entry, file, body });
|
|
424
518
|
}
|
|
425
519
|
if (entries.length === 0) {
|
|
426
|
-
console.log('
|
|
520
|
+
console.log('No memories found.');
|
|
427
521
|
return;
|
|
428
522
|
}
|
|
429
523
|
const kcEntries = entries;
|
|
430
524
|
const results = (0, knowledge_core_1.searchEntries)(kcEntries, keyword, { summary: 3, tags: 2, body: 1 });
|
|
431
525
|
if (results.length === 0) {
|
|
432
|
-
console.log(`'${keyword}'
|
|
526
|
+
console.log(`No related memories found for '${keyword}'.`);
|
|
433
527
|
return;
|
|
434
528
|
}
|
|
435
|
-
console.log(`## π
|
|
529
|
+
console.log(`## π Search results: "${keyword}" (${results.length} items, sorted by BM25 score)\n`);
|
|
436
530
|
for (const entry of results) {
|
|
437
|
-
const score = entry._score != null ? ` [${entry._score}
|
|
531
|
+
const score = entry._score != null ? ` [${entry._score}pts]` : '';
|
|
438
532
|
console.log(`[${entry.id}] [${entry.date}] [${this.getPrimaryTag(entry)}]${score} ${entry.summary}`);
|
|
439
533
|
}
|
|
440
534
|
const resultTags = [...new Set(results.flatMap(e => e.tags ?? []))].slice(0, 3);
|
|
@@ -470,7 +564,7 @@ class MemoryEngine {
|
|
|
470
564
|
const raw = fs_1.default.readFileSync(path_1.default.join(entriesDir, e.file), 'utf-8');
|
|
471
565
|
const { content: rawContent } = (0, parser_1.parseFrontmatter)(raw);
|
|
472
566
|
const body = rawContent.replace(/^#[^\n]*\n*/m, '').trim();
|
|
473
|
-
if (body !== '(
|
|
567
|
+
if (body !== '(Add details here)')
|
|
474
568
|
content = body;
|
|
475
569
|
}
|
|
476
570
|
catch (_err) {
|
|
@@ -494,10 +588,10 @@ class MemoryEngine {
|
|
|
494
588
|
filters.push(`category=${options.category}`);
|
|
495
589
|
if (options.tag)
|
|
496
590
|
filters.push(`tag=${options.tag}`);
|
|
497
|
-
console.log(
|
|
591
|
+
console.log(`No memories match the filters. (${filters.join(', ') || 'all'})`);
|
|
498
592
|
return;
|
|
499
593
|
}
|
|
500
|
-
console.log(`## π
|
|
594
|
+
console.log(`## π List: ${agentId} (${filtered.length} items)\n`);
|
|
501
595
|
for (const entry of filtered) {
|
|
502
596
|
console.log(`[${entry.id}] [${entry.date}] [${this.getPrimaryTag(entry)}] ${entry.summary}`);
|
|
503
597
|
}
|
|
@@ -518,7 +612,7 @@ class MemoryEngine {
|
|
|
518
612
|
stats(agentId) {
|
|
519
613
|
const entries = this.loadAllEntries(agentId);
|
|
520
614
|
if (entries.length === 0) {
|
|
521
|
-
console.log('
|
|
615
|
+
console.log('No memories found.');
|
|
522
616
|
return;
|
|
523
617
|
}
|
|
524
618
|
const byCategory = {};
|
|
@@ -532,15 +626,15 @@ class MemoryEngine {
|
|
|
532
626
|
byTag[tag] = (byTag[tag] ?? 0) + 1;
|
|
533
627
|
}
|
|
534
628
|
}
|
|
535
|
-
console.log(`# ${agentId}
|
|
536
|
-
console.log(`##
|
|
629
|
+
console.log(`# ${agentId} Stats (${entries.length} items)\n`);
|
|
630
|
+
console.log(`## By category`);
|
|
537
631
|
for (const [cat, count] of Object.entries(byCategory).sort((a, b) => b[1] - a[1])) {
|
|
538
|
-
console.log(` ${cat}: ${count}
|
|
632
|
+
console.log(` ${cat}: ${count} items`);
|
|
539
633
|
}
|
|
540
|
-
console.log(`\n##
|
|
634
|
+
console.log(`\n## Top 20 Tags`);
|
|
541
635
|
const sortedTags = Object.entries(byTag).sort((a, b) => b[1] - a[1]).slice(0, 20);
|
|
542
636
|
for (const [tag, count] of sortedTags) {
|
|
543
|
-
console.log(` ${tag}: ${count}
|
|
637
|
+
console.log(` ${tag}: ${count} items`);
|
|
544
638
|
}
|
|
545
639
|
const topCategories = Object.entries(byCategory).sort((a, b) => b[1] - a[1]).map(t => t[0]);
|
|
546
640
|
const statsCtx = {
|
|
@@ -557,7 +651,7 @@ class MemoryEngine {
|
|
|
557
651
|
tags(agentId) {
|
|
558
652
|
const entries = this.loadAllEntries(agentId);
|
|
559
653
|
if (entries.length === 0) {
|
|
560
|
-
console.log('
|
|
654
|
+
console.log('No memories found.');
|
|
561
655
|
return;
|
|
562
656
|
}
|
|
563
657
|
const byTag = {};
|
|
@@ -568,18 +662,18 @@ class MemoryEngine {
|
|
|
568
662
|
}
|
|
569
663
|
const sortedTags = Object.entries(byTag).sort((a, b) => b[1] - a[1]);
|
|
570
664
|
if (sortedTags.length === 0) {
|
|
571
|
-
console.log('
|
|
665
|
+
console.log('No tags found.');
|
|
572
666
|
return;
|
|
573
667
|
}
|
|
574
|
-
console.log(`## π·οΈ
|
|
668
|
+
console.log(`## π·οΈ All tags (${sortedTags.length} items)\n`);
|
|
575
669
|
for (const [tag, count] of sortedTags) {
|
|
576
|
-
console.log(` ${tag}: ${count}
|
|
670
|
+
console.log(` ${tag}: ${count} items`);
|
|
577
671
|
}
|
|
578
672
|
}
|
|
579
673
|
get(agentId, memoryId) {
|
|
580
674
|
const entriesDir = this.getEntriesDir(agentId);
|
|
581
675
|
if (!fs_1.default.existsSync(entriesDir)) {
|
|
582
|
-
console.log('
|
|
676
|
+
console.log('No memories found.');
|
|
583
677
|
return;
|
|
584
678
|
}
|
|
585
679
|
const files = fs_1.default.readdirSync(entriesDir).filter((f) => f.endsWith('.md'));
|
|
@@ -591,10 +685,10 @@ class MemoryEngine {
|
|
|
591
685
|
const d = data;
|
|
592
686
|
console.log(`## π ${d.summary}\n`);
|
|
593
687
|
console.log(`- ID: ${d.id}`);
|
|
594
|
-
console.log(`-
|
|
595
|
-
console.log(`-
|
|
596
|
-
console.log(`-
|
|
597
|
-
console.log(`-
|
|
688
|
+
console.log(`- Date: ${d.date}`);
|
|
689
|
+
console.log(`- Category: ${d.category ?? '-'}`);
|
|
690
|
+
console.log(`- Tags: ${(d.tags ?? []).join(', ') || '-'}`);
|
|
691
|
+
console.log(`- File: data/${agentId}/entries/${file}`);
|
|
598
692
|
console.log(`\n---\n`);
|
|
599
693
|
console.log(content.trim());
|
|
600
694
|
const graphPath = path_1.default.join(this.getAgentDir(agentId), 'graph.json');
|
|
@@ -611,7 +705,7 @@ class MemoryEngine {
|
|
|
611
705
|
nodeMap[n.id] = n;
|
|
612
706
|
});
|
|
613
707
|
console.log(`\n---\n`);
|
|
614
|
-
console.log(`## π
|
|
708
|
+
console.log(`## π Related memories (${relatedEdges.length} items)\n`);
|
|
615
709
|
for (const edge of relatedEdges) {
|
|
616
710
|
const otherId = edge.from === memoryId ? edge.to : edge.from;
|
|
617
711
|
const other = nodeMap[otherId];
|
|
@@ -638,12 +732,12 @@ class MemoryEngine {
|
|
|
638
732
|
return;
|
|
639
733
|
}
|
|
640
734
|
}
|
|
641
|
-
console.log(`ID '${memoryId}'
|
|
735
|
+
console.log(`ID '${memoryId}' not found.`);
|
|
642
736
|
}
|
|
643
737
|
update(agentId, memoryId, options = {}) {
|
|
644
738
|
const entry = this.findEntryById(agentId, memoryId);
|
|
645
739
|
if (!entry) {
|
|
646
|
-
console.log(`β ID '${memoryId}'
|
|
740
|
+
console.log(`β ID '${memoryId}' not found.`);
|
|
647
741
|
process.exit(1);
|
|
648
742
|
}
|
|
649
743
|
const { filePath, data, content } = entry;
|
|
@@ -664,11 +758,11 @@ class MemoryEngine {
|
|
|
664
758
|
else if (options['no-important']) {
|
|
665
759
|
delete newData['important'];
|
|
666
760
|
}
|
|
667
|
-
const newBody = options.body ?? existingBody ?? '(
|
|
761
|
+
const newBody = options.body ?? existingBody ?? '(Add details here)';
|
|
668
762
|
const newFrontmatter = (0, parser_1.stringifyFrontmatter)(newData);
|
|
669
763
|
const newContent = `${newFrontmatter}\n\n# ${String(newData['summary'])}\n\n${newBody}\n`;
|
|
670
764
|
fs_1.default.writeFileSync(filePath, newContent, 'utf-8');
|
|
671
|
-
console.log(`β
|
|
765
|
+
console.log(`β
Updated: ${entry.file}`);
|
|
672
766
|
console.log(` ID: ${memoryId}`);
|
|
673
767
|
if (options.summary)
|
|
674
768
|
console.log(` Summary: ${String(newData['summary'])}`);
|
|
@@ -677,28 +771,28 @@ class MemoryEngine {
|
|
|
677
771
|
if (options.tags)
|
|
678
772
|
console.log(` Tags: ${newData['tags'].join(', ')}`);
|
|
679
773
|
if (options.body)
|
|
680
|
-
console.log(` Body: (
|
|
774
|
+
console.log(` Body: (updated)`);
|
|
681
775
|
}
|
|
682
776
|
delete(agentId, memoryId, options = {}) {
|
|
683
777
|
const entry = this.findEntryById(agentId, memoryId);
|
|
684
778
|
if (!entry) {
|
|
685
|
-
console.log(`β ID '${memoryId}'
|
|
779
|
+
console.log(`β ID '${memoryId}' not found.`);
|
|
686
780
|
process.exit(1);
|
|
687
781
|
}
|
|
688
782
|
if (!options.force) {
|
|
689
|
-
console.log(`β οΈ
|
|
690
|
-
console.log(`
|
|
691
|
-
console.log(`\
|
|
692
|
-
console.log(
|
|
783
|
+
console.log(`β οΈ Delete target: [${entry.data.id}] ${entry.data.summary}`);
|
|
784
|
+
console.log(` File: ${entry.file}`);
|
|
785
|
+
console.log(`\nAdd --force to delete it.`);
|
|
786
|
+
console.log(`Example: memory delete ${agentId} ${memoryId} --force`);
|
|
693
787
|
return;
|
|
694
788
|
}
|
|
695
789
|
fs_1.default.unlinkSync(entry.filePath);
|
|
696
|
-
console.log(`ποΈ
|
|
790
|
+
console.log(`ποΈ Deleted: ${entry.file}`);
|
|
697
791
|
console.log(` ID: ${memoryId}`);
|
|
698
792
|
console.log(` Summary: ${entry.data.summary}`);
|
|
699
793
|
try {
|
|
700
794
|
this.markSummaryDirty(agentId);
|
|
701
|
-
console.log(` π summary.md
|
|
795
|
+
console.log(` π summary.md update scheduled`);
|
|
702
796
|
}
|
|
703
797
|
catch (_e) {
|
|
704
798
|
}
|
|
@@ -707,27 +801,27 @@ class MemoryEngine {
|
|
|
707
801
|
const entry1 = this.findEntryById(agentId, memoryId1);
|
|
708
802
|
const entry2 = this.findEntryById(agentId, memoryId2);
|
|
709
803
|
if (!entry1) {
|
|
710
|
-
console.log(`β ID '${memoryId1}'
|
|
804
|
+
console.log(`β ID '${memoryId1}' not found.`);
|
|
711
805
|
process.exit(1);
|
|
712
806
|
}
|
|
713
807
|
if (!entry2) {
|
|
714
|
-
console.log(`β ID '${memoryId2}'
|
|
808
|
+
console.log(`β ID '${memoryId2}' not found.`);
|
|
715
809
|
process.exit(1);
|
|
716
810
|
}
|
|
717
811
|
const body1 = entry1.content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
718
812
|
const body2 = entry2.content.replace(/^#[^\n]*\n*/m, '').trim();
|
|
719
813
|
const mergedSummary = options.summary ?? `${entry1.data.summary} + ${entry2.data.summary}`;
|
|
720
|
-
const mergedBody = `##
|
|
814
|
+
const mergedBody = `## Merged memory 1 (${entry1.data.id}, ${entry1.data.date})
|
|
721
815
|
${entry1.data.summary}
|
|
722
816
|
|
|
723
|
-
${body1 !== '(
|
|
817
|
+
${body1 !== '(Add details here)' ? body1 : '(No details)'}
|
|
724
818
|
|
|
725
819
|
---
|
|
726
820
|
|
|
727
|
-
##
|
|
821
|
+
## Merged memory 2 (${entry2.data.id}, ${entry2.data.date})
|
|
728
822
|
${entry2.data.summary}
|
|
729
823
|
|
|
730
|
-
${body2 !== '(
|
|
824
|
+
${body2 !== '(Add details here)' ? body2 : '(No details)'}`;
|
|
731
825
|
const mergedTags = [
|
|
732
826
|
...new Set([...(entry1.data.tags ?? []), ...(entry2.data.tags ?? [])]),
|
|
733
827
|
];
|
|
@@ -749,10 +843,10 @@ ${body2 !== '(μμΈ λ΄μ©μ μ¬κΈ°μ μΆκ°)' ? body2 : '(μμΈ μμ)'}`;
|
|
|
749
843
|
fs_1.default.writeFileSync(filepath, newContent, 'utf-8');
|
|
750
844
|
fs_1.default.unlinkSync(entry1.filePath);
|
|
751
845
|
fs_1.default.unlinkSync(entry2.filePath);
|
|
752
|
-
console.log(`π
|
|
753
|
-
console.log(`
|
|
754
|
-
console.log(`
|
|
755
|
-
console.log(`
|
|
846
|
+
console.log(`π Merge complete!`);
|
|
847
|
+
console.log(` New ID: ${newId}`);
|
|
848
|
+
console.log(` New file: ${filename}`);
|
|
849
|
+
console.log(` Merged memory: [${memoryId1}] + [${memoryId2}]`);
|
|
756
850
|
console.log(` Summary: ${mergedSummary}`);
|
|
757
851
|
}
|
|
758
852
|
buildFallbackSummary(entries) {
|
|
@@ -778,7 +872,7 @@ ${body2 !== '(μμΈ λ΄μ©μ μ¬κΈ°μ μΆκ°)' ? body2 : '(μμΈ μμ)'}`;
|
|
|
778
872
|
const oldest = dates[0];
|
|
779
873
|
const newest = dates[dates.length - 1];
|
|
780
874
|
const rangeLabel = oldest && newest ? `, ${toMD(oldest)}~${toMD(newest)}` : '';
|
|
781
|
-
body += `## ${topic} (${items.length}
|
|
875
|
+
body += `## ${topic} (${items.length} items${rangeLabel})\n`;
|
|
782
876
|
for (const e of items)
|
|
783
877
|
body += `- [${e.date}] ${e.summary}\n`;
|
|
784
878
|
for (const e of items)
|
|
@@ -788,22 +882,26 @@ ${body2 !== '(μμΈ λ΄μ©μ μ¬κΈ°μ μΆκ°)' ? body2 : '(μμΈ μμ)'}`;
|
|
|
788
882
|
return body;
|
|
789
883
|
}
|
|
790
884
|
callSummarizer(prompt) {
|
|
791
|
-
const tmpFile = path_1.default.join(os_1.default.tmpdir(), `memory-summarizer-${Date.now()}.txt`);
|
|
792
885
|
try {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
886
|
+
const result = (0, child_process_1.execSync)(`${CREWX_CLI} q "${this.summarizerAgent}"`, {
|
|
887
|
+
encoding: 'utf-8',
|
|
888
|
+
timeout: this.summarizerTimeout,
|
|
889
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
890
|
+
input: prompt,
|
|
891
|
+
});
|
|
892
|
+
const parsed = parseCrewxResponse(result);
|
|
893
|
+
this.recordSummarizerSuccess();
|
|
894
|
+
return parsed;
|
|
895
|
+
}
|
|
896
|
+
catch (e) {
|
|
897
|
+
const err = e;
|
|
898
|
+
const reason = err.code === 'ETIMEDOUT' || err.killed
|
|
899
|
+
? `timed out after ${this.summarizerTimeout}ms`
|
|
900
|
+
: err.message;
|
|
901
|
+
console.error(`[memory] callSummarizer failed (agent=${this.summarizerAgent}), falling back to raw entry list: ${reason}`);
|
|
902
|
+
this.recordSummarizerFailure();
|
|
798
903
|
return null;
|
|
799
904
|
}
|
|
800
|
-
finally {
|
|
801
|
-
try {
|
|
802
|
-
fs_1.default.unlinkSync(tmpFile);
|
|
803
|
-
}
|
|
804
|
-
catch (_e) {
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
905
|
}
|
|
808
906
|
generateSummary(agentId, options = {}) {
|
|
809
907
|
const allEntries = this.loadAllEntries(agentId);
|
|
@@ -861,13 +959,13 @@ ${body2 !== '(μμΈ λ΄μ©μ μ¬κΈ°μ μΆκ°)' ? body2 : '(μμΈ μμ)'}`;
|
|
|
861
959
|
for (const entry of newItems) {
|
|
862
960
|
const importantTag = entry.important ? ' [important]' : '';
|
|
863
961
|
p += `- [${entry.id}] [${entry.date}] [${this.getPrimaryTag(entry)}]${importantTag} ${entry.summary}`;
|
|
864
|
-
if (entry.body && entry.body !== '(
|
|
962
|
+
if (entry.body && entry.body !== '(Add details here)') {
|
|
865
963
|
p += ` β ${entry.body.slice(0, 200)}`;
|
|
866
964
|
}
|
|
867
965
|
p += ` (file: entries/${entry.file})\n`;
|
|
868
966
|
}
|
|
869
967
|
p += `\n## Instructions:\n`;
|
|
870
|
-
p += `- Group by topic, each topic as ## heading with entry count and date range, e.g. "## mcp-http (4
|
|
968
|
+
p += `- Group by topic, each topic as ## heading with entry count and date range, e.g. "## mcp-http (4 items, 01/28~02/14)"\n`;
|
|
871
969
|
p += `- Write concise Korean summary per topic (2-5 sentences)\n`;
|
|
872
970
|
p += `- After each topic summary, list relevant entry file links as "β [entries/filename](entries/filename)"\n`;
|
|
873
971
|
p += `- Highlight key decisions, insights, and action items\n`;
|
|
@@ -917,76 +1015,141 @@ ${body2 !== '(μμΈ λ΄μ©μ μ¬κΈ°μ μΆκ°)' ? body2 : '(μμΈ μμ)'}`;
|
|
|
917
1015
|
summarize(agentId, options = {}) {
|
|
918
1016
|
const entries = this.loadAllEntries(agentId);
|
|
919
1017
|
if (entries.length === 0) {
|
|
920
|
-
console.log('
|
|
1018
|
+
console.log('No memories found.');
|
|
921
1019
|
return;
|
|
922
1020
|
}
|
|
923
|
-
console.log(`π
|
|
1021
|
+
console.log(`π Starting summary generation (${entries.length} items entries)\n`);
|
|
924
1022
|
const result = this.generateSummary(agentId, { force: options.force });
|
|
925
1023
|
if (result.status === 'up-to-date') {
|
|
926
|
-
console.log('β
summary.md
|
|
1024
|
+
console.log('β
summary.md is up to date.');
|
|
927
1025
|
}
|
|
928
1026
|
else if (result.status === 'fallback') {
|
|
929
|
-
console.log(`π
|
|
1027
|
+
console.log(`π Fallback summary generated (AI call failed)`);
|
|
930
1028
|
}
|
|
931
1029
|
else if (result.status === 'updated') {
|
|
932
|
-
console.log(`π€
|
|
1030
|
+
console.log(`π€ Summary complete (${result.newEntries} items applied)`);
|
|
1031
|
+
}
|
|
1032
|
+
console.log(` File: data/${agentId}/summary.md`);
|
|
1033
|
+
console.log(` entries: ${result.entryCount} items`);
|
|
1034
|
+
}
|
|
1035
|
+
isSummarizeLockStale(lockPath) {
|
|
1036
|
+
let content;
|
|
1037
|
+
try {
|
|
1038
|
+
content = fs_1.default.readFileSync(lockPath, 'utf-8');
|
|
1039
|
+
}
|
|
1040
|
+
catch {
|
|
1041
|
+
return true;
|
|
1042
|
+
}
|
|
1043
|
+
const pidMatch = content.match(/^pid=(\d+)/m);
|
|
1044
|
+
const pid = pidMatch ? Number(pidMatch[1]) : NaN;
|
|
1045
|
+
if (Number.isFinite(pid)) {
|
|
1046
|
+
try {
|
|
1047
|
+
process.kill(pid, 0);
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
catch (e) {
|
|
1051
|
+
const err = e;
|
|
1052
|
+
if (err.code === 'ESRCH')
|
|
1053
|
+
return true;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
const stat = fs_1.default.statSync(lockPath);
|
|
1058
|
+
return Date.now() - stat.mtimeMs > exports.SUMMARIZE_LOCK_STALE_MS;
|
|
1059
|
+
}
|
|
1060
|
+
catch {
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
acquireSummarizeLock() {
|
|
1065
|
+
const lockPath = this.getSummarizeLockPath();
|
|
1066
|
+
const content = `pid=${process.pid}\nmtime=${new Date().toISOString()}\n`;
|
|
1067
|
+
try {
|
|
1068
|
+
const fd = fs_1.default.openSync(lockPath, 'wx');
|
|
1069
|
+
fs_1.default.writeSync(fd, content);
|
|
1070
|
+
fs_1.default.closeSync(fd);
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
catch (e) {
|
|
1074
|
+
const err = e;
|
|
1075
|
+
if (err.code !== 'EEXIST')
|
|
1076
|
+
throw err;
|
|
1077
|
+
}
|
|
1078
|
+
if (!this.isSummarizeLockStale(lockPath)) {
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
fs_1.default.writeFileSync(lockPath, content, 'utf-8');
|
|
1082
|
+
return true;
|
|
1083
|
+
}
|
|
1084
|
+
releaseSummarizeLock() {
|
|
1085
|
+
try {
|
|
1086
|
+
fs_1.default.unlinkSync(this.getSummarizeLockPath());
|
|
1087
|
+
}
|
|
1088
|
+
catch {
|
|
933
1089
|
}
|
|
934
|
-
console.log(` νμΌ: data/${agentId}/summary.md`);
|
|
935
|
-
console.log(` μνΈλ¦¬: ${result.entryCount}κ°`);
|
|
936
1090
|
}
|
|
937
1091
|
summarizeDirty() {
|
|
938
1092
|
if (!fs_1.default.existsSync(this.dataDir)) {
|
|
939
|
-
console.log(`π‘
|
|
1093
|
+
console.log(`π‘ Register debounce cron:`);
|
|
940
1094
|
console.log(`npx cron add "*/1 * * * *" "npx memory summarize-dirty" --mode command --name "memory-debounce"`);
|
|
941
1095
|
return;
|
|
942
1096
|
}
|
|
943
|
-
|
|
944
|
-
.
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
const
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1097
|
+
if (!this.acquireSummarizeLock()) {
|
|
1098
|
+
console.log(`βοΈ summarize-dirty is already running (lock held) β skipping this run.`);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
try {
|
|
1102
|
+
const agentDirs = fs_1.default
|
|
1103
|
+
.readdirSync(this.dataDir, { withFileTypes: true })
|
|
1104
|
+
.filter((entry) => entry.isDirectory())
|
|
1105
|
+
.map((entry) => entry.name);
|
|
1106
|
+
for (const agentId of agentDirs) {
|
|
1107
|
+
const dirtyPath = this.getDirtySummaryPath(agentId);
|
|
1108
|
+
if (!fs_1.default.existsSync(dirtyPath))
|
|
1109
|
+
continue;
|
|
1110
|
+
try {
|
|
1111
|
+
const result = this.generateSummary(agentId);
|
|
1112
|
+
if (result.status === 'updated') {
|
|
1113
|
+
console.log(`π€ ${agentId} summary.md updated (${result.newEntries} items applied)`);
|
|
1114
|
+
}
|
|
1115
|
+
else if (result.status === 'fallback') {
|
|
1116
|
+
console.log(`π ${agentId} Fallback summary generated (AI call failed)`);
|
|
1117
|
+
}
|
|
1118
|
+
else if (result.status === 'up-to-date') {
|
|
1119
|
+
console.log(`β
${agentId} summary.md is up to date.`);
|
|
1120
|
+
}
|
|
1121
|
+
else if (result.status === 'empty') {
|
|
1122
|
+
console.log(`βΉοΈ ${agentId} has no memories to summarize.`);
|
|
1123
|
+
}
|
|
1124
|
+
fs_1.default.unlinkSync(dirtyPath);
|
|
961
1125
|
}
|
|
962
|
-
|
|
963
|
-
console.
|
|
1126
|
+
catch (e) {
|
|
1127
|
+
console.error(`β ${agentId} summary.md update failed: ${e.message}`);
|
|
964
1128
|
}
|
|
965
|
-
fs_1.default.unlinkSync(dirtyPath);
|
|
966
|
-
}
|
|
967
|
-
catch (e) {
|
|
968
|
-
console.error(`β ${agentId} summary.md κ°±μ μ€ν¨: ${e.message}`);
|
|
969
1129
|
}
|
|
970
1130
|
}
|
|
971
|
-
|
|
1131
|
+
finally {
|
|
1132
|
+
this.releaseSummarizeLock();
|
|
1133
|
+
}
|
|
1134
|
+
console.log(`π‘ Register debounce cron:`);
|
|
972
1135
|
console.log(`npx cron add "*/1 * * * *" "npx memory summarize-dirty" --mode command --name "memory-debounce"`);
|
|
973
1136
|
}
|
|
974
1137
|
search(agentId, query) {
|
|
975
1138
|
const entries = this.loadAllEntries(agentId);
|
|
976
1139
|
if (entries.length === 0) {
|
|
977
|
-
console.log('
|
|
1140
|
+
console.log('No memories found.');
|
|
978
1141
|
return;
|
|
979
1142
|
}
|
|
980
1143
|
const memoryList = entries
|
|
981
1144
|
.map((e) => `[${e.id}] [${e.date}] [${this.getPrimaryTag(e)}] ${e.summary}`)
|
|
982
1145
|
.join('\n');
|
|
983
|
-
const task =
|
|
1146
|
+
const task = `Memory list:\n${memoryList}\n\nQuestion: "${query}"`;
|
|
984
1147
|
try {
|
|
985
|
-
console.log(`π "${query}"
|
|
1148
|
+
console.log(`π "${query}" searching... (${this.searcherAgent})\n`);
|
|
986
1149
|
const result = (0, child_process_1.execSync)(`${CREWX_CLI} q "${this.searcherAgent} ${task.replace(/"/g, '\\"').replace(/\n/g, ' ')}"`, { encoding: 'utf-8', timeout: this.searcherTimeout, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
987
1150
|
const parsed = parseCrewxResponse(result);
|
|
988
1151
|
if (parsed) {
|
|
989
|
-
console.log(`## π§
|
|
1152
|
+
console.log(`## π§ Semantic search results\n`);
|
|
990
1153
|
console.log(parsed);
|
|
991
1154
|
}
|
|
992
1155
|
else {
|
|
@@ -996,12 +1159,12 @@ ${body2 !== '(μμΈ λ΄μ©μ μ¬κΈ°μ μΆκ°)' ? body2 : '(μμΈ μμ)'}`;
|
|
|
996
1159
|
catch (error) {
|
|
997
1160
|
const err = error;
|
|
998
1161
|
if (err.code === 'ETIMEDOUT' || err.killed) {
|
|
999
|
-
console.log('
|
|
1162
|
+
console.log('Search failed: timed out (max 5 minutes)');
|
|
1000
1163
|
}
|
|
1001
1164
|
else {
|
|
1002
|
-
console.log('
|
|
1165
|
+
console.log('Search failed:', err.message);
|
|
1003
1166
|
}
|
|
1004
|
-
console.log('\nπ‘ Tip:
|
|
1167
|
+
console.log('\nπ‘ Tip: try keyword search with the find command.');
|
|
1005
1168
|
}
|
|
1006
1169
|
}
|
|
1007
1170
|
}
|