@su-record/vibe 0.1.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/LICENSE +21 -0
- package/README.md +448 -0
- package/agents/backend-python-expert.md +453 -0
- package/agents/database-postgres-expert.md +538 -0
- package/agents/frontend-flutter-expert.md +487 -0
- package/agents/frontend-react-expert.md +424 -0
- package/agents/quality-reviewer.md +542 -0
- package/agents/specification-agent.md +505 -0
- package/bin/sutory +332 -0
- package/bin/vibe +338 -0
- package/mcp/dist/__tests__/complexity.test.js +126 -0
- package/mcp/dist/__tests__/memory.test.js +120 -0
- package/mcp/dist/__tests__/python-dart-complexity.test.js +146 -0
- package/mcp/dist/index.js +230 -0
- package/mcp/dist/lib/ContextCompressor.js +305 -0
- package/mcp/dist/lib/MemoryManager.js +334 -0
- package/mcp/dist/lib/ProjectCache.js +126 -0
- package/mcp/dist/lib/PythonParser.js +241 -0
- package/mcp/dist/tools/browser/browserPool.js +76 -0
- package/mcp/dist/tools/browser/browserUtils.js +135 -0
- package/mcp/dist/tools/browser/inspectNetworkRequests.js +140 -0
- package/mcp/dist/tools/browser/monitorConsoleLogs.js +97 -0
- package/mcp/dist/tools/convention/analyzeComplexity.js +248 -0
- package/mcp/dist/tools/convention/applyQualityRules.js +102 -0
- package/mcp/dist/tools/convention/checkCouplingCohesion.js +233 -0
- package/mcp/dist/tools/convention/complexityMetrics.js +133 -0
- package/mcp/dist/tools/convention/dartComplexity.js +117 -0
- package/mcp/dist/tools/convention/getCodingGuide.js +64 -0
- package/mcp/dist/tools/convention/languageDetector.js +50 -0
- package/mcp/dist/tools/convention/pythonComplexity.js +109 -0
- package/mcp/dist/tools/convention/suggestImprovements.js +257 -0
- package/mcp/dist/tools/convention/validateCodeQuality.js +177 -0
- package/mcp/dist/tools/memory/autoSaveContext.js +79 -0
- package/mcp/dist/tools/memory/database.js +123 -0
- package/mcp/dist/tools/memory/deleteMemory.js +39 -0
- package/mcp/dist/tools/memory/listMemories.js +38 -0
- package/mcp/dist/tools/memory/memoryConfig.js +27 -0
- package/mcp/dist/tools/memory/memorySQLite.js +138 -0
- package/mcp/dist/tools/memory/memoryUtils.js +34 -0
- package/mcp/dist/tools/memory/migrate.js +113 -0
- package/mcp/dist/tools/memory/prioritizeMemory.js +109 -0
- package/mcp/dist/tools/memory/recallMemory.js +40 -0
- package/mcp/dist/tools/memory/restoreSessionContext.js +69 -0
- package/mcp/dist/tools/memory/saveMemory.js +34 -0
- package/mcp/dist/tools/memory/searchMemories.js +37 -0
- package/mcp/dist/tools/memory/startSession.js +100 -0
- package/mcp/dist/tools/memory/updateMemory.js +46 -0
- package/mcp/dist/tools/planning/analyzeRequirements.js +166 -0
- package/mcp/dist/tools/planning/createUserStories.js +119 -0
- package/mcp/dist/tools/planning/featureRoadmap.js +202 -0
- package/mcp/dist/tools/planning/generatePrd.js +156 -0
- package/mcp/dist/tools/prompt/analyzePrompt.js +145 -0
- package/mcp/dist/tools/prompt/enhancePrompt.js +105 -0
- package/mcp/dist/tools/semantic/findReferences.js +195 -0
- package/mcp/dist/tools/semantic/findSymbol.js +200 -0
- package/mcp/dist/tools/thinking/analyzeProblem.js +50 -0
- package/mcp/dist/tools/thinking/breakDownProblem.js +140 -0
- package/mcp/dist/tools/thinking/createThinkingChain.js +39 -0
- package/mcp/dist/tools/thinking/formatAsPlan.js +73 -0
- package/mcp/dist/tools/thinking/stepByStepAnalysis.js +58 -0
- package/mcp/dist/tools/thinking/thinkAloudProcess.js +75 -0
- package/mcp/dist/tools/time/getCurrentTime.js +61 -0
- package/mcp/dist/tools/ui/previewUiAscii.js +232 -0
- package/mcp/dist/types/tool.js +2 -0
- package/mcp/package.json +53 -0
- package/package.json +49 -0
- package/scripts/install-mcp.js +48 -0
- package/scripts/install.sh +70 -0
- package/skills/core/communication-guide.md +104 -0
- package/skills/core/development-philosophy.md +53 -0
- package/skills/core/quick-start.md +121 -0
- package/skills/languages/dart-flutter.md +509 -0
- package/skills/languages/python-fastapi.md +386 -0
- package/skills/languages/typescript-nextjs.md +441 -0
- package/skills/languages/typescript-react-native.md +446 -0
- package/skills/languages/typescript-react.md +525 -0
- package/skills/quality/checklist.md +276 -0
- package/skills/quality/testing-strategy.md +437 -0
- package/skills/standards/anti-patterns.md +369 -0
- package/skills/standards/code-structure.md +291 -0
- package/skills/standards/complexity-metrics.md +312 -0
- package/skills/standards/naming-conventions.md +198 -0
- package/skills/tools/mcp-hi-ai-guide.md +665 -0
- package/skills/tools/mcp-workflow.md +51 -0
- package/templates/constitution-template.md +193 -0
- package/templates/plan-template.md +237 -0
- package/templates/spec-template.md +142 -0
- package/templates/tasks-template.md +132 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// SQLite database connection and schema management
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { getMemoryDir } from './memoryConfig.js';
|
|
5
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
6
|
+
class MemoryDatabase {
|
|
7
|
+
db = null;
|
|
8
|
+
dbPath;
|
|
9
|
+
constructor() {
|
|
10
|
+
const memoryDir = getMemoryDir();
|
|
11
|
+
// Ensure directory exists
|
|
12
|
+
if (!existsSync(memoryDir)) {
|
|
13
|
+
mkdirSync(memoryDir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
this.dbPath = path.join(memoryDir, 'memories.db');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get database connection (lazy initialization)
|
|
19
|
+
*/
|
|
20
|
+
getConnection() {
|
|
21
|
+
if (!this.db) {
|
|
22
|
+
// Ensure directory exists before opening database
|
|
23
|
+
const memoryDir = getMemoryDir();
|
|
24
|
+
if (!existsSync(memoryDir)) {
|
|
25
|
+
mkdirSync(memoryDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
this.db = new Database(this.dbPath);
|
|
28
|
+
// Enable WAL mode for better concurrency
|
|
29
|
+
this.db.pragma('journal_mode = WAL');
|
|
30
|
+
// Initialize schema
|
|
31
|
+
this.initSchema();
|
|
32
|
+
}
|
|
33
|
+
return this.db;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Initialize database schema
|
|
37
|
+
*/
|
|
38
|
+
initSchema() {
|
|
39
|
+
const db = this.db;
|
|
40
|
+
// Memories table
|
|
41
|
+
db.exec(`
|
|
42
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
43
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
44
|
+
key TEXT UNIQUE NOT NULL,
|
|
45
|
+
value TEXT NOT NULL,
|
|
46
|
+
category TEXT NOT NULL DEFAULT 'general',
|
|
47
|
+
timestamp TEXT NOT NULL,
|
|
48
|
+
lastAccessed TEXT NOT NULL
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_memories_key ON memories(key);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(category);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_memories_lastAccessed ON memories(lastAccessed);
|
|
54
|
+
`);
|
|
55
|
+
// Sessions table
|
|
56
|
+
db.exec(`
|
|
57
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
58
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
59
|
+
sessionId TEXT NOT NULL,
|
|
60
|
+
contextType TEXT NOT NULL,
|
|
61
|
+
summary TEXT NOT NULL,
|
|
62
|
+
urgency TEXT NOT NULL,
|
|
63
|
+
currentTask TEXT,
|
|
64
|
+
codeChanges TEXT,
|
|
65
|
+
decisions TEXT,
|
|
66
|
+
blockers TEXT,
|
|
67
|
+
nextSteps TEXT,
|
|
68
|
+
timestamp TEXT NOT NULL
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_sessionId ON sessions(sessionId);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_timestamp ON sessions(timestamp);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_urgency ON sessions(urgency);
|
|
74
|
+
`);
|
|
75
|
+
// Guides table (for coding guides)
|
|
76
|
+
db.exec(`
|
|
77
|
+
CREATE TABLE IF NOT EXISTS guides (
|
|
78
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
79
|
+
name TEXT UNIQUE NOT NULL,
|
|
80
|
+
category TEXT NOT NULL,
|
|
81
|
+
content TEXT NOT NULL,
|
|
82
|
+
timestamp TEXT NOT NULL
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
CREATE INDEX IF NOT EXISTS idx_guides_name ON guides(name);
|
|
86
|
+
CREATE INDEX IF NOT EXISTS idx_guides_category ON guides(category);
|
|
87
|
+
`);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Close database connection
|
|
91
|
+
*/
|
|
92
|
+
close() {
|
|
93
|
+
if (this.db) {
|
|
94
|
+
this.db.close();
|
|
95
|
+
this.db = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Run a checkpoint to merge WAL file
|
|
100
|
+
*/
|
|
101
|
+
checkpoint() {
|
|
102
|
+
if (this.db) {
|
|
103
|
+
this.db.pragma('wal_checkpoint(TRUNCATE)');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Singleton instance
|
|
108
|
+
export const memoryDB = new MemoryDatabase();
|
|
109
|
+
// Cleanup on process exit
|
|
110
|
+
process.on('exit', () => {
|
|
111
|
+
memoryDB.checkpoint();
|
|
112
|
+
memoryDB.close();
|
|
113
|
+
});
|
|
114
|
+
process.on('SIGINT', () => {
|
|
115
|
+
memoryDB.checkpoint();
|
|
116
|
+
memoryDB.close();
|
|
117
|
+
process.exit(0);
|
|
118
|
+
});
|
|
119
|
+
process.on('SIGTERM', () => {
|
|
120
|
+
memoryDB.checkpoint();
|
|
121
|
+
memoryDB.close();
|
|
122
|
+
process.exit(0);
|
|
123
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Memory management tool - completely independent
|
|
2
|
+
import { MemoryManager } from '../../lib/MemoryManager.js';
|
|
3
|
+
export const deleteMemoryDefinition = {
|
|
4
|
+
name: 'delete_memory',
|
|
5
|
+
description: '잊어|삭제해|지워|forget|delete|remove|erase - Delete specific memory',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
key: { type: 'string', description: 'Memory key to delete' }
|
|
10
|
+
},
|
|
11
|
+
required: ['key']
|
|
12
|
+
},
|
|
13
|
+
annotations: {
|
|
14
|
+
title: 'Delete Memory',
|
|
15
|
+
audience: ['user', 'assistant']
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export async function deleteMemory(args) {
|
|
19
|
+
const { key: deleteKey } = args;
|
|
20
|
+
try {
|
|
21
|
+
const mm = MemoryManager.getInstance();
|
|
22
|
+
const deleted = mm.delete(deleteKey);
|
|
23
|
+
if (deleted) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: 'text', text: `✓ Deleted memory: "${deleteKey}"` }]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: 'text', text: `✗ Memory not found: "${deleteKey}"` }]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: 'text', text: `✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}` }]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Memory management tool - completely independent
|
|
2
|
+
import { MemoryManager } from '../../lib/MemoryManager.js';
|
|
3
|
+
export const listMemoriesDefinition = {
|
|
4
|
+
name: 'list_memories',
|
|
5
|
+
description: '뭐 있었지|저장된 거|목록|what did I save|list memories|show saved - List saved memories',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
category: { type: 'string', description: 'Filter by category' },
|
|
10
|
+
limit: { type: 'number', description: 'Maximum number of results' }
|
|
11
|
+
},
|
|
12
|
+
required: []
|
|
13
|
+
},
|
|
14
|
+
annotations: {
|
|
15
|
+
title: 'List Memories',
|
|
16
|
+
audience: ['user', 'assistant']
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export async function listMemories(args) {
|
|
20
|
+
const { category: listCategory, limit = 10 } = args;
|
|
21
|
+
try {
|
|
22
|
+
const mm = MemoryManager.getInstance();
|
|
23
|
+
const allMemories = mm.list(listCategory);
|
|
24
|
+
const limitedMemories = allMemories.slice(0, limit);
|
|
25
|
+
const memoryList = limitedMemories.map(m => `• ${m.key} (${m.category}): ${m.value.substring(0, 50)}${m.value.length > 50 ? '...' : ''}`).join('\n');
|
|
26
|
+
return {
|
|
27
|
+
content: [{
|
|
28
|
+
type: 'text',
|
|
29
|
+
text: `✓ Found ${allMemories.length} memories${listCategory ? ` in '${listCategory}'` : ''}:\n${memoryList || 'None'}`
|
|
30
|
+
}]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: 'text', text: `✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}` }]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Memory configuration - centralized path management
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Get memory directory path
|
|
5
|
+
* Respects HI_AI_MEMORY_DIR environment variable
|
|
6
|
+
*/
|
|
7
|
+
export function getMemoryDir() {
|
|
8
|
+
return process.env.HI_AI_MEMORY_DIR || path.join(process.cwd(), 'memories');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get memory file path
|
|
12
|
+
*/
|
|
13
|
+
export function getMemoryFile() {
|
|
14
|
+
return path.join(getMemoryDir(), 'memories.json');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get session context directory
|
|
18
|
+
*/
|
|
19
|
+
export function getSessionDir() {
|
|
20
|
+
return path.join(getMemoryDir(), 'sessions');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get guides directory
|
|
24
|
+
*/
|
|
25
|
+
export function getGuidesDir() {
|
|
26
|
+
return process.env.HI_AI_GUIDES_DIR || path.join(process.cwd(), 'guides');
|
|
27
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// SQLite-based memory operations
|
|
2
|
+
import { memoryDB } from './database.js';
|
|
3
|
+
/**
|
|
4
|
+
* Save or update memory
|
|
5
|
+
*/
|
|
6
|
+
export function saveMemory(key, value, category = 'general') {
|
|
7
|
+
const db = memoryDB.getConnection();
|
|
8
|
+
const timestamp = new Date().toISOString();
|
|
9
|
+
const stmt = db.prepare(`
|
|
10
|
+
INSERT INTO memories (key, value, category, timestamp, lastAccessed)
|
|
11
|
+
VALUES (?, ?, ?, ?, ?)
|
|
12
|
+
ON CONFLICT(key) DO UPDATE SET
|
|
13
|
+
value = excluded.value,
|
|
14
|
+
category = excluded.category,
|
|
15
|
+
timestamp = excluded.timestamp,
|
|
16
|
+
lastAccessed = excluded.lastAccessed
|
|
17
|
+
`);
|
|
18
|
+
stmt.run(key, value, category, timestamp, timestamp);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get memory by key
|
|
22
|
+
*/
|
|
23
|
+
export function getMemory(key) {
|
|
24
|
+
const db = memoryDB.getConnection();
|
|
25
|
+
const stmt = db.prepare('SELECT * FROM memories WHERE key = ?');
|
|
26
|
+
const memory = stmt.get(key);
|
|
27
|
+
// Update last accessed time
|
|
28
|
+
if (memory) {
|
|
29
|
+
const updateStmt = db.prepare('UPDATE memories SET lastAccessed = ? WHERE key = ?');
|
|
30
|
+
updateStmt.run(new Date().toISOString(), key);
|
|
31
|
+
}
|
|
32
|
+
return memory;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* List memories with optional filtering
|
|
36
|
+
*/
|
|
37
|
+
export function listMemories(options) {
|
|
38
|
+
const db = memoryDB.getConnection();
|
|
39
|
+
const { category, limit = 10, offset = 0 } = options;
|
|
40
|
+
let whereClause = '';
|
|
41
|
+
const params = [];
|
|
42
|
+
if (category) {
|
|
43
|
+
whereClause = 'WHERE category = ?';
|
|
44
|
+
params.push(category);
|
|
45
|
+
}
|
|
46
|
+
// Get total count
|
|
47
|
+
const countStmt = db.prepare(`SELECT COUNT(*) as count FROM memories ${whereClause}`);
|
|
48
|
+
const { count } = countStmt.get(...params);
|
|
49
|
+
// Get paginated results
|
|
50
|
+
const stmt = db.prepare(`
|
|
51
|
+
SELECT * FROM memories ${whereClause}
|
|
52
|
+
ORDER BY lastAccessed DESC
|
|
53
|
+
LIMIT ? OFFSET ?
|
|
54
|
+
`);
|
|
55
|
+
const memories = stmt.all(...params, limit, offset);
|
|
56
|
+
return { memories, total: count };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Search memories by content
|
|
60
|
+
*/
|
|
61
|
+
export function searchMemories(query, options) {
|
|
62
|
+
const db = memoryDB.getConnection();
|
|
63
|
+
const { category, limit = 20 } = options || {};
|
|
64
|
+
let whereClause = 'WHERE (key LIKE ? OR value LIKE ?)';
|
|
65
|
+
const params = [`%${query}%`, `%${query}%`];
|
|
66
|
+
if (category) {
|
|
67
|
+
whereClause += ' AND category = ?';
|
|
68
|
+
params.push(category);
|
|
69
|
+
}
|
|
70
|
+
const stmt = db.prepare(`
|
|
71
|
+
SELECT * FROM memories ${whereClause}
|
|
72
|
+
ORDER BY lastAccessed DESC
|
|
73
|
+
LIMIT ?
|
|
74
|
+
`);
|
|
75
|
+
return stmt.all(...params, limit);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Delete memory by key
|
|
79
|
+
*/
|
|
80
|
+
export function deleteMemory(key) {
|
|
81
|
+
const db = memoryDB.getConnection();
|
|
82
|
+
const stmt = db.prepare('DELETE FROM memories WHERE key = ?');
|
|
83
|
+
const result = stmt.run(key);
|
|
84
|
+
return result.changes > 0;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Update memory
|
|
88
|
+
*/
|
|
89
|
+
export function updateMemory(key, value, append = false) {
|
|
90
|
+
const db = memoryDB.getConnection();
|
|
91
|
+
if (append) {
|
|
92
|
+
const existing = getMemory(key);
|
|
93
|
+
if (existing) {
|
|
94
|
+
value = existing.value + '\n' + value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const stmt = db.prepare(`
|
|
98
|
+
UPDATE memories
|
|
99
|
+
SET value = ?, lastAccessed = ?
|
|
100
|
+
WHERE key = ?
|
|
101
|
+
`);
|
|
102
|
+
const result = stmt.run(value, new Date().toISOString(), key);
|
|
103
|
+
return result.changes > 0;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get memory statistics
|
|
107
|
+
*/
|
|
108
|
+
export function getMemoryStats() {
|
|
109
|
+
const db = memoryDB.getConnection();
|
|
110
|
+
// Total count
|
|
111
|
+
const totalStmt = db.prepare('SELECT COUNT(*) as count FROM memories');
|
|
112
|
+
const { count: totalMemories } = totalStmt.get();
|
|
113
|
+
// By category
|
|
114
|
+
const categoryStmt = db.prepare(`
|
|
115
|
+
SELECT category, COUNT(*) as count
|
|
116
|
+
FROM memories
|
|
117
|
+
GROUP BY category
|
|
118
|
+
`);
|
|
119
|
+
const categoryResults = categoryStmt.all();
|
|
120
|
+
const byCategory = Object.fromEntries(categoryResults.map(r => [r.category, r.count]));
|
|
121
|
+
// Recently accessed
|
|
122
|
+
const recentStmt = db.prepare(`
|
|
123
|
+
SELECT * FROM memories
|
|
124
|
+
ORDER BY lastAccessed DESC
|
|
125
|
+
LIMIT 10
|
|
126
|
+
`);
|
|
127
|
+
const recentlyAccessed = recentStmt.all();
|
|
128
|
+
return { totalMemories, byCategory, recentlyAccessed };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Clear all memories (use with caution!)
|
|
132
|
+
*/
|
|
133
|
+
export function clearAllMemories() {
|
|
134
|
+
const db = memoryDB.getConnection();
|
|
135
|
+
const stmt = db.prepare('DELETE FROM memories');
|
|
136
|
+
const result = stmt.run();
|
|
137
|
+
return result.changes;
|
|
138
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Shared memory utilities
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import { getMemoryDir, getMemoryFile } from './memoryConfig.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ensure memory directory exists
|
|
6
|
+
*/
|
|
7
|
+
export async function ensureMemoryDir() {
|
|
8
|
+
try {
|
|
9
|
+
await fs.access(getMemoryDir());
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
await fs.mkdir(getMemoryDir(), { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load all memories from file
|
|
17
|
+
*/
|
|
18
|
+
export async function loadMemories() {
|
|
19
|
+
try {
|
|
20
|
+
await ensureMemoryDir();
|
|
21
|
+
const data = await fs.readFile(getMemoryFile(), 'utf-8');
|
|
22
|
+
return JSON.parse(data);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Save memories to file
|
|
30
|
+
*/
|
|
31
|
+
export async function saveMemories(memories) {
|
|
32
|
+
await ensureMemoryDir();
|
|
33
|
+
await fs.writeFile(getMemoryFile(), JSON.stringify(memories, null, 2));
|
|
34
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Migration utility to convert JSON memories to SQLite
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { getMemoryDir } from './memoryConfig.js';
|
|
6
|
+
import { saveMemory } from './memorySQLite.js';
|
|
7
|
+
import { memoryDB } from './database.js';
|
|
8
|
+
/**
|
|
9
|
+
* Migrate from JSON to SQLite
|
|
10
|
+
*/
|
|
11
|
+
export async function migrateFromJSON() {
|
|
12
|
+
const memoryDir = getMemoryDir();
|
|
13
|
+
const jsonPath = path.join(memoryDir, 'memories.json');
|
|
14
|
+
const result = {
|
|
15
|
+
success: false,
|
|
16
|
+
migrated: 0,
|
|
17
|
+
errors: []
|
|
18
|
+
};
|
|
19
|
+
// Check if JSON file exists
|
|
20
|
+
if (!existsSync(jsonPath)) {
|
|
21
|
+
result.errors.push('No legacy memories.json file found');
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
// Read JSON file
|
|
26
|
+
const jsonData = readFileSync(jsonPath, 'utf-8');
|
|
27
|
+
const memories = JSON.parse(jsonData);
|
|
28
|
+
console.log(`Found ${memories.length} memories to migrate`);
|
|
29
|
+
// Migrate each memory
|
|
30
|
+
for (const memory of memories) {
|
|
31
|
+
try {
|
|
32
|
+
saveMemory(memory.key, memory.value, memory.category);
|
|
33
|
+
result.migrated++;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
result.errors.push(`Failed to migrate key "${memory.key}": ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
result.success = true;
|
|
40
|
+
console.log(`✅ Successfully migrated ${result.migrated} memories`);
|
|
41
|
+
if (result.errors.length > 0) {
|
|
42
|
+
console.warn(`⚠️ ${result.errors.length} errors occurred during migration:`);
|
|
43
|
+
result.errors.forEach(err => console.warn(` ${err}`));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
result.errors.push(`Failed to read JSON file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Export SQLite database to JSON (for backup)
|
|
53
|
+
*/
|
|
54
|
+
export async function exportToJSON(outputPath) {
|
|
55
|
+
const db = memoryDB.getConnection();
|
|
56
|
+
const stmt = db.prepare('SELECT * FROM memories');
|
|
57
|
+
const memories = stmt.all();
|
|
58
|
+
const exportPath = outputPath || path.join(getMemoryDir(), `backup-${Date.now()}.json`);
|
|
59
|
+
try {
|
|
60
|
+
const { writeFileSync } = await import('fs');
|
|
61
|
+
writeFileSync(exportPath, JSON.stringify(memories, null, 2));
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
exported: memories.length,
|
|
65
|
+
path: exportPath
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw new Error(`Failed to export: ${error instanceof Error ? error.message : 'Unknown'}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// CLI interface
|
|
73
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
74
|
+
const command = process.argv[2];
|
|
75
|
+
switch (command) {
|
|
76
|
+
case 'migrate':
|
|
77
|
+
console.log('🔄 Starting migration from JSON to SQLite...');
|
|
78
|
+
migrateFromJSON().then(result => {
|
|
79
|
+
if (result.success) {
|
|
80
|
+
console.log(`\n✅ Migration completed: ${result.migrated} memories migrated`);
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.error('\n❌ Migration failed');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
break;
|
|
89
|
+
case 'export':
|
|
90
|
+
console.log('📦 Exporting SQLite to JSON...');
|
|
91
|
+
exportToJSON().then(result => {
|
|
92
|
+
console.log(`\n✅ Export completed: ${result.exported} memories exported to ${result.path}`);
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}).catch(error => {
|
|
95
|
+
console.error(`\n❌ Export failed: ${error.message}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
console.log(`
|
|
101
|
+
Hi-AI Memory Migration Tool
|
|
102
|
+
|
|
103
|
+
Usage:
|
|
104
|
+
node dist/tools/memory/migrate.js migrate Migrate from JSON to SQLite
|
|
105
|
+
node dist/tools/memory/migrate.js export Export SQLite to JSON backup
|
|
106
|
+
|
|
107
|
+
Options:
|
|
108
|
+
migrate Convert memories.json to SQLite database
|
|
109
|
+
export Create JSON backup of SQLite database
|
|
110
|
+
`);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Memory management tool - completely independent
|
|
2
|
+
import { MemoryManager } from '../../lib/MemoryManager.js';
|
|
3
|
+
export const prioritizeMemoryDefinition = {
|
|
4
|
+
name: 'prioritize_memory',
|
|
5
|
+
description: '중요한 거|우선순위|prioritize|important|what matters|priority - Prioritize memories by importance',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
currentTask: { type: 'string', description: 'Current task description' },
|
|
10
|
+
criticalDecisions: { type: 'array', items: { type: 'string' }, description: 'List of critical decisions made' },
|
|
11
|
+
codeChanges: { type: 'array', items: { type: 'string' }, description: 'Important code changes' },
|
|
12
|
+
blockers: { type: 'array', items: { type: 'string' }, description: 'Current blockers or issues' },
|
|
13
|
+
nextSteps: { type: 'array', items: { type: 'string' }, description: 'Planned next steps' }
|
|
14
|
+
},
|
|
15
|
+
required: ['currentTask']
|
|
16
|
+
},
|
|
17
|
+
annotations: {
|
|
18
|
+
title: 'Prioritize Memory',
|
|
19
|
+
audience: ['user', 'assistant']
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export async function prioritizeMemory(args) {
|
|
23
|
+
const { currentTask, criticalDecisions = [], codeChanges = [], blockers = [], nextSteps = [] } = args;
|
|
24
|
+
try {
|
|
25
|
+
const mm = MemoryManager.getInstance();
|
|
26
|
+
const allMemories = mm.list();
|
|
27
|
+
const prioritizedMemories = [];
|
|
28
|
+
for (const memory of allMemories) {
|
|
29
|
+
let priority = 0;
|
|
30
|
+
let reason = '';
|
|
31
|
+
// Analyze importance based on content
|
|
32
|
+
if (memory.value.includes('error') || memory.value.includes('Error')) {
|
|
33
|
+
priority = 0.9;
|
|
34
|
+
reason = 'error info';
|
|
35
|
+
}
|
|
36
|
+
else if (memory.value.includes('decision') || memory.value.includes('Decision')) {
|
|
37
|
+
priority = 0.8;
|
|
38
|
+
reason = 'decision';
|
|
39
|
+
}
|
|
40
|
+
else if (memory.value.includes('code') || memory.value.includes('function')) {
|
|
41
|
+
priority = 0.7;
|
|
42
|
+
reason = 'code-related';
|
|
43
|
+
}
|
|
44
|
+
else if (memory.category === 'context') {
|
|
45
|
+
priority = 0.6;
|
|
46
|
+
reason = 'context';
|
|
47
|
+
}
|
|
48
|
+
else if (memory.category === 'project') {
|
|
49
|
+
priority = 0.7;
|
|
50
|
+
reason = 'project';
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
priority = 0.5;
|
|
54
|
+
reason = 'general';
|
|
55
|
+
}
|
|
56
|
+
// Boost priority for memories related to current task
|
|
57
|
+
if (memory.value.toLowerCase().includes(currentTask.toLowerCase())) {
|
|
58
|
+
priority += 0.2;
|
|
59
|
+
reason += ' +task';
|
|
60
|
+
}
|
|
61
|
+
// Boost priority for critical decisions
|
|
62
|
+
for (const decision of criticalDecisions) {
|
|
63
|
+
if (memory.value.toLowerCase().includes(decision.toLowerCase())) {
|
|
64
|
+
priority += 0.15;
|
|
65
|
+
reason += ' +critical';
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Boost priority for code changes
|
|
70
|
+
for (const change of codeChanges) {
|
|
71
|
+
if (memory.value.toLowerCase().includes(change.toLowerCase())) {
|
|
72
|
+
priority += 0.1;
|
|
73
|
+
reason += ' +change';
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Boost priority for blockers
|
|
78
|
+
for (const blocker of blockers) {
|
|
79
|
+
if (memory.value.toLowerCase().includes(blocker.toLowerCase())) {
|
|
80
|
+
priority += 0.25;
|
|
81
|
+
reason += ' +blocker';
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Cap priority at 1.0
|
|
86
|
+
priority = Math.min(1.0, priority);
|
|
87
|
+
if (priority >= 0.6) {
|
|
88
|
+
prioritizedMemories.push({ memory, priority, reason });
|
|
89
|
+
// Update priority in database
|
|
90
|
+
mm.setPriority(memory.key, Math.floor(priority * 100));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const sortedMemories = prioritizedMemories
|
|
94
|
+
.sort((a, b) => b.priority - a.priority)
|
|
95
|
+
.slice(0, 20);
|
|
96
|
+
const resultList = sortedMemories.map(pm => `• [${(pm.priority * 100).toFixed(0)}%] ${pm.memory.key} (${pm.reason}): ${pm.memory.value.substring(0, 60)}${pm.memory.value.length > 60 ? '...' : ''}`).join('\n');
|
|
97
|
+
return {
|
|
98
|
+
content: [{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: `✓ Prioritized ${sortedMemories.length} memories for "${currentTask}":\n${resultList || 'None'}`
|
|
101
|
+
}]
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: 'text', text: `✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}` }]
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Memory management tool - SQLite based (v1.3)
|
|
2
|
+
import { MemoryManager } from '../../lib/MemoryManager.js';
|
|
3
|
+
export const recallMemoryDefinition = {
|
|
4
|
+
name: 'recall_memory',
|
|
5
|
+
description: '떠올려|recall|기억나|remember what|what was|remind - Retrieve from memory',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
key: { type: 'string', description: 'Memory key to retrieve' },
|
|
10
|
+
category: { type: 'string', description: 'Memory category to search in' }
|
|
11
|
+
},
|
|
12
|
+
required: ['key']
|
|
13
|
+
},
|
|
14
|
+
annotations: {
|
|
15
|
+
title: 'Recall Memory',
|
|
16
|
+
audience: ['user', 'assistant']
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export async function recallMemory(args) {
|
|
20
|
+
const { key: recallKey } = args;
|
|
21
|
+
try {
|
|
22
|
+
const memoryManager = MemoryManager.getInstance();
|
|
23
|
+
const memory = memoryManager.recall(recallKey);
|
|
24
|
+
if (memory) {
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: 'text', text: `${memory.key}: ${memory.value}\n[${memory.category}]` }]
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: `✗ Not found: "${recallKey}"` }]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: `✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}` }]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|