kratos-memory 1.6.0 → 1.7.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.
Files changed (47) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +27 -5
  3. package/dist/cli/capture-handler.d.ts +5 -0
  4. package/dist/cli/capture-handler.d.ts.map +1 -1
  5. package/dist/cli/capture-handler.js +39 -0
  6. package/dist/cli/capture-handler.js.map +1 -1
  7. package/dist/cli/commands/ask.d.ts.map +1 -1
  8. package/dist/cli/commands/ask.js +126 -121
  9. package/dist/cli/commands/ask.js.map +1 -1
  10. package/dist/cli/commands/capture.d.ts.map +1 -1
  11. package/dist/cli/commands/capture.js +3 -0
  12. package/dist/cli/commands/capture.js.map +1 -1
  13. package/dist/cli/commands/context.d.ts +11 -0
  14. package/dist/cli/commands/context.d.ts.map +1 -0
  15. package/dist/cli/commands/context.js +119 -0
  16. package/dist/cli/commands/context.js.map +1 -0
  17. package/dist/cli/commands/hooks.d.ts.map +1 -1
  18. package/dist/cli/commands/hooks.js +149 -62
  19. package/dist/cli/commands/hooks.js.map +1 -1
  20. package/dist/cli/commands/migrate.d.ts.map +1 -1
  21. package/dist/cli/commands/migrate.js +111 -27
  22. package/dist/cli/commands/migrate.js.map +1 -1
  23. package/dist/cli/commands/summary.d.ts.map +1 -1
  24. package/dist/cli/commands/summary.js +44 -0
  25. package/dist/cli/commands/summary.js.map +1 -1
  26. package/dist/cli/index.js +12 -0
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/memory-server/database.d.ts +0 -1
  29. package/dist/memory-server/database.d.ts.map +1 -1
  30. package/dist/memory-server/database.js +11 -35
  31. package/dist/memory-server/database.js.map +1 -1
  32. package/dist/project-manager.d.ts.map +1 -1
  33. package/dist/project-manager.js +6 -2
  34. package/dist/project-manager.js.map +1 -1
  35. package/package.json +1 -1
  36. package/dist/memory-server/concept-store-enhanced.d.ts +0 -88
  37. package/dist/memory-server/concept-store-enhanced.d.ts.map +0 -1
  38. package/dist/memory-server/concept-store-enhanced.js +0 -392
  39. package/dist/memory-server/concept-store-enhanced.js.map +0 -1
  40. package/dist/memory-server/concept-store.d.ts +0 -58
  41. package/dist/memory-server/concept-store.d.ts.map +0 -1
  42. package/dist/memory-server/concept-store.js +0 -329
  43. package/dist/memory-server/concept-store.js.map +0 -1
  44. package/dist/memory-server/context-broker.d.ts +0 -63
  45. package/dist/memory-server/context-broker.d.ts.map +0 -1
  46. package/dist/memory-server/context-broker.js +0 -340
  47. package/dist/memory-server/context-broker.js.map +0 -1
