orquesta-cli 0.2.104 → 0.2.105
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.
|
@@ -2,10 +2,12 @@ import { commandRegistry } from './registry.js';
|
|
|
2
2
|
import { clearCommand } from './clear.js';
|
|
3
3
|
import { compactCommand } from './compact.js';
|
|
4
4
|
import { memoryCommand } from './memory.js';
|
|
5
|
+
import { recallCommand } from './recall.js';
|
|
5
6
|
import { helpCommand } from './help.js';
|
|
6
7
|
commandRegistry.register(clearCommand);
|
|
7
8
|
commandRegistry.register(compactCommand);
|
|
8
9
|
commandRegistry.register(memoryCommand);
|
|
10
|
+
commandRegistry.register(recallCommand);
|
|
9
11
|
commandRegistry.register(helpCommand);
|
|
10
12
|
export { commandRegistry } from './registry.js';
|
|
11
13
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { LOCAL_HOME_DIR } from '../../constants.js';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
const MAX_ENTRIES = 40;
|
|
5
|
+
const VALUE_PREVIEW = 280;
|
|
6
|
+
function stringifyValue(value) {
|
|
7
|
+
if (value == null)
|
|
8
|
+
return '';
|
|
9
|
+
if (typeof value === 'string')
|
|
10
|
+
return value;
|
|
11
|
+
try {
|
|
12
|
+
return JSON.stringify(value);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return String(value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function scanFindings(excludeSessionId) {
|
|
19
|
+
const sessionsDir = path.join(LOCAL_HOME_DIR, 'sessions');
|
|
20
|
+
let sessionIds;
|
|
21
|
+
try {
|
|
22
|
+
sessionIds = fs.readdirSync(sessionsDir);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
const entries = [];
|
|
28
|
+
for (const sessionId of sessionIds) {
|
|
29
|
+
if (excludeSessionId && sessionId === excludeSessionId)
|
|
30
|
+
continue;
|
|
31
|
+
const fp = path.join(sessionsDir, sessionId, 'memory.json');
|
|
32
|
+
let stat;
|
|
33
|
+
let raw;
|
|
34
|
+
try {
|
|
35
|
+
stat = fs.statSync(fp);
|
|
36
|
+
raw = fs.readFileSync(fp, 'utf8');
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
let obj;
|
|
42
|
+
try {
|
|
43
|
+
obj = JSON.parse(raw);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
for (const [key, entry] of Object.entries(obj)) {
|
|
49
|
+
const text = stringifyValue(entry?.value).trim();
|
|
50
|
+
if (!text)
|
|
51
|
+
continue;
|
|
52
|
+
entries.push({
|
|
53
|
+
sessionId,
|
|
54
|
+
mtimeMs: stat.mtimeMs,
|
|
55
|
+
key,
|
|
56
|
+
writtenAt: entry?.writtenAt,
|
|
57
|
+
source: entry?.source,
|
|
58
|
+
text,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
entries.sort((a, b) => (b.mtimeMs - a.mtimeMs) || (b.writtenAt || '').localeCompare(a.writtenAt || ''));
|
|
63
|
+
return entries;
|
|
64
|
+
}
|
|
65
|
+
function formatEntry(e) {
|
|
66
|
+
const when = e.writtenAt ? new Date(e.writtenAt).toLocaleString() : '';
|
|
67
|
+
const src = e.source ? `[${e.source}] ` : '';
|
|
68
|
+
const preview = e.text.length > VALUE_PREVIEW ? e.text.slice(0, VALUE_PREVIEW) + '…' : e.text;
|
|
69
|
+
const sid = e.sessionId.slice(0, 8);
|
|
70
|
+
return ` • ${src}${preview}\n ↳ session ${sid} · ${e.key}${when ? ` · ${when}` : ''}`;
|
|
71
|
+
}
|
|
72
|
+
export const recallCommand = {
|
|
73
|
+
name: '/recall',
|
|
74
|
+
description: 'Recall findings from previous sessions: /recall | /recall <query>',
|
|
75
|
+
async execute(context, rawInput) {
|
|
76
|
+
const query = rawInput.slice('/recall'.length).trim().toLowerCase();
|
|
77
|
+
const reply = (content) => {
|
|
78
|
+
const updatedMessages = [...context.messages, { role: 'assistant', content }];
|
|
79
|
+
context.setMessages(updatedMessages);
|
|
80
|
+
return { handled: true, shouldContinue: false, updatedContext: { messages: updatedMessages } };
|
|
81
|
+
};
|
|
82
|
+
const currentSessionId = context.sessionId;
|
|
83
|
+
let entries = scanFindings(currentSessionId);
|
|
84
|
+
if (entries.length === 0) {
|
|
85
|
+
return reply('No saved findings from previous sessions yet. They accumulate as the orchestrator runs.');
|
|
86
|
+
}
|
|
87
|
+
if (query) {
|
|
88
|
+
entries = entries.filter(e => e.text.toLowerCase().includes(query) || (e.source || '').toLowerCase().includes(query));
|
|
89
|
+
if (entries.length === 0) {
|
|
90
|
+
return reply(`No previous findings match "${query}".`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const shown = entries.slice(0, MAX_ENTRIES);
|
|
94
|
+
const header = query
|
|
95
|
+
? `🔎 ${shown.length}${entries.length > MAX_ENTRIES ? `/${entries.length}` : ''} finding(s) matching "${query}":`
|
|
96
|
+
: `🧠 ${shown.length} most-recent finding(s) from previous sessions:`;
|
|
97
|
+
const body = shown.map(formatEntry).join('\n');
|
|
98
|
+
const footer = entries.length > MAX_ENTRIES ? `\n\n(${entries.length - MAX_ENTRIES} more — narrow with /recall <query>)` : '';
|
|
99
|
+
return reply(`${header}\n${body}${footer}`);
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=recall.js.map
|
|
@@ -28,6 +28,10 @@ export const SLASH_COMMANDS = [
|
|
|
28
28
|
name: '/role',
|
|
29
29
|
description: 'Orchestration: per-role models, parallel, worktree, single-agent (single on|off)',
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
name: '/recall',
|
|
33
|
+
description: 'Recall findings from previous sessions: /recall | /recall <query>',
|
|
34
|
+
},
|
|
31
35
|
{
|
|
32
36
|
name: '/sync',
|
|
33
37
|
description: 'Sync LLM configs with Orquesta dashboard',
|