@pcircle/memesh 2.9.1 ā 2.9.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.de.md +119 -78
- package/README.es.md +116 -75
- package/README.fr.md +116 -75
- package/README.id.md +115 -74
- package/README.ja.md +111 -70
- package/README.ko.md +116 -75
- package/README.md +113 -72
- package/README.th.md +118 -77
- package/README.vi.md +116 -75
- package/README.zh-CN.md +120 -79
- package/README.zh-TW.md +116 -75
- package/dist/core/GitCommandParser.d.ts +11 -0
- package/dist/core/GitCommandParser.d.ts.map +1 -0
- package/dist/core/GitCommandParser.js +43 -0
- package/dist/core/GitCommandParser.js.map +1 -0
- package/dist/core/HookIntegration.d.ts +0 -3
- package/dist/core/HookIntegration.d.ts.map +1 -1
- package/dist/core/HookIntegration.js +6 -30
- package/dist/core/HookIntegration.js.map +1 -1
- package/dist/embeddings/EmbeddingService.d.ts +3 -0
- package/dist/embeddings/EmbeddingService.d.ts.map +1 -1
- package/dist/embeddings/EmbeddingService.js +32 -6
- package/dist/embeddings/EmbeddingService.js.map +1 -1
- package/dist/embeddings/InMemoryVectorAdapter.d.ts +15 -0
- package/dist/embeddings/InMemoryVectorAdapter.d.ts.map +1 -0
- package/dist/embeddings/InMemoryVectorAdapter.js +58 -0
- package/dist/embeddings/InMemoryVectorAdapter.js.map +1 -0
- package/dist/embeddings/SqliteVecAdapter.d.ts +15 -0
- package/dist/embeddings/SqliteVecAdapter.d.ts.map +1 -0
- package/dist/embeddings/SqliteVecAdapter.js +107 -0
- package/dist/embeddings/SqliteVecAdapter.js.map +1 -0
- package/dist/embeddings/VectorExtension.d.ts +2 -4
- package/dist/embeddings/VectorExtension.d.ts.map +1 -1
- package/dist/embeddings/VectorExtension.js.map +1 -1
- package/dist/embeddings/VectorSearchAdapter.d.ts +17 -0
- package/dist/embeddings/VectorSearchAdapter.d.ts.map +1 -0
- package/dist/embeddings/VectorSearchAdapter.js +2 -0
- package/dist/embeddings/VectorSearchAdapter.js.map +1 -0
- package/dist/embeddings/index.d.ts +6 -1
- package/dist/embeddings/index.d.ts.map +1 -1
- package/dist/embeddings/index.js +4 -1
- package/dist/embeddings/index.js.map +1 -1
- package/dist/knowledge-graph/ContentHasher.d.ts +4 -0
- package/dist/knowledge-graph/ContentHasher.d.ts.map +1 -0
- package/dist/knowledge-graph/ContentHasher.js +8 -0
- package/dist/knowledge-graph/ContentHasher.js.map +1 -0
- package/dist/knowledge-graph/KGSearchEngine.d.ts +36 -0
- package/dist/knowledge-graph/KGSearchEngine.d.ts.map +1 -0
- package/dist/knowledge-graph/KGSearchEngine.js +257 -0
- package/dist/knowledge-graph/KGSearchEngine.js.map +1 -0
- package/dist/knowledge-graph/index.d.ts +18 -7
- package/dist/knowledge-graph/index.d.ts.map +1 -1
- package/dist/knowledge-graph/index.js +147 -242
- package/dist/knowledge-graph/index.js.map +1 -1
- package/dist/mcp/ServerInitializer.d.ts.map +1 -1
- package/dist/mcp/ServerInitializer.js +1 -1
- package/dist/mcp/ServerInitializer.js.map +1 -1
- package/dist/mcp/StdinBufferManager.d.ts +11 -0
- package/dist/mcp/StdinBufferManager.d.ts.map +1 -0
- package/dist/mcp/StdinBufferManager.js +62 -0
- package/dist/mcp/StdinBufferManager.js.map +1 -0
- package/dist/mcp/daemon/StdioProxyClient.d.ts.map +1 -1
- package/dist/mcp/daemon/StdioProxyClient.js +6 -0
- package/dist/mcp/daemon/StdioProxyClient.js.map +1 -1
- package/dist/mcp/handlers/HookToolHandler.d.ts +10 -0
- package/dist/mcp/handlers/HookToolHandler.d.ts.map +1 -0
- package/dist/mcp/handlers/HookToolHandler.js +92 -0
- package/dist/mcp/handlers/HookToolHandler.js.map +1 -0
- package/dist/mcp/handlers/MemoryToolHandler.d.ts +21 -0
- package/dist/mcp/handlers/MemoryToolHandler.d.ts.map +1 -0
- package/dist/mcp/handlers/MemoryToolHandler.js +430 -0
- package/dist/mcp/handlers/MemoryToolHandler.js.map +1 -0
- package/dist/mcp/handlers/SystemToolHandler.d.ts +14 -0
- package/dist/mcp/handlers/SystemToolHandler.d.ts.map +1 -0
- package/dist/mcp/handlers/SystemToolHandler.js +224 -0
- package/dist/mcp/handlers/SystemToolHandler.js.map +1 -0
- package/dist/mcp/handlers/ToolHandlers.d.ts +4 -17
- package/dist/mcp/handlers/ToolHandlers.d.ts.map +1 -1
- package/dist/mcp/handlers/ToolHandlers.js +19 -689
- package/dist/mcp/handlers/ToolHandlers.js.map +1 -1
- package/dist/mcp/handlers/index.d.ts +3 -0
- package/dist/mcp/handlers/index.d.ts.map +1 -1
- package/dist/mcp/handlers/index.js +3 -0
- package/dist/mcp/handlers/index.js.map +1 -1
- package/dist/mcp/server-bootstrap.js +24 -59
- package/dist/mcp/server-bootstrap.js.map +1 -1
- package/dist/mcp/tools/create-entities.d.ts.map +1 -1
- package/dist/mcp/tools/create-entities.js +18 -24
- package/dist/mcp/tools/create-entities.js.map +1 -1
- package/dist/memory/MemorySearchEngine.d.ts +17 -0
- package/dist/memory/MemorySearchEngine.d.ts.map +1 -0
- package/dist/memory/MemorySearchEngine.js +88 -0
- package/dist/memory/MemorySearchEngine.js.map +1 -0
- package/dist/memory/ProactiveRecaller.d.ts +26 -0
- package/dist/memory/ProactiveRecaller.d.ts.map +1 -0
- package/dist/memory/ProactiveRecaller.js +96 -0
- package/dist/memory/ProactiveRecaller.js.map +1 -0
- package/dist/memory/UnifiedMemoryStore.d.ts +1 -0
- package/dist/memory/UnifiedMemoryStore.d.ts.map +1 -1
- package/dist/memory/UnifiedMemoryStore.js +7 -63
- package/dist/memory/UnifiedMemoryStore.js.map +1 -1
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -1
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +0 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/tracing/index.d.ts +0 -1
- package/dist/utils/tracing/index.d.ts.map +1 -1
- package/dist/utils/tracing/index.js +0 -1
- package/dist/utils/tracing/index.js.map +1 -1
- package/package.json +2 -11
- package/plugin.json +1 -1
- package/scripts/hooks/__tests__/post-tool-use-recall.test.js +192 -0
- package/scripts/hooks/__tests__/session-start-recall.test.js +86 -0
- package/scripts/hooks/post-tool-use-recall-utils.js +74 -0
- package/scripts/hooks/post-tool-use.js +79 -0
- package/scripts/hooks/session-start-recall-utils.js +40 -0
- package/scripts/hooks/session-start.js +66 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { buildSessionRecallQuery, formatRecallOutput } from '../session-start-recall-utils.js';
|
|
3
|
+
|
|
4
|
+
describe('buildSessionRecallQuery', () => {
|
|
5
|
+
it('combines project name and commits', () => {
|
|
6
|
+
const result = buildSessionRecallQuery('my-project', ['add login page', 'fix header bug']);
|
|
7
|
+
expect(result).toBe('my-project add login page fix header bug');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('handles empty commits array', () => {
|
|
11
|
+
const result = buildSessionRecallQuery('my-project', []);
|
|
12
|
+
expect(result).toBe('my-project');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('handles undefined commits', () => {
|
|
16
|
+
const result = buildSessionRecallQuery('my-project');
|
|
17
|
+
expect(result).toBe('my-project');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('strips conventional commit prefixes', () => {
|
|
21
|
+
const commits = [
|
|
22
|
+
'fix: resolve null pointer',
|
|
23
|
+
'feat(auth): add OAuth support',
|
|
24
|
+
'chore(deps): update dependencies',
|
|
25
|
+
'refactor: simplify logic',
|
|
26
|
+
];
|
|
27
|
+
const result = buildSessionRecallQuery('app', commits);
|
|
28
|
+
expect(result).toBe('app resolve null pointer add OAuth support update dependencies simplify logic');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('filters out empty strings after stripping prefixes', () => {
|
|
32
|
+
const commits = ['fix:', 'feat(scope): '];
|
|
33
|
+
const result = buildSessionRecallQuery('app', commits);
|
|
34
|
+
expect(result).toBe('app');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('formatRecallOutput', () => {
|
|
39
|
+
it('formats results with similarity percentage', () => {
|
|
40
|
+
const results = [
|
|
41
|
+
{ name: 'Entity1', observations: ['obs1', 'obs2'], similarity: 0.85 },
|
|
42
|
+
];
|
|
43
|
+
const output = formatRecallOutput(results);
|
|
44
|
+
expect(output).toBe(' - Entity1 (85%): obs1; obs2');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns empty string for empty results', () => {
|
|
48
|
+
expect(formatRecallOutput([])).toBe('');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('returns empty string for null results', () => {
|
|
52
|
+
expect(formatRecallOutput(null)).toBe('');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('returns empty string for undefined results', () => {
|
|
56
|
+
expect(formatRecallOutput(undefined)).toBe('');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('limits observations to 2', () => {
|
|
60
|
+
const results = [
|
|
61
|
+
{ name: 'Entity1', observations: ['obs1', 'obs2', 'obs3', 'obs4'], similarity: 0.7 },
|
|
62
|
+
];
|
|
63
|
+
const output = formatRecallOutput(results);
|
|
64
|
+
expect(output).toBe(' - Entity1 (70%): obs1; obs2');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('formats multiple results on separate lines', () => {
|
|
68
|
+
const results = [
|
|
69
|
+
{ name: 'A', observations: ['a1'], similarity: 0.9 },
|
|
70
|
+
{ name: 'B', observations: ['b1', 'b2'], similarity: 0.6 },
|
|
71
|
+
];
|
|
72
|
+
const output = formatRecallOutput(results);
|
|
73
|
+
const lines = output.split('\n');
|
|
74
|
+
expect(lines).toHaveLength(2);
|
|
75
|
+
expect(lines[0]).toBe(' - A (90%): a1');
|
|
76
|
+
expect(lines[1]).toBe(' - B (60%): b1; b2');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('rounds similarity percentage', () => {
|
|
80
|
+
const results = [
|
|
81
|
+
{ name: 'X', observations: ['x1'], similarity: 0.456 },
|
|
82
|
+
];
|
|
83
|
+
const output = formatRecallOutput(results);
|
|
84
|
+
expect(output).toBe(' - X (46%): x1');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-Tool-Use Recall Utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for detecting test failures and errors in tool output,
|
|
5
|
+
* used by the proactive recall system in post-tool-use.js.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const TEST_PATTERNS = [
|
|
9
|
+
/vitest\s*(run|watch)?/i,
|
|
10
|
+
/jest\b/i,
|
|
11
|
+
/npm\s+test/i,
|
|
12
|
+
/npm\s+run\s+test/i,
|
|
13
|
+
/npx\s+vitest/i,
|
|
14
|
+
/npx\s+jest/i,
|
|
15
|
+
/pytest\b/i,
|
|
16
|
+
/bun\s+test/i,
|
|
17
|
+
/mocha\b/i,
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a command string is a test runner invocation.
|
|
22
|
+
* @param {string} command - Shell command to check
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function isTestCommand(command) {
|
|
26
|
+
if (!command) return false;
|
|
27
|
+
return TEST_PATTERNS.some(p => p.test(command));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extract test failure context (test name + error message) from test output.
|
|
32
|
+
* Returns null if no failure is detected.
|
|
33
|
+
* @param {string} output - Test runner stdout/stderr
|
|
34
|
+
* @returns {{ testName: string, errorMessage: string } | null}
|
|
35
|
+
*/
|
|
36
|
+
export function extractTestFailureContext(output) {
|
|
37
|
+
if (!output) return null;
|
|
38
|
+
const hasFailure = /FAIL|failed|failing|\u2715|\u2717|error/i.test(output);
|
|
39
|
+
if (!hasFailure) return null;
|
|
40
|
+
|
|
41
|
+
const fileMatch = output.match(/FAIL\s+(\S+\.(?:test|spec)\.\S+)/i)
|
|
42
|
+
|| output.match(/(\S+\.(?:test|spec)\.\S+)/i);
|
|
43
|
+
const testName = fileMatch ? fileMatch[1] : 'unknown test';
|
|
44
|
+
|
|
45
|
+
const errorMatch = output.match(/(?:Error|AssertionError|AssertError|expect).*$/m)
|
|
46
|
+
|| output.match(/(?:\u2715|\u2717)\s*(.+)$/m);
|
|
47
|
+
const errorMessage = errorMatch ? errorMatch[0].trim() : 'test failed';
|
|
48
|
+
|
|
49
|
+
return { testName, errorMessage };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Build a search query from a test failure context.
|
|
54
|
+
* Strips path and test/spec suffix from the test file name.
|
|
55
|
+
* @param {string} testName - Test file name or path
|
|
56
|
+
* @param {string} errorMessage - Error message
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
export function buildTestFailureQuery(testName, errorMessage) {
|
|
60
|
+
const shortName = testName.replace(/^.*[/\\]/, '').replace(/\.(test|spec)\.\w+$/, '');
|
|
61
|
+
return `${shortName} ${errorMessage}`.trim();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build a search query from an error type and message.
|
|
66
|
+
* Uses only the first line of the error message.
|
|
67
|
+
* @param {string} errorType - Error class name (e.g. "TypeError")
|
|
68
|
+
* @param {string} errorMessage - Error message (may be multiline)
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
export function buildErrorQuery(errorType, errorMessage) {
|
|
72
|
+
const firstLine = (errorMessage || '').split('\n')[0].trim();
|
|
73
|
+
return `${errorType || 'Error'} ${firstLine}`.trim();
|
|
74
|
+
}
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
updateEntityMetadata,
|
|
35
35
|
addObservation,
|
|
36
36
|
} from './hook-utils.js';
|
|
37
|
+
import { isTestCommand, extractTestFailureContext, buildTestFailureQuery, buildErrorQuery } from './post-tool-use-recall-utils.js';
|
|
37
38
|
import fs from 'fs';
|
|
38
39
|
import path from 'path';
|
|
39
40
|
|
|
@@ -729,6 +730,81 @@ function detectPlanFile(toolData) {
|
|
|
729
730
|
}
|
|
730
731
|
}
|
|
731
732
|
|
|
733
|
+
// ============================================================================
|
|
734
|
+
// Proactive Recall on Test Failure / Error Detection
|
|
735
|
+
// ============================================================================
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Trigger proactive recall on test failure or error detection.
|
|
739
|
+
* Writes results to proactive-recall.json for HookToolHandler.
|
|
740
|
+
* @param {Object} toolData - Normalized tool data
|
|
741
|
+
*/
|
|
742
|
+
function triggerProactiveRecall(toolData) {
|
|
743
|
+
try {
|
|
744
|
+
const recallFile = path.join(STATE_DIR, 'proactive-recall.json');
|
|
745
|
+
let query = null;
|
|
746
|
+
let trigger = null;
|
|
747
|
+
|
|
748
|
+
// Test failure detection
|
|
749
|
+
if (toolData.toolName === 'Bash' && toolData.arguments?.command) {
|
|
750
|
+
if (isTestCommand(toolData.arguments.command) && !toolData.success) {
|
|
751
|
+
const ctx = extractTestFailureContext(toolData._raw?.output || '');
|
|
752
|
+
if (ctx) {
|
|
753
|
+
query = buildTestFailureQuery(ctx.testName, ctx.errorMessage);
|
|
754
|
+
trigger = 'test-failure';
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Error detection (non-test failures)
|
|
760
|
+
if (!trigger && !toolData.success && toolData._raw?.output) {
|
|
761
|
+
const errorMatch = toolData._raw.output.match(/(\w*Error):\s*(.+)/);
|
|
762
|
+
if (errorMatch) {
|
|
763
|
+
query = buildErrorQuery(errorMatch[1], errorMatch[2]);
|
|
764
|
+
trigger = 'error-detection';
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (!query || !trigger) return;
|
|
769
|
+
|
|
770
|
+
// Build FTS5 query
|
|
771
|
+
const ftsTokens = query.split(/\s+/)
|
|
772
|
+
.filter(t => t.length > 2)
|
|
773
|
+
.slice(0, 8)
|
|
774
|
+
.map(t => `"${t.replace(/"/g, '""')}"*`)
|
|
775
|
+
.join(' OR ');
|
|
776
|
+
|
|
777
|
+
if (!ftsTokens) return;
|
|
778
|
+
|
|
779
|
+
const sql = `
|
|
780
|
+
SELECT e.name,
|
|
781
|
+
(SELECT json_group_array(content) FROM observations o WHERE o.entity_id = e.id) as observations_json
|
|
782
|
+
FROM entities e
|
|
783
|
+
JOIN entities_fts ON entities_fts.rowid = e.id
|
|
784
|
+
WHERE entities_fts MATCH ?
|
|
785
|
+
ORDER BY bm25(entities_fts, 10.0, 5.0)
|
|
786
|
+
LIMIT 3
|
|
787
|
+
`;
|
|
788
|
+
|
|
789
|
+
const result = sqliteQueryJSON(MEMESH_DB_PATH, sql, [ftsTokens]);
|
|
790
|
+
if (!result || result.length === 0) return;
|
|
791
|
+
|
|
792
|
+
const recallData = {
|
|
793
|
+
trigger,
|
|
794
|
+
query,
|
|
795
|
+
timestamp: Date.now(),
|
|
796
|
+
results: result.map(r => ({
|
|
797
|
+
name: r.name,
|
|
798
|
+
observations: JSON.parse(r.observations_json || '[]').filter(Boolean).slice(0, 2),
|
|
799
|
+
})),
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
writeJSONFile(recallFile, recallData);
|
|
803
|
+
} catch (error) {
|
|
804
|
+
logError('proactive-recall-trigger', error);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
732
808
|
// ============================================================================
|
|
733
809
|
// Main PostToolUse Logic
|
|
734
810
|
// ============================================================================
|
|
@@ -778,6 +854,9 @@ async function postToolUse() {
|
|
|
778
854
|
// Detect anomalies
|
|
779
855
|
const anomalies = detectAnomalies(toolData, sessionContext);
|
|
780
856
|
|
|
857
|
+
// Trigger proactive recall on test failure or error
|
|
858
|
+
triggerProactiveRecall(toolData);
|
|
859
|
+
|
|
781
860
|
// Fire async writes in parallel
|
|
782
861
|
const pendingWrites = [contextWritePromise];
|
|
783
862
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-Start Recall Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utility functions for proactive memory recall during session start.
|
|
5
|
+
* Builds search queries from project context and formats recall results.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build FTS5 search query from project name and recent commits.
|
|
10
|
+
* Strips conventional commit prefixes (fix:, feat(scope):, etc.)
|
|
11
|
+
* @param {string} projectName
|
|
12
|
+
* @param {string[]} recentCommits
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
export function buildSessionRecallQuery(projectName, recentCommits = []) {
|
|
16
|
+
const parts = [projectName];
|
|
17
|
+
if (recentCommits.length > 0) {
|
|
18
|
+
const cleaned = recentCommits
|
|
19
|
+
.map(msg => msg.replace(/^(fix|feat|chore|refactor|perf|docs|test|style|ci)\(?[^)]*\)?:\s*/i, '').trim())
|
|
20
|
+
.filter(msg => msg.length > 0);
|
|
21
|
+
parts.push(...cleaned);
|
|
22
|
+
}
|
|
23
|
+
return parts.join(' ').trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Format recall results for hook output display.
|
|
28
|
+
* Shows max 2 observations per entity.
|
|
29
|
+
* @param {Array<{name: string, observations: string[], similarity: number}>} results
|
|
30
|
+
* @returns {string} Formatted output (empty string if no results)
|
|
31
|
+
*/
|
|
32
|
+
export function formatRecallOutput(results) {
|
|
33
|
+
if (!results || results.length === 0) return '';
|
|
34
|
+
const lines = results.map(r => {
|
|
35
|
+
const pct = Math.round(r.similarity * 100);
|
|
36
|
+
const obs = r.observations.slice(0, 2).join('; ');
|
|
37
|
+
return ` - ${r.name} (${pct}%): ${obs}`;
|
|
38
|
+
});
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
@@ -27,8 +27,10 @@ import {
|
|
|
27
27
|
queryActivePlans,
|
|
28
28
|
renderTimelineCompact,
|
|
29
29
|
} from './hook-utils.js';
|
|
30
|
+
import { buildSessionRecallQuery, formatRecallOutput } from './session-start-recall-utils.js';
|
|
30
31
|
import fs from 'fs';
|
|
31
32
|
import path from 'path';
|
|
33
|
+
import { execFileSync } from 'child_process';
|
|
32
34
|
|
|
33
35
|
// ============================================================================
|
|
34
36
|
// File Paths
|
|
@@ -444,6 +446,67 @@ function displayActivePlans() {
|
|
|
444
446
|
}
|
|
445
447
|
}
|
|
446
448
|
|
|
449
|
+
/**
|
|
450
|
+
* Proactive memory recall ā queries KG for memories related to current project.
|
|
451
|
+
* Uses project name + last 3 git commits as search query.
|
|
452
|
+
*/
|
|
453
|
+
function recallProactiveMemories() {
|
|
454
|
+
try {
|
|
455
|
+
const projectName = path.basename(process.cwd());
|
|
456
|
+
|
|
457
|
+
let recentCommits = [];
|
|
458
|
+
try {
|
|
459
|
+
const gitOutput = execFileSync('git', ['log', '--oneline', '-3', '--format=%s'], {
|
|
460
|
+
timeout: 3000,
|
|
461
|
+
encoding: 'utf-8',
|
|
462
|
+
cwd: process.cwd(),
|
|
463
|
+
});
|
|
464
|
+
recentCommits = gitOutput.trim().split('\n').filter(Boolean);
|
|
465
|
+
} catch {
|
|
466
|
+
// Not a git repo or no commits
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const query = buildSessionRecallQuery(projectName, recentCommits);
|
|
470
|
+
if (!query) return;
|
|
471
|
+
|
|
472
|
+
// Build FTS5 tokens
|
|
473
|
+
const ftsTokens = query.split(/\s+/)
|
|
474
|
+
.filter(t => t.length > 2)
|
|
475
|
+
.slice(0, 10)
|
|
476
|
+
.map(t => `"${t.replace(/"/g, '""')}"*`)
|
|
477
|
+
.join(' OR ');
|
|
478
|
+
|
|
479
|
+
if (!ftsTokens) return;
|
|
480
|
+
|
|
481
|
+
const sql = `
|
|
482
|
+
SELECT e.name, e.type,
|
|
483
|
+
(SELECT json_group_array(content) FROM observations o WHERE o.entity_id = e.id) as observations_json
|
|
484
|
+
FROM entities e
|
|
485
|
+
JOIN entities_fts ON entities_fts.rowid = e.id
|
|
486
|
+
WHERE entities_fts MATCH ?
|
|
487
|
+
ORDER BY bm25(entities_fts, 10.0, 5.0)
|
|
488
|
+
LIMIT 5
|
|
489
|
+
`;
|
|
490
|
+
|
|
491
|
+
const result = sqliteQueryJSON(MEMESH_DB_PATH, sql, [ftsTokens]);
|
|
492
|
+
if (!result || result.length === 0) return;
|
|
493
|
+
|
|
494
|
+
const formatted = result.map(r => ({
|
|
495
|
+
name: r.name,
|
|
496
|
+
observations: JSON.parse(r.observations_json || '[]').filter(Boolean).slice(0, 3),
|
|
497
|
+
similarity: 0.5,
|
|
498
|
+
}));
|
|
499
|
+
|
|
500
|
+
const output = formatRecallOutput(formatted);
|
|
501
|
+
if (output) {
|
|
502
|
+
console.log('\n Proactive Memory Recall:');
|
|
503
|
+
console.log(output);
|
|
504
|
+
}
|
|
505
|
+
} catch (error) {
|
|
506
|
+
logError('proactive-recall', error);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
447
510
|
function sessionStart() {
|
|
448
511
|
console.log('\nš Smart-Agents Session Started\n');
|
|
449
512
|
|
|
@@ -458,6 +521,9 @@ function sessionStart() {
|
|
|
458
521
|
const recalledMemory = recallRecentKeyPoints();
|
|
459
522
|
displayRecalledMemory(recalledMemory);
|
|
460
523
|
|
|
524
|
+
// Proactive memory recall (project context + recent commits)
|
|
525
|
+
recallProactiveMemories();
|
|
526
|
+
|
|
461
527
|
// Display active plans (beta)
|
|
462
528
|
displayActivePlans();
|
|
463
529
|
|