@@ -0,0 +1,119 @@
1
+ import { Output } from '../output.js';
2
+ const DEFAULT_BUDGET_TOKENS = 2000;
3
+ const CHARS_PER_TOKEN = 4;
4
+ /**
5
+ * Emit a compact, token-budgeted context block built from memories.
6
+ * Designed for SessionStart hook injection: stdout becomes session context.
7
+ * Plain text — chalk is bypassed so hook output stays clean.
8
+ */
9
+ export async function contextCommand(ctx, opts) {
10
+ const budgetTokens = opts.budget ? parseInt(opts.budget, 10) : DEFAULT_BUDGET_TOKENS;
11
+ const budgetChars = budgetTokens * CHARS_PER_TOKEN;
12
+ const projectMemories = ctx.projectMemoryDb.getRecent({ k: 100 });
13
+ const globalMemories = ctx.globalMemoryDb.getRecent({ k: 50 });
14
+ const entries = [
15
+ ...projectMemories.map(m => ({ memory: m, scope: 'project' })),
16
+ ...globalMemories.map(m => ({ memory: m, scope: 'global' })),
17
+ ];
18
+ if (entries.length === 0) {
19
+ if (opts.json) {
20
+ Output.json({ project: ctx.project.name, count: 0, sections: {} });
21
+ }
22
+ // No memories — emit nothing so hooks inject nothing
23
+ return;
24
+ }
25
+ const isPinned = (m) => m.tags.includes('__pinned');
26
+ const isAutoCapture = (m) => m.tags.includes('auto-capture');
27
+ const isSessionSummary = (m) => m.tags.includes('session-summary');
28
+ const pinned = entries.filter(e => isPinned(e.memory));
29
+ const important = entries.filter(e => !isPinned(e.memory) && !isAutoCapture(e.memory) && e.memory.importance >= 4).sort((a, b) => b.memory.importance - a.memory.importance || b.memory.created_at - a.memory.created_at);
30
+ const recent = entries.filter(e => !isPinned(e.memory) && !isAutoCapture(e.memory) && e.memory.importance < 4).sort((a, b) => b.memory.created_at - a.memory.created_at);
31
+ // Latest auto-captured session summary gets one line of its own
32
+ const lastSession = entries
33
+ .filter(e => isSessionSummary(e.memory))
34
+ .sort((a, b) => b.memory.created_at - a.memory.created_at)[0];
35
+ if (opts.json) {
36
+ Output.json({
37
+ project: ctx.project.name,
38
+ count: entries.length,
39
+ sections: {
40
+ pinned: pinned.map(serializeEntry),
41
+ important: important.map(serializeEntry),
42
+ recent: recent.slice(0, 15).map(serializeEntry),
43
+ last_session: lastSession ? serializeEntry(lastSession) : null,
44
+ },
45
+ });
46
+ return;
47
+ }
48
+ const lines = [];
49
+ lines.push(`# Kratos memory — ${ctx.project.name}`);
50
+ lines.push('Prior knowledge about this project, loaded from local memory. ' +
51
+ 'Before solving a problem, check it here first: `kratos search "<topic>"`. ' +
52
+ 'After fixing a bug or making a decision, save it: `kratos save "<what + why>" --tags <tags>`.');
53
+ lines.push('');
54
+ let used = lines.join('\n').length;
55
+ const sections = [
56
+ { title: 'Pinned', items: pinned, max: pinned.length },
57
+ { title: 'Decisions & fixes', items: important, max: 20 },
58
+ { title: 'Recent', items: recent, max: 10 },
59
+ ];
60
+ for (const section of sections) {
61
+ if (section.items.length === 0)
62
+ continue;
63
+ const sectionLines = [`## ${section.title}`];
64
+ let added = 0;
65
+ for (const entry of section.items.slice(0, section.max)) {
66
+ const line = formatEntry(entry);
67
+ if (used + sectionLines.join('\n').length + line.length > budgetChars)
68
+ break;
69
+ sectionLines.push(line);
70
+ added++;
71
+ }
72
+ if (added > 0) {
73
+ lines.push(...sectionLines, '');
74
+ used = lines.join('\n').length;
75
+ }
76
+ }
77
+ if (lastSession && used + 200 < budgetChars) {
78
+ lines.push(`Last session: ${truncate(lastSession.memory.summary, 160)} (${relativeTime(lastSession.memory.created_at)})`);
79
+ }
80
+ console.log(lines.join('\n').trimEnd());
81
+ }
82
+ function formatEntry(entry) {
83
+ const m = entry.memory;
84
+ const scopeTag = entry.scope === 'global' ? ' [global]' : '';
85
+ const body = m.text && m.text !== m.summary
86
+ ? `${m.summary}: ${truncate(m.text, 200)}`
87
+ : truncate(m.summary, 200);
88
+ return `- ${body}${scopeTag} (${relativeTime(m.created_at)})`;
89
+ }
90
+ function serializeEntry(entry) {
91
+ return {
92
+ id: entry.memory.id,
93
+ scope: entry.scope,
94
+ summary: entry.memory.summary,
95
+ text: entry.memory.text,
96
+ tags: entry.memory.tags,
97
+ importance: entry.memory.importance,
98
+ created_at: entry.memory.created_at,
99
+ };
100
+ }
101
+ function truncate(text, max) {
102
+ const oneLine = text.replace(/\s+/g, ' ').trim();
103
+ return oneLine.length > max ? oneLine.substring(0, max - 1) + '…' : oneLine;
104
+ }
105
+ function relativeTime(timestamp) {
106
+ const diffMs = Date.now() - timestamp;
107
+ const minutes = Math.floor(diffMs / 60000);
108
+ if (minutes < 60)
109
+ return `${Math.max(minutes, 1)}m ago`;
110
+ const hours = Math.floor(minutes / 60);
111
+ if (hours < 24)
112
+ return `${hours}h ago`;
113
+ const days = Math.floor(hours / 24);
114
+ if (days < 30)
115
+ return `${days}d ago`;
116
+ const months = Math.floor(days / 30);
117
+ return `${months}mo ago`;
118
+ }
119
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/cli/commands/context.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/hooks.ts"],"names":[],"mappings":"AAoBA,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAehE"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/hooks.ts"],"names":[],"mappings":"AAqCA,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAehE"}
@@ -1,21 +1,36 @@
1
1
  import { Output } from '../output.js';
