claude-recall 0.25.1 → 0.26.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/README.md +16 -13
- package/dist/cli/claude-recall-cli.js +93 -38
- package/dist/cli/commands/hook-commands.js +121 -86
- package/dist/cli/commands/repair.js +16 -1
- package/dist/hooks/failure-detectors.js +1 -1
- package/dist/hooks/llm-classifier.js +14 -4
- package/dist/hooks/memory-stop-hook.js +87 -15
- package/dist/hooks/post-compact-reload.js +1 -1
- package/dist/hooks/shared.js +21 -1
- package/dist/hooks/tool-outcome-watcher.js +5 -6
- package/dist/mcp/prompts-handler.js +25 -48
- package/dist/mcp/resources-handler.js +1 -1
- package/dist/mcp/server.js +92 -58
- package/dist/mcp/session-manager.js +34 -107
- package/dist/mcp/tools/memory-tools.js +6 -71
- package/dist/mcp/transports/stdio.js +50 -140
- package/dist/memory/storage.js +165 -40
- package/dist/pi/extension.js +7 -24
- package/dist/services/action-pattern-detector.js +4 -4
- package/dist/services/config.js +18 -1
- package/dist/services/database-manager.js +64 -68
- package/dist/services/failure-extractor.js +3 -3
- package/dist/services/memory.js +36 -6
- package/dist/services/preference-extractor.js +4 -7
- package/dist/services/process-manager.js +11 -1
- package/dist/services/promotion-engine.js +1 -2
- package/dist/shared/directives.js +22 -0
- package/dist/shared/event-processors.js +6 -5
- package/package.json +17 -8
|
@@ -42,12 +42,50 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
43
|
exports.handleMemoryStop = handleMemoryStop;
|
|
44
44
|
const shared_1 = require("./shared");
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
45
47
|
const memory_1 = require("../services/memory");
|
|
46
48
|
const config_1 = require("../services/config");
|
|
47
49
|
const failure_detectors_1 = require("./failure-detectors");
|
|
50
|
+
const llm_classifier_1 = require("./llm-classifier");
|
|
48
51
|
const outcome_storage_1 = require("../services/outcome-storage");
|
|
49
52
|
const event_processors_1 = require("../shared/event-processors");
|
|
50
53
|
const MAX_STORE = 3;
|
|
54
|
+
/**
|
|
55
|
+
* Debounce state for the heavy pipeline. Claude Code fires Stop after EVERY
|
|
56
|
+
* assistant turn, not at session end — without a debounce the full pipeline
|
|
57
|
+
* (episode insert, up to two Haiku calls, failure scan, promotion cycle,
|
|
58
|
+
* prune) runs per turn. Citations are still scanned every turn (cheap, and
|
|
59
|
+
* they keep cite_count — the anti-demotion signal — fresh).
|
|
60
|
+
*/
|
|
61
|
+
const STOP_DEBOUNCE_MS = (() => {
|
|
62
|
+
const raw = parseInt(process.env.CLAUDE_RECALL_STOP_DEBOUNCE_MS || '300000', 10); // 5 min
|
|
63
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : 300000;
|
|
64
|
+
})();
|
|
65
|
+
function stopStateFile(sessionId) {
|
|
66
|
+
return path.join((0, shared_1.hookStateDir)(), `memory-stop-${sessionId.replace(/[^a-zA-Z0-9-]/g, '_')}.json`);
|
|
67
|
+
}
|
|
68
|
+
function shouldRunHeavyPipeline(sessionId) {
|
|
69
|
+
if (STOP_DEBOUNCE_MS === 0)
|
|
70
|
+
return true; // debounce disabled
|
|
71
|
+
const file = stopStateFile(sessionId);
|
|
72
|
+
try {
|
|
73
|
+
const state = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
74
|
+
if (typeof state.lastHeavyRun === 'number' && Date.now() - state.lastHeavyRun < STOP_DEBOUNCE_MS) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Missing/corrupt state file → treat as never run
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
fs.writeFileSync(file, JSON.stringify({ lastHeavyRun: Date.now() }));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Non-fatal: worst case the pipeline runs again next turn
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
51
89
|
async function handleMemoryStop(input) {
|
|
52
90
|
const transcriptPath = input?.transcript_path ?? '';
|
|
53
91
|
if (!transcriptPath) {
|
|
@@ -59,14 +97,14 @@ async function handleMemoryStop(input) {
|
|
|
59
97
|
(0, shared_1.hookLog)('memory-stop', 'No transcript entries found');
|
|
60
98
|
return;
|
|
61
99
|
}
|
|
62
|
-
//
|
|
100
|
+
// Debounced: run only the cheap citation scan on most turns
|
|
101
|
+
const sessionId = input?.session_id || 'default';
|
|
102
|
+
if (!shouldRunHeavyPipeline(sessionId)) {
|
|
103
|
+
scanForCitations(transcriptPath);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
63
106
|
const outcomeStorage = outcome_storage_1.OutcomeStorage.getInstance();
|
|
64
107
|
const projectId = config_1.ConfigService.getInstance().getProjectId();
|
|
65
|
-
const episodeId = outcomeStorage.createEpisode({
|
|
66
|
-
project_id: projectId,
|
|
67
|
-
session_id: input?.session_id,
|
|
68
|
-
source: 'memory-stop',
|
|
69
|
-
});
|
|
70
108
|
// Extract user-only texts, filter, then batch-classify in one API call
|
|
71
109
|
const textsWithIndex = [];
|
|
72
110
|
for (let i = 0; i < entries.length; i++) {
|
|
@@ -79,10 +117,19 @@ async function handleMemoryStop(input) {
|
|
|
79
117
|
}
|
|
80
118
|
if (textsWithIndex.length === 0) {
|
|
81
119
|
(0, shared_1.hookLog)('memory-stop', 'No classifiable text in transcript entries');
|
|
82
|
-
// Still scan for citations — assistant messages may contain them
|
|
120
|
+
// Still scan for citations — assistant messages may contain them.
|
|
121
|
+
// No episode was created yet, so this early return leaves no dangling row.
|
|
83
122
|
scanForCitations(transcriptPath);
|
|
84
123
|
return;
|
|
85
124
|
}
|
|
125
|
+
// Create an episode for this session — AFTER the early returns so the
|
|
126
|
+
// frequent no-classifiable-text path doesn't leave permanently
|
|
127
|
+
// outcome-less episode rows.
|
|
128
|
+
const episodeId = outcomeStorage.createEpisode({
|
|
129
|
+
project_id: projectId,
|
|
130
|
+
session_id: input?.session_id,
|
|
131
|
+
source: 'memory-stop',
|
|
132
|
+
});
|
|
86
133
|
const results = await (0, shared_1.classifyBatch)(textsWithIndex.map((t) => t.text));
|
|
87
134
|
let stored = 0;
|
|
88
135
|
for (let i = 0; i < results.length; i++) {
|
|
@@ -156,7 +203,7 @@ async function handleMemoryStop(input) {
|
|
|
156
203
|
outcome_summary: `${stored} memories, ${allFailures.length} failures (${toolFailures.length} from tool events)`,
|
|
157
204
|
});
|
|
158
205
|
// Generate candidate lessons from high-confidence failures
|
|
159
|
-
generateCandidateLessons(allFailures, episodeId, projectId);
|
|
206
|
+
await generateCandidateLessons(allFailures, episodeId, projectId);
|
|
160
207
|
// Run promotion cycle
|
|
161
208
|
try {
|
|
162
209
|
const { PromotionEngine } = await Promise.resolve().then(() => __importStar(require('../services/promotion-engine')));
|
|
@@ -241,7 +288,7 @@ function scanForCitations(transcriptPath) {
|
|
|
241
288
|
try {
|
|
242
289
|
outcome_storage_1.OutcomeStorage.getInstance().recordHelpful(bestKey);
|
|
243
290
|
}
|
|
244
|
-
catch { }
|
|
291
|
+
catch { /* best-effort — ignore */ }
|
|
245
292
|
(0, shared_1.hookLog)('memory-stop', `Citation matched: "${cite.substring(0, 50)}" → rule ${bestKey} (containment=${bestScore.toFixed(3)})`);
|
|
246
293
|
}
|
|
247
294
|
else {
|
|
@@ -304,7 +351,7 @@ function extractRuleContent(value) {
|
|
|
304
351
|
/**
|
|
305
352
|
* Scan the last 200 transcript entries for failure signals and store up to 3.
|
|
306
353
|
*/
|
|
307
|
-
function detectAndStoreFailures(transcriptPath,
|
|
354
|
+
function detectAndStoreFailures(transcriptPath, _episodeId) {
|
|
308
355
|
try {
|
|
309
356
|
const entries = (0, shared_1.readTranscriptTail)(transcriptPath, 200);
|
|
310
357
|
if (entries.length === 0) {
|
|
@@ -378,17 +425,42 @@ function getToolFailureEvents(outcomeStorage) {
|
|
|
378
425
|
return [];
|
|
379
426
|
}
|
|
380
427
|
}
|
|
428
|
+
/** lesson_kind values extractHindsightHint may legitimately return. */
|
|
429
|
+
const VALID_LESSON_KINDS = new Set([
|
|
430
|
+
'rule', 'preference', 'anti_pattern', 'workflow', 'debug_fix', 'failure_preventer',
|
|
431
|
+
]);
|
|
381
432
|
/**
|
|
382
433
|
* Generate candidate lessons from high-confidence failures.
|
|
383
434
|
* Deduplicates against existing lessons and increments evidence count for similar ones.
|
|
435
|
+
*
|
|
436
|
+
* The lesson text must be failure-specific. Detectors emit a constant
|
|
437
|
+
* what_should_do ("Check command syntax..."), so using it verbatim made every
|
|
438
|
+
* unrelated failure "similar" to every other — evidence counts inflated across
|
|
439
|
+
* unrelated failures and the promotion engine could only ever promote generic
|
|
440
|
+
* boilerplate. Prefer an LLM hindsight hint; without one, ground the generic
|
|
441
|
+
* remedy in what actually failed so similarity matching compares failures,
|
|
442
|
+
* not the shared remedy string.
|
|
384
443
|
*/
|
|
385
|
-
function generateCandidateLessons(failures, episodeId, projectId) {
|
|
444
|
+
async function generateCandidateLessons(failures, episodeId, projectId) {
|
|
386
445
|
try {
|
|
387
446
|
const outcomeStorage = outcome_storage_1.OutcomeStorage.getInstance();
|
|
388
447
|
for (const f of failures) {
|
|
389
448
|
if (f.confidence < 0.7)
|
|
390
449
|
continue;
|
|
391
|
-
|
|
450
|
+
let lessonText = `${f.content.what_should_do} (failure: ${f.content.what_failed})`;
|
|
451
|
+
let lessonKind = 'failure_preventer';
|
|
452
|
+
let appliesWhen = extractTagsFromContext(f.content.context);
|
|
453
|
+
const hint = await (0, llm_classifier_1.extractHindsightHint)(`${f.content.what_failed}${f.content.why_failed ? ` — ${f.content.why_failed}` : ''}`, f.content.context || '');
|
|
454
|
+
if (hint) {
|
|
455
|
+
lessonText = hint.hint_text;
|
|
456
|
+
if (VALID_LESSON_KINDS.has(hint.hint_kind)) {
|
|
457
|
+
lessonKind = hint.hint_kind;
|
|
458
|
+
}
|
|
459
|
+
if (hint.applies_when.length > 0) {
|
|
460
|
+
appliesWhen = hint.applies_when;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const similar = outcomeStorage.findSimilarLessons(lessonText, projectId);
|
|
392
464
|
if (similar.length > 0) {
|
|
393
465
|
outcomeStorage.incrementEvidenceCount(similar[0].id);
|
|
394
466
|
}
|
|
@@ -396,9 +468,9 @@ function generateCandidateLessons(failures, episodeId, projectId) {
|
|
|
396
468
|
outcomeStorage.createCandidateLesson({
|
|
397
469
|
project_id: projectId,
|
|
398
470
|
episode_id: episodeId,
|
|
399
|
-
lesson_text:
|
|
400
|
-
lesson_kind:
|
|
401
|
-
applies_when:
|
|
471
|
+
lesson_text: lessonText,
|
|
472
|
+
lesson_kind: lessonKind,
|
|
473
|
+
applies_when: appliesWhen,
|
|
402
474
|
outcome_type: 'negative',
|
|
403
475
|
reward_band: -1,
|
|
404
476
|
confidence: f.confidence,
|
|
@@ -39,7 +39,7 @@ function formatRules(rules) {
|
|
|
39
39
|
}
|
|
40
40
|
return sections.join('\n\n');
|
|
41
41
|
}
|
|
42
|
-
async function handlePostCompactReload(
|
|
42
|
+
async function handlePostCompactReload(_input) {
|
|
43
43
|
try {
|
|
44
44
|
const projectId = config_1.ConfigService.getInstance().getProjectId();
|
|
45
45
|
const rules = memory_1.MemoryService.getInstance().loadActiveRules(projectId);
|
package/dist/hooks/shared.js
CHANGED
|
@@ -41,6 +41,8 @@ exports.jaccardSimilarity = jaccardSimilarity;
|
|
|
41
41
|
exports.isDuplicate = isDuplicate;
|
|
42
42
|
exports.storeMemory = storeMemory;
|
|
43
43
|
exports.searchExisting = searchExisting;
|
|
44
|
+
exports.claudeRecallDir = claudeRecallDir;
|
|
45
|
+
exports.hookStateDir = hookStateDir;
|
|
44
46
|
exports.safeErrorMessage = safeErrorMessage;
|
|
45
47
|
exports.hookLog = hookLog;
|
|
46
48
|
exports.readTranscriptTail = readTranscriptTail;
|
|
@@ -194,6 +196,24 @@ function searchExisting(query) {
|
|
|
194
196
|
const memoryService = memory_1.MemoryService.getInstance();
|
|
195
197
|
return memoryService.search(query);
|
|
196
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Base data directory — same as the database. The env override keeps tests
|
|
201
|
+
* (and custom setups) away from the real ~/.claude-recall.
|
|
202
|
+
*/
|
|
203
|
+
function claudeRecallDir() {
|
|
204
|
+
return process.env.CLAUDE_RECALL_DB_PATH || path.join(os.homedir(), '.claude-recall');
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Directory for per-session hook state files (pending failures, debounce
|
|
208
|
+
* markers). Created on demand.
|
|
209
|
+
*/
|
|
210
|
+
function hookStateDir() {
|
|
211
|
+
const dir = path.join(claudeRecallDir(), 'hook-state');
|
|
212
|
+
if (!fs.existsSync(dir)) {
|
|
213
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
214
|
+
}
|
|
215
|
+
return dir;
|
|
216
|
+
}
|
|
197
217
|
/**
|
|
198
218
|
* Extract a safe error message without exposing stack traces or internal paths.
|
|
199
219
|
*/
|
|
@@ -209,7 +229,7 @@ function safeErrorMessage(err) {
|
|
|
209
229
|
*/
|
|
210
230
|
function hookLog(hookName, message) {
|
|
211
231
|
try {
|
|
212
|
-
const logDir = path.join(
|
|
232
|
+
const logDir = path.join(claudeRecallDir(), 'hook-logs');
|
|
213
233
|
if (!fs.existsSync(logDir)) {
|
|
214
234
|
fs.mkdirSync(logDir, { recursive: true });
|
|
215
235
|
}
|
|
@@ -53,7 +53,6 @@ exports.shouldCaptureFailure = shouldCaptureFailure;
|
|
|
53
53
|
exports.handleToolFailure = handleToolFailure;
|
|
54
54
|
const fs = __importStar(require("fs"));
|
|
55
55
|
const path = __importStar(require("path"));
|
|
56
|
-
const os = __importStar(require("os"));
|
|
57
56
|
const shared_1 = require("./shared");
|
|
58
57
|
const memory_1 = require("../services/memory");
|
|
59
58
|
const outcome_storage_1 = require("../services/outcome-storage");
|
|
@@ -84,7 +83,7 @@ const MCP_ERROR_PATTERNS = [
|
|
|
84
83
|
];
|
|
85
84
|
// --- State management (Bash fix pairing only) ---
|
|
86
85
|
function getStateDir() {
|
|
87
|
-
return
|
|
86
|
+
return (0, shared_1.hookStateDir)();
|
|
88
87
|
}
|
|
89
88
|
function getStatePath(sessionId) {
|
|
90
89
|
return path.join(getStateDir(), `${sessionId}-failures.json`);
|
|
@@ -306,8 +305,10 @@ async function handleBashSuccess(command, sessionId) {
|
|
|
306
305
|
if (!matched && (0, shared_1.jaccardSimilarity)(pf.command, command) >= FIX_JACCARD_THRESHOLD) {
|
|
307
306
|
try {
|
|
308
307
|
const memoryService = memory_1.MemoryService.getInstance();
|
|
309
|
-
|
|
310
|
-
|
|
308
|
+
// Merge, don't replace — the failure memory's what_failed/why_failed
|
|
309
|
+
// fields are the counterfactual context the fix enriches
|
|
310
|
+
memoryService.mergeIntoValue(pf.memoryKey, {
|
|
311
|
+
what_should_do: `Fix: ${truncate(command, 200)}`,
|
|
311
312
|
});
|
|
312
313
|
recordOutcomeEvent('Bash', { command }, `Success after previous failure: ${truncate(pf.command, 100)}`, 0);
|
|
313
314
|
(0, shared_1.hookLog)(HOOK_NAME, `Paired fix: "${truncate(command, 60)}" → ${pf.memoryKey}`);
|
|
@@ -330,7 +331,6 @@ async function handleWriteToolOutcome(input) {
|
|
|
330
331
|
const output = input.tool_output ?? '';
|
|
331
332
|
const toolName = input.tool_name;
|
|
332
333
|
const filePath = input.tool_input?.file_path ?? '';
|
|
333
|
-
const sessionId = input.session_id ?? 'unknown';
|
|
334
334
|
// Check for error patterns
|
|
335
335
|
const errorMatch = WRITE_ERROR_PATTERNS.find(p => p.test(output));
|
|
336
336
|
if (errorMatch) {
|
|
@@ -365,7 +365,6 @@ async function handleWriteToolOutcome(input) {
|
|
|
365
365
|
async function handleMcpToolOutcome(input) {
|
|
366
366
|
const output = input.tool_output ?? '';
|
|
367
367
|
const toolName = input.tool_name;
|
|
368
|
-
const sessionId = input.session_id ?? 'unknown';
|
|
369
368
|
// Skip Claude Recall's own tools to avoid self-referential loops
|
|
370
369
|
if (toolName.includes('claude-recall') || toolName.includes('claude_recall'))
|
|
371
370
|
return;
|
|
@@ -14,7 +14,11 @@ class PromptsHandler {
|
|
|
14
14
|
constructor() {
|
|
15
15
|
this.logger = logging_1.LoggingService.getInstance();
|
|
16
16
|
this.memoryService = memory_1.MemoryService.getInstance();
|
|
17
|
-
this.memoryStorage = this.memoryService.
|
|
17
|
+
this.memoryStorage = this.memoryService.getStorage();
|
|
18
|
+
}
|
|
19
|
+
/** Build a spec-conformant text message. */
|
|
20
|
+
textMessage(text) {
|
|
21
|
+
return { role: 'user', content: { type: 'text', text } };
|
|
18
22
|
}
|
|
19
23
|
/**
|
|
20
24
|
* Handle prompts/list request
|
|
@@ -166,14 +170,11 @@ class PromptsHandler {
|
|
|
166
170
|
return {
|
|
167
171
|
description: 'User coding preferences automatically injected',
|
|
168
172
|
messages: [
|
|
169
|
-
|
|
170
|
-
role: 'system',
|
|
171
|
-
content: `# User Coding Preferences
|
|
173
|
+
this.textMessage(`# User Coding Preferences
|
|
172
174
|
|
|
173
175
|
${preferencesText}
|
|
174
176
|
|
|
175
|
-
Apply these preferences when generating code or making suggestions.`
|
|
176
|
-
}
|
|
177
|
+
Apply these preferences when generating code or making suggestions.`)
|
|
177
178
|
]
|
|
178
179
|
};
|
|
179
180
|
}
|
|
@@ -195,14 +196,11 @@ Apply these preferences when generating code or making suggestions.`
|
|
|
195
196
|
? `Project knowledge about ${topic}`
|
|
196
197
|
: 'All project knowledge',
|
|
197
198
|
messages: [
|
|
198
|
-
|
|
199
|
-
role: 'system',
|
|
200
|
-
content: `# Project Knowledge
|
|
199
|
+
this.textMessage(`# Project Knowledge
|
|
201
200
|
|
|
202
201
|
${contextText}
|
|
203
202
|
|
|
204
|
-
Use this information when working with the project.`
|
|
205
|
-
}
|
|
203
|
+
Use this information when working with the project.`)
|
|
206
204
|
]
|
|
207
205
|
};
|
|
208
206
|
}
|
|
@@ -220,14 +218,11 @@ Use this information when working with the project.`
|
|
|
220
218
|
return {
|
|
221
219
|
description: 'Recent correction patterns to avoid mistakes',
|
|
222
220
|
messages: [
|
|
223
|
-
|
|
224
|
-
role: 'system',
|
|
225
|
-
content: `# Correction Patterns
|
|
221
|
+
this.textMessage(`# Correction Patterns
|
|
226
222
|
|
|
227
223
|
${correctionsText}
|
|
228
224
|
|
|
229
|
-
Avoid these patterns when generating code.`
|
|
230
|
-
}
|
|
225
|
+
Avoid these patterns when generating code.`)
|
|
231
226
|
]
|
|
232
227
|
};
|
|
233
228
|
}
|
|
@@ -247,14 +242,11 @@ Avoid these patterns when generating code.`
|
|
|
247
242
|
return {
|
|
248
243
|
description: `Full context for: ${task}`,
|
|
249
244
|
messages: [
|
|
250
|
-
|
|
251
|
-
role: 'system',
|
|
252
|
-
content: `# Relevant Context
|
|
245
|
+
this.textMessage(`# Relevant Context
|
|
253
246
|
|
|
254
247
|
${contextText}
|
|
255
248
|
|
|
256
|
-
Use this context when working on: ${task}`
|
|
257
|
-
}
|
|
249
|
+
Use this context when working on: ${task}`)
|
|
258
250
|
]
|
|
259
251
|
};
|
|
260
252
|
}
|
|
@@ -270,9 +262,7 @@ Use this context when working on: ${task}`
|
|
|
270
262
|
return {
|
|
271
263
|
description: 'Analyze conversation for preference extraction',
|
|
272
264
|
messages: [
|
|
273
|
-
|
|
274
|
-
role: 'system',
|
|
275
|
-
content: `You are analyzing a conversation to extract user coding preferences.
|
|
265
|
+
this.textMessage(`You are analyzing a conversation to extract user coding preferences.
|
|
276
266
|
|
|
277
267
|
Extract any preferences about:
|
|
278
268
|
- Programming languages and frameworks
|
|
@@ -293,12 +283,8 @@ Return a JSON array with format:
|
|
|
293
283
|
}
|
|
294
284
|
]
|
|
295
285
|
|
|
296
|
-
Be conservative - only extract clear, explicit preferences.`
|
|
297
|
-
}
|
|
298
|
-
{
|
|
299
|
-
role: 'user',
|
|
300
|
-
content: `Analyze this conversation for preferences:\n\n${conversation}`
|
|
301
|
-
}
|
|
286
|
+
Be conservative - only extract clear, explicit preferences.`),
|
|
287
|
+
this.textMessage(`Analyze this conversation for preferences:\n\n${conversation}`)
|
|
302
288
|
]
|
|
303
289
|
};
|
|
304
290
|
}
|
|
@@ -468,20 +454,17 @@ Be conservative - only extract clear, explicit preferences.`
|
|
|
468
454
|
return {
|
|
469
455
|
description: topic ? `Active rules about ${topic}` : `All active rules (${totalRules} total)`,
|
|
470
456
|
messages: [
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
? `# Active Rules\n\nApply these rules when working on this project.\n\n${body}`
|
|
475
|
-
: 'No active rules found. This may be a new project.'
|
|
476
|
-
}
|
|
457
|
+
this.textMessage(body
|
|
458
|
+
? `# Active Rules\n\nApply these rules when working on this project.\n\n${body}`
|
|
459
|
+
: 'No active rules found. This may be a new project.')
|
|
477
460
|
]
|
|
478
461
|
};
|
|
479
462
|
}
|
|
480
463
|
/**
|
|
481
464
|
* Get session-review prompt — summarizes session outcomes and lessons
|
|
482
465
|
*/
|
|
483
|
-
async getSessionReviewPrompt(
|
|
484
|
-
|
|
466
|
+
async getSessionReviewPrompt(_sessionId) {
|
|
467
|
+
const sections = [];
|
|
485
468
|
try {
|
|
486
469
|
const outcomeStorage = outcome_storage_1.OutcomeStorage.getInstance();
|
|
487
470
|
const projectId = config_1.ConfigService.getInstance().getProjectId();
|
|
@@ -508,12 +491,9 @@ Be conservative - only extract clear, explicit preferences.`
|
|
|
508
491
|
return {
|
|
509
492
|
description: 'Session outcome review and lessons learned',
|
|
510
493
|
messages: [
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
? `# Session Review\n\n${sections.join('\n\n')}`
|
|
515
|
-
: '# Session Review\n\nNo outcome data available yet.'
|
|
516
|
-
}
|
|
494
|
+
this.textMessage(sections.length > 0
|
|
495
|
+
? `# Session Review\n\n${sections.join('\n\n')}`
|
|
496
|
+
: '# Session Review\n\nNo outcome data available yet.')
|
|
517
497
|
]
|
|
518
498
|
};
|
|
519
499
|
}
|
|
@@ -524,10 +504,7 @@ Be conservative - only extract clear, explicit preferences.`
|
|
|
524
504
|
return {
|
|
525
505
|
description: 'Error',
|
|
526
506
|
messages: [
|
|
527
|
-
{
|
|
528
|
-
role: 'system',
|
|
529
|
-
content: `Error: ${message}`
|
|
530
|
-
}
|
|
507
|
+
this.textMessage(`Error: ${message}`)
|
|
531
508
|
]
|
|
532
509
|
};
|
|
533
510
|
}
|
|
@@ -13,7 +13,7 @@ class ResourcesHandler {
|
|
|
13
13
|
constructor() {
|
|
14
14
|
this.logger = logging_1.LoggingService.getInstance();
|
|
15
15
|
this.memoryService = memory_1.MemoryService.getInstance();
|
|
16
|
-
this.memoryStorage = this.memoryService.
|
|
16
|
+
this.memoryStorage = this.memoryService.getStorage();
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Handle resources/list request
|