2
2
  import path from 'path';
3
3
  import fs from 'fs-extra';
4
- const HOOK_CONFIG = {
5
- hooks: {
6
- PostToolUse: [
7
- {
8
- matcher: 'Edit|Write|MultiEdit',
9
- command: 'kratos capture --event post-tool-use',
10
- },
11
- ],
12
- Stop: [
13
- {
14
- command: 'kratos capture --event session-end',
15
- },
16
- ],
17
- },
4
+ // Claude Code hook schema: each event holds matcher groups, each group holds
5
+ // hooks of { type: 'command', command }. Flat { matcher, command } entries
6
+ // (written by kratos <= 1.6.x) are silently ignored by Claude Code — install
7
+ // migrates them to this format.
8
+ const KRATOS_HOOKS = {
9
+ SessionStart: [
10
+ {
11
+ hooks: [{ type: 'command', command: 'kratos context' }],
12
+ },
13
+ ],
14
+ PostToolUse: [
15
+ {
16
+ matcher: 'Edit|Write|MultiEdit',
17
+ hooks: [{ type: 'command', command: 'kratos capture --event post-tool-use' }],
18
+ },
19
+ ],
20
+ Stop: [
21
+ {
22
+ hooks: [{ type: 'command', command: 'kratos capture --event session-end' }],
23
+ },
24
+ ],
18
25
  };
26
+ const HOOK_EVENTS = Object.keys(KRATOS_HOOKS);
27
+ const GIT_HOOK_START = '# >>> kratos-memory post-commit >>>';
28
+ const GIT_HOOK_END = '# <<< kratos-memory post-commit <<<';
29
+ const GIT_HOOK_BLOCK = [
30
+ GIT_HOOK_START,
31
+ 'command -v kratos >/dev/null 2>&1 && kratos capture --event git-commit >/dev/null 2>&1 || true',
32
+ GIT_HOOK_END,
33
+ ].join('\n');
19
34
  export async function hooksCommand(action) {
20
35
  switch (action) {
21
36
  case 'install':
@@ -32,12 +47,26 @@ export async function hooksCommand(action) {
32
47
  process.exit(1);
33
48
  }
34
49
  }
50
+ /** Matches both current-format and legacy flat-format kratos entries. */
51
+ function isKratosEntry(entry) {
52
+ if (typeof entry?.command === 'string' && entry.command.includes('kratos '))
53
+ return true;
54
+ if (Array.isArray(entry?.hooks)) {
55
+ return entry.hooks.some((h) => typeof h?.command === 'string' && h.command.includes('kratos '));
56
+ }
57
+ return false;
58
+ }
59
+ /** Legacy flat entries ({ matcher, command }) that Claude Code never executed. */
60
+ function isLegacyEntry(entry) {
61
+ return typeof entry?.command === 'string' && !Array.isArray(entry?.hooks);
62
+ }
63
+ function settingsFilePath() {
64
+ return path.join(process.cwd(), '.claude', 'settings.local.json');
65
+ }
35
66
  async function installHooks() {
36
- const settingsPath = path.join(process.cwd(), '.claude', 'settings.local.json');
37
- const settingsDir = path.dirname(settingsPath);
38
- await fs.ensureDir(settingsDir);
67
+ const settingsPath = settingsFilePath();
68
+ await fs.ensureDir(path.dirname(settingsPath));
39
69
  let settings = {};
40
- // Load existing settings if present
41
70
  if (await fs.pathExists(settingsPath)) {
42
71
  try {
43
72
  settings = await fs.readJson(settingsPath);
@@ -46,35 +75,75 @@ async function installHooks() {
46
75
  settings = {};
47
76
  }
48
77
  }
49
- // Check if hooks already installed
50
- if (settings.hooks?.PostToolUse || settings.hooks?.Stop) {
51
- // Check if our hooks are already there
52
- const existingPostTool = settings.hooks.PostToolUse || [];
53
- const hasKratosHook = existingPostTool.some((h) => h.command?.includes('kratos capture'));
54
- if (hasKratosHook) {
55
- Output.warn('Kratos auto-capture hooks are already installed');
56
- return;
57
- }
58
- }
59
- // Merge hooks into existing settings (don't overwrite other hooks)
60
78
  if (!settings.hooks)
61
79
  settings.hooks = {};
62
- // Add PostToolUse hooks
63
- if (!settings.hooks.PostToolUse)
64
- settings.hooks.PostToolUse = [];
65
- settings.hooks.PostToolUse.push(...HOOK_CONFIG.hooks.PostToolUse);
66
- // Add Stop hooks
67
- if (!settings.hooks.Stop)
68
- settings.hooks.Stop = [];
69
- settings.hooks.Stop.push(...HOOK_CONFIG.hooks.Stop);
80
+ let migrated = 0;
81
+ for (const event of HOOK_EVENTS) {
82
+ const existing = settings.hooks[event] || [];
83
+ migrated += existing.filter((e) => isKratosEntry(e) && isLegacyEntry(e)).length;
84
+ // Drop any prior kratos entries (legacy or current), keep everything else
85
+ const others = existing.filter((e) => !isKratosEntry(e));
86
+ settings.hooks[event] = [...others, ...KRATOS_HOOKS[event]];
87
+ }
70
88
  await fs.writeJson(settingsPath, settings, { spaces: 2 });
71
- Output.success('Auto-capture hooks installed!');
89
+ Output.success('Kratos hooks installed');
90
+ if (migrated > 0) {
91
+ Output.warn(`Migrated ${migrated} legacy hook entr${migrated === 1 ? 'y' : 'ies'} that Claude Code was ignoring`);
92
+ }
72
93
  Output.dim(`Config written to: ${settingsPath}`);
73
- Output.dim('Hooks will capture: file edits (Edit/Write/MultiEdit) and session summaries');
74
- Output.dim('Captured memories are auto-compressed and project-isolated');
94
+ Output.dim('SessionStart: injects project memory into every session (kratos context)');
95
+ Output.dim('PostToolUse: captures file edits (Edit/Write/MultiEdit)');
96
+ Output.dim('Stop: saves a session summary');
97
+ await installGitHook();
98
+ }
99
+ async function installGitHook() {
100
+ const hooksDir = path.join(process.cwd(), '.git', 'hooks');
101
+ if (!await fs.pathExists(path.join(process.cwd(), '.git'))) {
102
+ Output.dim('No .git directory — skipped git post-commit capture');
103
+ return;
104
+ }
105
+ await fs.ensureDir(hooksDir);
106
+ const hookPath = path.join(hooksDir, 'post-commit');
107
+ if (await fs.pathExists(hookPath)) {
108
+ const existing = await fs.readFile(hookPath, 'utf8');
109
+ if (existing.includes(GIT_HOOK_START)) {
110
+ Output.dim('Git post-commit capture already installed');
111
+ return;
112
+ }
113
+ // Preserve the user's existing hook — append our block
114
+ await fs.writeFile(hookPath, existing.trimEnd() + '\n\n' + GIT_HOOK_BLOCK + '\n');
115
+ }
116
+ else {
117
+ await fs.writeFile(hookPath, '#!/bin/sh\n' + GIT_HOOK_BLOCK + '\n');
118
+ }
119
+ await fs.chmod(hookPath, 0o755);
120
+ Output.dim('Git: post-commit saves each commit as a memory');
121
+ }
122
+ async function uninstallGitHook() {
123
+ const hookPath = path.join(process.cwd(), '.git', 'hooks', 'post-commit');
124
+ if (!await fs.pathExists(hookPath))
125
+ return;
126
+ const existing = await fs.readFile(hookPath, 'utf8');
127
+ if (!existing.includes(GIT_HOOK_START))
128
+ return;
129
+ const startIdx = existing.indexOf(GIT_HOOK_START);
130
+ const endIdx = existing.indexOf(GIT_HOOK_END);
131
+ if (endIdx === -1)
132
+ return;
133
+ const cleaned = (existing.slice(0, startIdx) + existing.slice(endIdx + GIT_HOOK_END.length))
134
+ .replace(/\n{3,}/g, '\n\n')
135
+ .trimEnd();
136
+ // Nothing left but the shebang — remove the file entirely
137
+ if (cleaned.replace(/^#!.*$/m, '').trim() === '') {
138
+ await fs.remove(hookPath);
139
+ }
140
+ else {
141
+ await fs.writeFile(hookPath, cleaned + '\n');
142
+ }
143
+ Output.dim('Git post-commit capture removed');
75
144
  }
76
145
  async function uninstallHooks() {
77
- const settingsPath = path.join(process.cwd(), '.claude', 'settings.local.json');
146
+ const settingsPath = settingsFilePath();
78
147
  if (!await fs.pathExists(settingsPath)) {
79
148
  Output.warn('No hooks configuration found');
80
149
  return;
@@ -85,48 +154,66 @@ async function uninstallHooks() {
85
154
  Output.warn('No hooks found in settings');
86
155
  return;
87
156
  }
88
- // Remove only kratos hooks
89
- if (settings.hooks.PostToolUse) {
90
- settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter((h) => !h.command?.includes('kratos capture'));
91
- if (settings.hooks.PostToolUse.length === 0) {
92
- delete settings.hooks.PostToolUse;
93
- }
94
- }
95
- if (settings.hooks.Stop) {
96
- settings.hooks.Stop = settings.hooks.Stop.filter((h) => !h.command?.includes('kratos capture'));
97
- if (settings.hooks.Stop.length === 0) {
98
- delete settings.hooks.Stop;
157
+ for (const event of Object.keys(settings.hooks)) {
158
+ settings.hooks[event] = (settings.hooks[event] || []).filter((e) => !isKratosEntry(e));
159
+ if (settings.hooks[event].length === 0) {
160
+ delete settings.hooks[event];
99
161
  }
100
162
  }
101
- // Clean up empty hooks object
102
163
  if (Object.keys(settings.hooks).length === 0) {
103
164
  delete settings.hooks;
104
165
  }
105
166
  await fs.writeJson(settingsPath, settings, { spaces: 2 });
106
- Output.success('Kratos auto-capture hooks removed');
167
+ Output.success('Kratos hooks removed');
107
168
  }
108
169
  catch {
109
170
  Output.error('Failed to read settings file');
110
171
  process.exit(1);
111
172
  }
173
+ await uninstallGitHook();
112
174
  }
113
175
  async function checkHooksStatus() {
114
- const settingsPath = path.join(process.cwd(), '.claude', 'settings.local.json');
176
+ const settingsPath = settingsFilePath();
115
177
  if (!await fs.pathExists(settingsPath)) {
116
178
  Output.info('No hooks installed (no settings file found)');
117
179
  return;
118
180
  }
119
181
  try {
120
182
  const settings = await fs.readJson(settingsPath);
121
- const postToolHooks = (settings.hooks?.PostToolUse || []).filter((h) => h.command?.includes('kratos capture'));
122
- const stopHooks = (settings.hooks?.Stop || []).filter((h) => h.command?.includes('kratos capture'));
123
- if (postToolHooks.length === 0 && stopHooks.length === 0) {
183
+ let installed = 0;
184
+ let legacy = 0;
185
+ for (const event of HOOK_EVENTS) {
186
+ const entries = settings.hooks?.[event] || [];
187
+ for (const entry of entries) {
188
+ if (!isKratosEntry(entry))
189
+ continue;
190
+ if (isLegacyEntry(entry))
191
+ legacy++;
192
+ else
193
+ installed++;
194
+ }
195
+ }
196
+ if (installed === 0 && legacy === 0) {
124
197
  Output.info('Kratos hooks are NOT installed');
198
+ return;
199
+ }
200
+ if (legacy > 0) {
201
+ Output.warn(`${legacy} kratos hook entr${legacy === 1 ? 'y is' : 'ies are'} in a legacy format Claude Code ignores — run: kratos hooks install`);
125
202
  }
126
- else {
127
- Output.success('Kratos hooks are installed');
128
- Output.dim(`PostToolUse hooks: ${postToolHooks.length}`);
129
- Output.dim(`Stop hooks: ${stopHooks.length}`);
203
+ if (installed > 0) {
204
+ Output.success(`Kratos hooks are installed (${installed} active entr${installed === 1 ? 'y' : 'ies'})`);
205
+ for (const event of HOOK_EVENTS) {
206
+ const count = (settings.hooks?.[event] || []).filter((e) => isKratosEntry(e) && !isLegacyEntry(e)).length;
207
+ if (count > 0)
208
+ Output.dim(`${event}: ${count}`);
209
+ }
210
+ }
211
+ const gitHookPath = path.join(process.cwd(), '.git', 'hooks', 'post-commit');
212
+ if (await fs.pathExists(gitHookPath)) {
213
+ const content = await fs.readFile(gitHookPath, 'utf8');
214
+ if (content.includes(GIT_HOOK_START)) {
215
+ Output.dim('Git post-commit: installed');
216
+ }
130
217
  }
131
218
  }
132
219
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/cli/commands/hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/cli/commands/hooks.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C,wBAAsB,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDhB"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,wBAAsB,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CA6JhB"}
@@ -1,56 +1,140 @@
1
1
  import { Output } from '../output.js';
2
2
  import path from 'path';
3
- import fs from 'fs-extra';
3
+ import fs from 'node:fs';
4
+ import crypto from 'crypto';
4
5
  export async function migrateCommand(ctx, opts) {
5
6
  const kratosHome = opts.from || path.join(process.env.HOME || process.env.USERPROFILE || '', '.kratos');
6
7
  const projectsDir = path.join(kratosHome, 'projects');
7
8
  Output.header('Kratos Data Migration');
8
- if (!await fs.pathExists(projectsDir)) {
9
+ if (!fs.existsSync(projectsDir)) {
9
10
  Output.warn('No existing data found at: ' + projectsDir);
10
11
  Output.dim('If your data is elsewhere, use --from <path>');
11
12
  return;
12
13
  }
13
- const entries = await fs.readdir(projectsDir);
14
+ const entries = fs.readdirSync(projectsDir);
14
15
  let totalMemories = 0;
15
16
  let totalProjects = 0;
17
+ let mergedCount = 0;
18
+ // Build map: canonical (lowercase) project ID → project root
19
+ // Then find orphans: projects whose ID doesn't match the canonical hash of their root
20
+ const Database = (await import('better-sqlite3')).default;
21
+ const orphans = [];
16
22
  for (const entry of entries) {
17
- const dbPath = path.join(projectsDir, entry, 'databases', 'memories.db');
18
23
  const projectJsonPath = path.join(projectsDir, entry, 'project.json');
19
- if (!await fs.pathExists(dbPath))
24
+ const dbPath = path.join(projectsDir, entry, 'databases', 'memories.db');
25
+ if (!fs.existsSync(projectJsonPath))
20
26
  continue;
21
- totalProjects++;
22
- // Read project metadata
23
- let projectName = entry;
27
+ let meta;
24
28
  try {
25
- if (await fs.pathExists(projectJsonPath)) {
26
- const meta = await fs.readJson(projectJsonPath);
27
- projectName = meta.name || entry;
28
- }
29
+ meta = JSON.parse(fs.readFileSync(projectJsonPath, 'utf-8'));
29
30
  }
30
31
  catch {
31
- // Use directory name
32
+ continue;
32
33
  }
33
- // Count memories in the database
34
- try {
35
- const Database = (await import('better-sqlite3')).default;
36
- const db = new Database(dbPath, { readonly: true });
37
- const row = db.prepare('SELECT COUNT(*) as count FROM memories').get();
38
- totalMemories += row.count;
39
- db.close();
40
- Output.success(`${projectName}: ${row.count} memories found`);
34
+ const root = meta.root;
35
+ if (!root)
36
+ continue;
37
+ // Compute canonical ID (lowercase path hash the correct one)
38
+ const canonicalHash = crypto.createHash('sha256')
39
+ .update(root.toLowerCase())
40
+ .digest('hex');
41
+ const canonicalId = `proj_${canonicalHash.substring(0, 12)}`;
42
+ totalProjects++;
43
+ // Count memories
44
+ if (fs.existsSync(dbPath)) {
45
+ try {
46
+ const db = new Database(dbPath, { readonly: true });
47
+ const row = db.prepare('SELECT COUNT(*) as count FROM memories').get();
48
+ totalMemories += row.count;
49
+ db.close();
50
+ }
51
+ catch { /* skip */ }
52
+ }
53
+ // If this project's folder name doesn't match canonical ID, it's an orphan
54
+ if (entry !== canonicalId && fs.existsSync(dbPath)) {
55
+ orphans.push({ dir: entry, name: meta.name || entry, root, canonicalId });
41
56
  }
42
- catch (error) {
43
- Output.warn(`${projectName}: could not read database`);
57
+ }
58
+ if (orphans.length > 0) {
59
+ Output.header('Merging Orphaned Projects');
60
+ Output.dim(`Found ${orphans.length} orphaned project(s) from case-sensitive ID change`);
61
+ console.log('');
62
+ for (const orphan of orphans) {
63
+ const orphanDbPath = path.join(projectsDir, orphan.dir, 'databases', 'memories.db');
64
+ const canonicalDir = path.join(projectsDir, orphan.canonicalId);
65
+ const canonicalDbDir = path.join(canonicalDir, 'databases');
66
+ const canonicalDbPath = path.join(canonicalDbDir, 'memories.db');
67
+ try {
68
+ const srcDb = new Database(orphanDbPath, { readonly: true });
69
+ const memories = srcDb.prepare('SELECT * FROM memories').all();
70
+ srcDb.close();
71
+ if (memories.length === 0) {
72
+ Output.dim(` ${orphan.name} (${orphan.dir}): empty, skipping`);
73
+ continue;
74
+ }
75
+ // Ensure canonical project directory exists
76
+ fs.mkdirSync(canonicalDbDir, { recursive: true });
77
+ // Write project.json if missing
78
+ const canonicalPjPath = path.join(canonicalDir, 'project.json');
79
+ if (!fs.existsSync(canonicalPjPath)) {
80
+ fs.writeFileSync(canonicalPjPath, JSON.stringify({
81
+ id: orphan.canonicalId,
82
+ name: orphan.name,
83
+ root: orphan.root,
84
+ createdAt: new Date(),
85
+ lastAccessed: new Date(),
86
+ }, null, 2));
87
+ }
88
+ // Open or create canonical DB and copy memories
89
+ const dstDb = new Database(canonicalDbPath);
90
+ dstDb.pragma('journal_mode = WAL');
91
+ // Ensure schema exists
92
+ dstDb.exec(`
93
+ CREATE TABLE IF NOT EXISTS memories (
94
+ id TEXT PRIMARY KEY,
95
+ project_id TEXT NOT NULL,
96
+ summary TEXT NOT NULL,
97
+ text TEXT NOT NULL,
98
+ tags TEXT DEFAULT '[]',
99
+ paths TEXT DEFAULT '[]',
100
+ importance INTEGER DEFAULT 3,
101
+ created_at INTEGER NOT NULL,
102
+ updated_at INTEGER NOT NULL,
103
+ ttl INTEGER,
104
+ expires_at INTEGER,
105
+ dedupe_hash TEXT
106
+ )
107
+ `);
108
+ const insert = dstDb.prepare(`
109
+ INSERT OR IGNORE INTO memories (id, project_id, summary, text, tags, paths, importance, created_at, updated_at, ttl, expires_at, dedupe_hash)
110
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
111
+ `);
112
+ let copied = 0;
113
+ for (const mem of memories) {
114
+ const result = insert.run(mem.id, orphan.canonicalId, mem.summary, mem.text, mem.tags, mem.paths, mem.importance, mem.created_at, mem.updated_at, mem.ttl, mem.expires_at, mem.dedupe_hash);
115
+ if (result.changes > 0)
116
+ copied++;
117
+ }
118
+ dstDb.close();
119
+ mergedCount += copied;
120
+ Output.success(` ${orphan.name}: merged ${copied}/${memories.length} memories → ${orphan.canonicalId}`);
121
+ }
122
+ catch (error) {
123
+ Output.warn(` ${orphan.name}: merge failed — ${error instanceof Error ? error.message : 'unknown error'}`);
124
+ }
44
125
  }
45
126
  }
46
127
  console.log('');
47
128
  Output.header('Migration Summary');
48
129
  Output.info(`Projects found: ${totalProjects}`);
49
130
  Output.info(`Total memories: ${totalMemories}`);
131
+ if (mergedCount > 0) {
132
+ Output.success(`Merged memories: ${mergedCount}`);
133
+ }
50
134
  Output.info(`Data location: ${projectsDir}`);
51
- console.log('');
52
- Output.success('Your data is already compatible with the CLI!');
53
- Output.dim('The CLI reads from the same database files — no data copy needed.');
54
- Output.dim('All project isolation is preserved. Each project has its own database.');
135
+ if (orphans.length === 0) {
136
+ console.log('');
137
+ Output.success('All projects are using canonical IDs. No migration needed.');
138
+ }
55
139
  }
56
140
  //# sourceMappingURL=migrate.js.map