memorix 1.1.10 → 1.1.11

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 (187) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/TEAM.md +86 -86
  3. package/dist/cli/index.js +2282 -815
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/dashboard/static/app.js +29 -3
  6. package/dist/dashboard/static/index.html +201 -201
  7. package/dist/dashboard/static/style.css +3762 -3584
  8. package/dist/index.js +2639 -1283
  9. package/dist/index.js.map +1 -1
  10. package/dist/maintenance-runner.d.ts +40 -0
  11. package/dist/maintenance-runner.js +8193 -0
  12. package/dist/maintenance-runner.js.map +1 -0
  13. package/dist/memcode-runtime/CHANGELOG.md +22 -0
  14. package/dist/memcode-runtime/package.json +4 -4
  15. package/dist/sdk.js +2636 -1280
  16. package/dist/sdk.js.map +1 -1
  17. package/docs/CONFIGURATION.md +15 -4
  18. package/docs/DESIGN_DECISIONS.md +357 -357
  19. package/docs/GIT_MEMORY.md +2 -2
  20. package/docs/PERFORMANCE.md +23 -0
  21. package/docs/dev-log/progress.txt +54 -27
  22. package/package.json +1 -1
  23. package/src/audit/index.ts +156 -156
  24. package/src/cli/commands/audit-list.ts +89 -89
  25. package/src/cli/commands/background.ts +659 -659
  26. package/src/cli/commands/cleanup.ts +279 -255
  27. package/src/cli/commands/codegraph.ts +7 -5
  28. package/src/cli/commands/formation.ts +48 -48
  29. package/src/cli/commands/git-hook-install.ts +111 -111
  30. package/src/cli/commands/handoff.ts +66 -66
  31. package/src/cli/commands/hooks-status.ts +63 -63
  32. package/src/cli/commands/ingest-commit.ts +153 -153
  33. package/src/cli/commands/ingest-image.ts +73 -73
  34. package/src/cli/commands/ingest-log.ts +180 -180
  35. package/src/cli/commands/ingest.ts +44 -44
  36. package/src/cli/commands/integrate-shared.ts +15 -15
  37. package/src/cli/commands/lock.ts +96 -96
  38. package/src/cli/commands/message.ts +121 -121
  39. package/src/cli/commands/poll.ts +70 -70
  40. package/src/cli/commands/purge-all-memory.ts +85 -85
  41. package/src/cli/commands/purge-project-memory.ts +83 -83
  42. package/src/cli/commands/reasoning.ts +132 -132
  43. package/src/cli/commands/retention.ts +107 -108
  44. package/src/cli/commands/serve-http.ts +49 -22
  45. package/src/cli/commands/serve-shared.ts +118 -118
  46. package/src/cli/commands/skills.ts +123 -123
  47. package/src/cli/commands/task.ts +192 -192
  48. package/src/cli/commands/transfer.ts +73 -73
  49. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  50. package/src/cli/tui/ChatView.tsx +234 -234
  51. package/src/cli/tui/CommandBar.tsx +312 -312
  52. package/src/cli/tui/ContextRail.tsx +118 -118
  53. package/src/cli/tui/HeaderBar.tsx +72 -72
  54. package/src/cli/tui/LogoBanner.tsx +51 -51
  55. package/src/cli/tui/Panels.tsx +632 -632
  56. package/src/cli/tui/Sidebar.tsx +179 -179
  57. package/src/cli/tui/chat-service.ts +742 -742
  58. package/src/cli/tui/data.ts +547 -547
  59. package/src/cli/tui/index.ts +41 -41
  60. package/src/cli/tui/markdown-render.tsx +371 -371
  61. package/src/cli/tui/theme.ts +178 -178
  62. package/src/cli/tui/use-mouse.ts +157 -157
  63. package/src/cli/tui/useNavigation.ts +56 -56
  64. package/src/cli/update-checker.ts +211 -211
  65. package/src/cli/version.ts +7 -7
  66. package/src/cli/workbench.ts +1 -1
  67. package/src/codegraph/auto-context.ts +49 -22
  68. package/src/codegraph/exclude.ts +10 -0
  69. package/src/codegraph/lite-provider.ts +146 -16
  70. package/src/codegraph/project-context.ts +23 -0
  71. package/src/codegraph/store.ts +123 -0
  72. package/src/compact/token-budget.ts +74 -74
  73. package/src/config/behavior.ts +97 -59
  74. package/src/config/resolved-config.ts +4 -0
  75. package/src/config/toml-loader.ts +2 -0
  76. package/src/config/yaml-loader.ts +3 -1
  77. package/src/dashboard/project-classification.ts +64 -64
  78. package/src/dashboard/server.ts +31 -30
  79. package/src/dashboard/static/index.html +201 -201
  80. package/src/dashboard/static/style.css +3762 -3584
  81. package/src/embedding/fastembed-provider.ts +142 -142
  82. package/src/embedding/transformers-provider.ts +111 -111
  83. package/src/git/extractor.ts +209 -209
  84. package/src/git/hooks-path.ts +85 -85
  85. package/src/git/noise-filter.ts +226 -210
  86. package/src/hooks/handler.ts +10 -2
  87. package/src/hooks/pattern-detector.ts +173 -173
  88. package/src/hooks/significance-filter.ts +250 -250
  89. package/src/llm/memory-manager.ts +328 -328
  90. package/src/llm/provider.ts +885 -885
  91. package/src/llm/quality.ts +248 -248
  92. package/src/memory/attribution-guard.ts +249 -249
  93. package/src/memory/auto-relations.ts +107 -107
  94. package/src/memory/consolidation.ts +328 -302
  95. package/src/memory/disclosure-policy.ts +141 -141
  96. package/src/memory/entity-extractor.ts +197 -197
  97. package/src/memory/export-import.ts +3 -1
  98. package/src/memory/formation/evaluate.ts +217 -217
  99. package/src/memory/formation/extract.ts +361 -361
  100. package/src/memory/formation/index.ts +417 -417
  101. package/src/memory/formation/resolve.ts +344 -344
  102. package/src/memory/formation/types.ts +315 -315
  103. package/src/memory/freshness.ts +122 -122
  104. package/src/memory/graph.ts +197 -197
  105. package/src/memory/observations.ts +91 -27
  106. package/src/memory/refs.ts +94 -94
  107. package/src/memory/retention.ts +511 -433
  108. package/src/memory/secret-filter.ts +79 -79
  109. package/src/memory/session.ts +542 -523
  110. package/src/multimodal/image-loader.ts +143 -143
  111. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  112. package/src/orchestrate/adapters/claude.ts +111 -111
  113. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  114. package/src/orchestrate/adapters/codex.ts +41 -41
  115. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  116. package/src/orchestrate/adapters/gemini.ts +42 -42
  117. package/src/orchestrate/adapters/index.ts +73 -73
  118. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  119. package/src/orchestrate/adapters/opencode.ts +47 -47
  120. package/src/orchestrate/adapters/spawn-helper.ts +286 -286
  121. package/src/orchestrate/adapters/types.ts +77 -77
  122. package/src/orchestrate/capability-router.ts +284 -284
  123. package/src/orchestrate/context-compact.ts +188 -188
  124. package/src/orchestrate/cost-tracker.ts +219 -219
  125. package/src/orchestrate/error-recovery.ts +191 -191
  126. package/src/orchestrate/evidence.ts +140 -140
  127. package/src/orchestrate/ledger.ts +110 -110
  128. package/src/orchestrate/memorix-bridge.ts +380 -380
  129. package/src/orchestrate/output-budget.ts +80 -80
  130. package/src/orchestrate/permission.ts +152 -152
  131. package/src/orchestrate/pipeline-trace.ts +131 -131
  132. package/src/orchestrate/prompt-builder.ts +155 -155
  133. package/src/orchestrate/ring-buffer.ts +37 -37
  134. package/src/orchestrate/task-graph.ts +389 -389
  135. package/src/orchestrate/verify-gate.ts +219 -219
  136. package/src/orchestrate/worktree.ts +232 -232
  137. package/src/project/aliases.ts +374 -374
  138. package/src/project/detector.ts +268 -268
  139. package/src/rules/adapters/claude-code.ts +99 -99
  140. package/src/rules/adapters/codex.ts +97 -97
  141. package/src/rules/adapters/copilot.ts +124 -124
  142. package/src/rules/adapters/cursor.ts +114 -114
  143. package/src/rules/adapters/kiro.ts +126 -126
  144. package/src/rules/adapters/trae.ts +56 -56
  145. package/src/rules/adapters/windsurf.ts +83 -83
  146. package/src/rules/syncer.ts +235 -235
  147. package/src/runtime/control-plane-maintenance.ts +62 -0
  148. package/src/runtime/isolated-maintenance.ts +187 -0
  149. package/src/runtime/maintenance-jobs.ts +512 -0
  150. package/src/runtime/maintenance-runner.ts +96 -0
  151. package/src/runtime/maintenance-targets.ts +59 -0
  152. package/src/runtime/project-maintenance.ts +274 -0
  153. package/src/sdk.ts +327 -327
  154. package/src/search/intent-detector.ts +289 -289
  155. package/src/search/query-expansion.ts +52 -52
  156. package/src/server/formation-timeout.ts +27 -27
  157. package/src/server.ts +212 -224
  158. package/src/skills/mini-skills.ts +386 -386
  159. package/src/store/chat-store.ts +119 -119
  160. package/src/store/file-lock.ts +100 -100
  161. package/src/store/graph-store.ts +249 -249
  162. package/src/store/mini-skill-store.ts +349 -349
  163. package/src/store/obs-store.ts +293 -255
  164. package/src/store/persistence-json.ts +212 -212
  165. package/src/store/persistence.ts +291 -291
  166. package/src/store/project-affinity.ts +195 -195
  167. package/src/store/session-store.ts +268 -259
  168. package/src/store/sqlite-db.ts +40 -0
  169. package/src/store/sqlite-store.ts +429 -339
  170. package/src/team/event-bus.ts +76 -76
  171. package/src/team/file-locks.ts +173 -173
  172. package/src/team/handoff.ts +167 -167
  173. package/src/team/messages.ts +203 -203
  174. package/src/team/poll.ts +132 -132
  175. package/src/team/tasks.ts +211 -211
  176. package/src/wiki/generator.ts +237 -237
  177. package/src/wiki/knowledge-graph.ts +334 -334
  178. package/src/wiki/types.ts +85 -85
  179. package/src/workspace/mcp-adapters/codex.ts +191 -191
  180. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  181. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  182. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  183. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  184. package/src/workspace/mcp-adapters/trae.ts +134 -134
  185. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  186. package/src/workspace/sanitizer.ts +60 -60
  187. package/src/workspace/workflow-sync.ts +131 -131
@@ -9,85 +9,85 @@
9
9
  * Set MEMORIX_AUTO_UPDATE=install to opt into silent background install.
10
10
  * Disable via MEMORIX_AUTO_UPDATE=off.
11
11
  */
12
-
13
- import { execFile } from 'node:child_process';
14
- import type { ExecFileException } from 'node:child_process';
15
- import { readFile, writeFile, mkdir } from 'node:fs/promises';
16
- import { join } from 'node:path';
17
- import { homedir } from 'node:os';
18
- import { getCliVersion } from './version.js';
19
-
20
- const PACKAGE_NAME = 'memorix';
21
- const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
22
- const DEFAULT_AUTO_UPDATE_TIMEOUT_MS = 5 * 60 * 1000;
23
- const MIN_AUTO_UPDATE_TIMEOUT_MS = 5_000;
24
- const MAX_AUTO_UPDATE_TIMEOUT_MS = 30 * 60 * 1000;
25
- const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
26
- const CACHE_DIR = join(homedir(), '.memorix');
27
- const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
28
-
12
+
13
+ import { execFile } from 'node:child_process';
14
+ import type { ExecFileException } from 'node:child_process';
15
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
16
+ import { join } from 'node:path';
17
+ import { homedir } from 'node:os';
18
+ import { getCliVersion } from './version.js';
19
+
20
+ const PACKAGE_NAME = 'memorix';
21
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
22
+ const DEFAULT_AUTO_UPDATE_TIMEOUT_MS = 5 * 60 * 1000;
23
+ const MIN_AUTO_UPDATE_TIMEOUT_MS = 5_000;
24
+ const MAX_AUTO_UPDATE_TIMEOUT_MS = 30 * 60 * 1000;
25
+ const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
26
+ const CACHE_DIR = join(homedir(), '.memorix');
27
+ const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
28
+
29
29
  export interface UpdateCache {
30
- lastCheck: number;
31
- latestVersion: string;
32
- lastAutoUpdate?: number;
33
- lastAutoUpdateStatus?: 'success' | 'failed';
34
- lastAutoUpdateError?: string;
35
- lastAutoUpdateTimedOut?: boolean;
36
- lastAutoUpdateExitCode?: number | null;
37
- lastAutoUpdateSignal?: string | null;
38
- lastAutoUpdateTimeoutMs?: number;
39
- updatedFrom?: string;
40
- updatedTo?: string;
30
+ lastCheck: number;
31
+ latestVersion: string;
32
+ lastAutoUpdate?: number;
33
+ lastAutoUpdateStatus?: 'success' | 'failed';
34
+ lastAutoUpdateError?: string;
35
+ lastAutoUpdateTimedOut?: boolean;
36
+ lastAutoUpdateExitCode?: number | null;
37
+ lastAutoUpdateSignal?: string | null;
38
+ lastAutoUpdateTimeoutMs?: number;
39
+ updatedFrom?: string;
40
+ updatedTo?: string;
41
41
  }
42
42
 
43
43
  export type AutoUpdateMode = 'off' | 'notify' | 'install';
44
-
45
- /**
46
- * Get the current installed version from package.json.
47
- */
48
- export function getCurrentVersion(): string {
49
- try {
50
- return getCliVersion();
51
- } catch {
52
- return '0.0.0';
53
- }
54
- }
55
-
56
- /**
57
- * Compare two semver strings. Returns true if remote > local.
58
- */
59
- export function isNewer(remote: string, local: string): boolean {
60
- const r = remote.split('.').map(Number);
61
- const l = local.split('.').map(Number);
62
- for (let i = 0; i < 3; i++) {
63
- if ((r[i] ?? 0) > (l[i] ?? 0)) return true;
64
- if ((r[i] ?? 0) < (l[i] ?? 0)) return false;
65
- }
66
- return false;
67
- }
68
-
69
- /**
70
- * Read the update check cache. Exported for doctor/status display.
71
- */
72
- export async function readCache(): Promise<UpdateCache | null> {
73
- try {
74
- const data = await readFile(CACHE_FILE, 'utf-8');
75
- return JSON.parse(data);
76
- } catch {
77
- return null;
78
- }
79
- }
80
-
81
- /**
82
- * Write the update check cache.
83
- */
84
- async function writeCache(cache: UpdateCache): Promise<void> {
85
- try {
86
- await mkdir(CACHE_DIR, { recursive: true });
87
- await writeFile(CACHE_FILE, JSON.stringify(cache, null, 2), 'utf-8');
88
- } catch { /* silent */ }
89
- }
90
-
44
+
45
+ /**
46
+ * Get the current installed version from package.json.
47
+ */
48
+ export function getCurrentVersion(): string {
49
+ try {
50
+ return getCliVersion();
51
+ } catch {
52
+ return '0.0.0';
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Compare two semver strings. Returns true if remote > local.
58
+ */
59
+ export function isNewer(remote: string, local: string): boolean {
60
+ const r = remote.split('.').map(Number);
61
+ const l = local.split('.').map(Number);
62
+ for (let i = 0; i < 3; i++) {
63
+ if ((r[i] ?? 0) > (l[i] ?? 0)) return true;
64
+ if ((r[i] ?? 0) < (l[i] ?? 0)) return false;
65
+ }
66
+ return false;
67
+ }
68
+
69
+ /**
70
+ * Read the update check cache. Exported for doctor/status display.
71
+ */
72
+ export async function readCache(): Promise<UpdateCache | null> {
73
+ try {
74
+ const data = await readFile(CACHE_FILE, 'utf-8');
75
+ return JSON.parse(data);
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Write the update check cache.
83
+ */
84
+ async function writeCache(cache: UpdateCache): Promise<void> {
85
+ try {
86
+ await mkdir(CACHE_DIR, { recursive: true });
87
+ await writeFile(CACHE_FILE, JSON.stringify(cache, null, 2), 'utf-8');
88
+ } catch { /* silent */ }
89
+ }
90
+
91
91
  /**
92
92
  * Parse MEMORIX_AUTO_UPDATE into an explicit mode.
93
93
  * Default: notify-only.
@@ -99,105 +99,105 @@ function parseAutoUpdateMode(raw: string | undefined): AutoUpdateMode {
99
99
  if (env === 'install' || env === 'true' || env === '1') return 'install';
100
100
  return 'notify';
101
101
  }
102
-
103
- function parseAutoUpdateTimeoutMs(raw: string | undefined): number {
104
- const value = raw?.trim();
105
- if (!value) return DEFAULT_AUTO_UPDATE_TIMEOUT_MS;
106
- const parsed = Number(value);
107
- if (!Number.isFinite(parsed)) return DEFAULT_AUTO_UPDATE_TIMEOUT_MS;
108
- return Math.max(MIN_AUTO_UPDATE_TIMEOUT_MS, Math.min(MAX_AUTO_UPDATE_TIMEOUT_MS, Math.floor(parsed)));
109
- }
110
-
111
- function describeAutoUpdateFailure(error: ExecFileException, timeoutMs: number): {
112
- message: string;
113
- timedOut: boolean;
114
- exitCode: number | null;
115
- signal: string | null;
116
- } {
117
- const timedOut = /timed out/i.test(error.message);
118
- const exitCode = typeof error.code === 'number' ? error.code : null;
119
- const signal = typeof error.signal === 'string' ? error.signal : null;
120
- const details: string[] = [];
121
- if (timedOut) details.push(`timeout ${timeoutMs}ms`);
122
- if (exitCode !== null) details.push(`exit code ${exitCode}`);
123
- if (signal) details.push(`signal ${signal}`);
124
- return {
125
- message: details.length > 0 ? `${error.message} (${details.join(', ')})` : error.message,
126
- timedOut,
127
- exitCode,
128
- signal,
129
- };
130
- }
131
-
132
- /**
133
- * Fetch the latest version from npm registry.
134
- * Uses native https to avoid dependencies. Timeout: 5s.
135
- */
136
- async function fetchLatestVersion(): Promise<string | null> {
137
- try {
138
- const { default: https } = await import('node:https');
139
- return new Promise((resolve) => {
140
- const req = https.get(REGISTRY_URL, { timeout: 5000 }, (res) => {
141
- if (res.statusCode !== 200) { resolve(null); return; }
142
- let body = '';
143
- res.on('data', (chunk: Buffer) => { body += chunk.toString(); });
144
- res.on('end', () => {
145
- try {
146
- const data = JSON.parse(body);
147
- resolve(data.version ?? null);
148
- } catch { resolve(null); }
149
- });
150
- });
151
- req.on('error', () => resolve(null));
152
- req.on('timeout', () => { req.destroy(); resolve(null); });
153
- });
154
- } catch {
155
- return null;
156
- }
157
- }
158
-
159
- /**
160
- * Silently install the latest version in the background.
161
- * Writes result to the cache file so doctor/status can report it.
162
- */
163
- function installUpdateInBackground(targetVersion: string, currentVersion: string, cache: UpdateCache): void {
164
- try {
165
- const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
166
- const timeoutMs = parseAutoUpdateTimeoutMs(process.env.MEMORIX_AUTO_UPDATE_TIMEOUT_MS);
167
- const child = execFile(
168
- npmCmd,
169
- ['install', '-g', `${PACKAGE_NAME}@${targetVersion}`],
170
- { timeout: timeoutMs },
171
- async (error) => {
172
- const failure = error ? describeAutoUpdateFailure(error, timeoutMs) : null;
173
- const updatedCache: UpdateCache = {
174
- ...cache,
175
- lastAutoUpdate: Date.now(),
176
- updatedFrom: currentVersion,
177
- updatedTo: targetVersion,
178
- lastAutoUpdateStatus: error ? 'failed' : 'success',
179
- lastAutoUpdateError: failure?.message,
180
- lastAutoUpdateTimedOut: failure?.timedOut,
181
- lastAutoUpdateExitCode: failure?.exitCode,
182
- lastAutoUpdateSignal: failure?.signal,
183
- lastAutoUpdateTimeoutMs: timeoutMs,
184
- };
185
- await writeCache(updatedCache);
186
- if (error) {
187
- console.error(`[memorix] Auto-update failed: ${failure?.message ?? error.message}`);
188
- } else {
189
- console.error(`[memorix] Auto-updated to v${targetVersion} — restart to apply`);
190
- }
191
- },
192
- );
193
- // Unref so the child process doesn't prevent the main process from exiting
194
- child.unref();
195
- } catch (err) {
196
- console.error(`[memorix] Auto-update spawn failed:`, (err as Error)?.message ?? err);
197
- }
198
- }
199
-
200
- /**
102
+
103
+ function parseAutoUpdateTimeoutMs(raw: string | undefined): number {
104
+ const value = raw?.trim();
105
+ if (!value) return DEFAULT_AUTO_UPDATE_TIMEOUT_MS;
106
+ const parsed = Number(value);
107
+ if (!Number.isFinite(parsed)) return DEFAULT_AUTO_UPDATE_TIMEOUT_MS;
108
+ return Math.max(MIN_AUTO_UPDATE_TIMEOUT_MS, Math.min(MAX_AUTO_UPDATE_TIMEOUT_MS, Math.floor(parsed)));
109
+ }
110
+
111
+ function describeAutoUpdateFailure(error: ExecFileException, timeoutMs: number): {
112
+ message: string;
113
+ timedOut: boolean;
114
+ exitCode: number | null;
115
+ signal: string | null;
116
+ } {
117
+ const timedOut = /timed out/i.test(error.message);
118
+ const exitCode = typeof error.code === 'number' ? error.code : null;
119
+ const signal = typeof error.signal === 'string' ? error.signal : null;
120
+ const details: string[] = [];
121
+ if (timedOut) details.push(`timeout ${timeoutMs}ms`);
122
+ if (exitCode !== null) details.push(`exit code ${exitCode}`);
123
+ if (signal) details.push(`signal ${signal}`);
124
+ return {
125
+ message: details.length > 0 ? `${error.message} (${details.join(', ')})` : error.message,
126
+ timedOut,
127
+ exitCode,
128
+ signal,
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Fetch the latest version from npm registry.
134
+ * Uses native https to avoid dependencies. Timeout: 5s.
135
+ */
136
+ async function fetchLatestVersion(): Promise<string | null> {
137
+ try {
138
+ const { default: https } = await import('node:https');
139
+ return new Promise((resolve) => {
140
+ const req = https.get(REGISTRY_URL, { timeout: 5000 }, (res) => {
141
+ if (res.statusCode !== 200) { resolve(null); return; }
142
+ let body = '';
143
+ res.on('data', (chunk: Buffer) => { body += chunk.toString(); });
144
+ res.on('end', () => {
145
+ try {
146
+ const data = JSON.parse(body);
147
+ resolve(data.version ?? null);
148
+ } catch { resolve(null); }
149
+ });
150
+ });
151
+ req.on('error', () => resolve(null));
152
+ req.on('timeout', () => { req.destroy(); resolve(null); });
153
+ });
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Silently install the latest version in the background.
161
+ * Writes result to the cache file so doctor/status can report it.
162
+ */
163
+ function installUpdateInBackground(targetVersion: string, currentVersion: string, cache: UpdateCache): void {
164
+ try {
165
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
166
+ const timeoutMs = parseAutoUpdateTimeoutMs(process.env.MEMORIX_AUTO_UPDATE_TIMEOUT_MS);
167
+ const child = execFile(
168
+ npmCmd,
169
+ ['install', '-g', `${PACKAGE_NAME}@${targetVersion}`],
170
+ { timeout: timeoutMs },
171
+ async (error) => {
172
+ const failure = error ? describeAutoUpdateFailure(error, timeoutMs) : null;
173
+ const updatedCache: UpdateCache = {
174
+ ...cache,
175
+ lastAutoUpdate: Date.now(),
176
+ updatedFrom: currentVersion,
177
+ updatedTo: targetVersion,
178
+ lastAutoUpdateStatus: error ? 'failed' : 'success',
179
+ lastAutoUpdateError: failure?.message,
180
+ lastAutoUpdateTimedOut: failure?.timedOut,
181
+ lastAutoUpdateExitCode: failure?.exitCode,
182
+ lastAutoUpdateSignal: failure?.signal,
183
+ lastAutoUpdateTimeoutMs: timeoutMs,
184
+ };
185
+ await writeCache(updatedCache);
186
+ if (error) {
187
+ console.error(`[memorix] Auto-update failed: ${failure?.message ?? error.message}`);
188
+ } else {
189
+ console.error(`[memorix] Auto-updated to v${targetVersion} — restart to apply`);
190
+ }
191
+ },
192
+ );
193
+ // Unref so the child process doesn't prevent the main process from exiting
194
+ child.unref();
195
+ } catch (err) {
196
+ console.error(`[memorix] Auto-update spawn failed:`, (err as Error)?.message ?? err);
197
+ }
198
+ }
199
+
200
+ /**
201
201
  * Run the background update check.
202
202
  *
203
203
  * Call this fire-and-forget from entry points (serve-http, TUI).
@@ -215,32 +215,32 @@ export async function checkForUpdates(): Promise<void> {
215
215
 
216
216
  const cache = await readCache();
217
217
  const now = Date.now();
218
-
219
- // Rate limit: skip if checked within the last 24 hours
220
- if (cache && (now - cache.lastCheck) < CHECK_INTERVAL_MS) {
221
- return;
222
- }
223
-
224
- const latestVersion = await fetchLatestVersion();
225
- if (!latestVersion) return;
226
-
227
- const currentVersion = getCurrentVersion();
228
-
229
- // Update cache with check timestamp
230
- const updatedCache: UpdateCache = {
231
- lastCheck: now,
232
- latestVersion,
233
- lastAutoUpdate: cache?.lastAutoUpdate,
234
- lastAutoUpdateStatus: cache?.lastAutoUpdateStatus,
235
- lastAutoUpdateError: cache?.lastAutoUpdateError,
236
- lastAutoUpdateTimedOut: cache?.lastAutoUpdateTimedOut,
237
- lastAutoUpdateExitCode: cache?.lastAutoUpdateExitCode,
238
- lastAutoUpdateSignal: cache?.lastAutoUpdateSignal,
239
- lastAutoUpdateTimeoutMs: cache?.lastAutoUpdateTimeoutMs,
240
- updatedFrom: cache?.updatedFrom,
241
- updatedTo: cache?.updatedTo,
242
- };
243
- await writeCache(updatedCache);
218
+
219
+ // Rate limit: skip if checked within the last 24 hours
220
+ if (cache && (now - cache.lastCheck) < CHECK_INTERVAL_MS) {
221
+ return;
222
+ }
223
+
224
+ const latestVersion = await fetchLatestVersion();
225
+ if (!latestVersion) return;
226
+
227
+ const currentVersion = getCurrentVersion();
228
+
229
+ // Update cache with check timestamp
230
+ const updatedCache: UpdateCache = {
231
+ lastCheck: now,
232
+ latestVersion,
233
+ lastAutoUpdate: cache?.lastAutoUpdate,
234
+ lastAutoUpdateStatus: cache?.lastAutoUpdateStatus,
235
+ lastAutoUpdateError: cache?.lastAutoUpdateError,
236
+ lastAutoUpdateTimedOut: cache?.lastAutoUpdateTimedOut,
237
+ lastAutoUpdateExitCode: cache?.lastAutoUpdateExitCode,
238
+ lastAutoUpdateSignal: cache?.lastAutoUpdateSignal,
239
+ lastAutoUpdateTimeoutMs: cache?.lastAutoUpdateTimeoutMs,
240
+ updatedFrom: cache?.updatedFrom,
241
+ updatedTo: cache?.updatedTo,
242
+ };
243
+ await writeCache(updatedCache);
244
244
 
245
245
  if (isNewer(latestVersion, currentVersion)) {
246
246
  if (mode === 'install') {
@@ -254,12 +254,12 @@ export async function checkForUpdates(): Promise<void> {
254
254
  }
255
255
  } catch {
256
256
  // Entire update check is best-effort — never crash the caller
257
- }
258
- }
259
-
260
- // ── Test helpers (exported for testing only) ──────────────────
261
-
262
- /** @internal */
257
+ }
258
+ }
259
+
260
+ // ── Test helpers (exported for testing only) ──────────────────
261
+
262
+ /** @internal */
263
263
  export const _testing = {
264
264
  CACHE_FILE,
265
265
  CHECK_INTERVAL_MS,
@@ -268,8 +268,8 @@ export const _testing = {
268
268
  parseAutoUpdateTimeoutMs,
269
269
  describeAutoUpdateFailure,
270
270
  fetchLatestVersion,
271
- installUpdateInBackground,
272
- writeCache,
273
- isNewer,
274
- getCurrentVersion,
275
- };
271
+ installUpdateInBackground,
272
+ writeCache,
273
+ isNewer,
274
+ getCurrentVersion,
275
+ };
@@ -1,7 +1,7 @@
1
- import pkg from '../../package.json';
2
-
3
- export function getCliVersion(): string {
4
- return typeof __MEMORIX_VERSION__ !== 'undefined'
5
- ? __MEMORIX_VERSION__
6
- : pkg.version;
7
- }
1
+ import pkg from '../../package.json';
2
+
3
+ export function getCliVersion(): string {
4
+ return typeof __MEMORIX_VERSION__ !== 'undefined'
5
+ ? __MEMORIX_VERSION__
6
+ : pkg.version;
7
+ }
@@ -5,4 +5,4 @@
5
5
  * Kept for backward-compat: `import { startWorkbench } from './workbench.js'`
6
6
  */
7
7
 
8
- export { startWorkbench } from './tui/index.js';
8
+ export { startWorkbench } from './tui/index.js';
@@ -4,7 +4,7 @@ import { getResolvedConfig } from '../config/resolved-config.js';
4
4
  import type { ProjectInfo } from '../types.js';
5
5
  import { backfillMissingObservationCodeRefs, type CodeRefBackfillResult } from './binder.js';
6
6
  import { collectCurrentProjectFacts, type CurrentProjectFacts } from './current-facts.js';
7
- import { indexProjectLite } from './lite-provider.js';
7
+ import { refreshProjectLite } from './lite-provider.js';
8
8
  import {
9
9
  buildProjectContextExplain,
10
10
  type ProjectContextExplain,
@@ -27,7 +27,7 @@ export type AutoContextRefreshMode = 'auto' | 'always' | 'never';
27
27
  export interface AutoContextRefreshResult {
28
28
  mode: AutoContextRefreshMode;
29
29
  performed: boolean;
30
- reason: 'forced' | 'empty-index' | 'missing-scan-time' | 'stale-index' | 'fresh-enough' | 'disabled' | 'failed';
30
+ reason: 'forced' | 'empty-index' | 'missing-scan-time' | 'stale-index' | 'fresh-enough' | 'disabled' | 'queued' | 'failed';
31
31
  message: string;
32
32
  backfill?: CodeRefBackfillResult;
33
33
  }
@@ -101,12 +101,20 @@ export async function buildAutoProjectContext(input: {
101
101
  maxAgeMs?: number;
102
102
  now?: Date;
103
103
  exclude?: string[];
104
+ maxFileBytes?: number;
105
+ /**
106
+ * When supplied, a needed refresh is queued instead of running in this
107
+ * request. MCP and hook callers use this to keep their response path fast.
108
+ */
109
+ enqueueRefresh?: () => void | Promise<void>;
104
110
  }): Promise<AutoProjectContext> {
105
111
  const refreshMode = input.refresh ?? 'auto';
106
112
  const now = input.now ?? new Date();
107
113
  const task = input.task?.trim();
108
114
  const lens = resolveTaskLens(task);
109
- const exclude = input.exclude ?? getResolvedConfig({ projectRoot: input.project.rootPath }).codegraph.excludePatterns;
115
+ const codegraphConfig = getResolvedConfig({ projectRoot: input.project.rootPath }).codegraph;
116
+ const exclude = input.exclude ?? codegraphConfig.excludePatterns;
117
+ const maxFileBytes = input.maxFileBytes ?? codegraphConfig.maxFileBytes;
110
118
  const store = new CodeGraphStore();
111
119
  await store.init(input.dataDir);
112
120
 
@@ -124,25 +132,44 @@ export async function buildAutoProjectContext(input: {
124
132
  };
125
133
 
126
134
  if (decision.performed) {
127
- try {
128
- const indexed = await indexProjectLite({
129
- projectId: input.project.id,
130
- projectRoot: input.project.rootPath,
131
- exclude,
132
- });
133
- store.replaceProjectIndex(input.project.id, indexed);
134
- const backfill = await backfillMissingObservationCodeRefs(
135
- store,
136
- activeProjectObservations(input.observations, input.project.id) as any,
137
- );
138
- refresh = { ...refresh, backfill };
139
- } catch (error) {
140
- refresh = {
141
- mode: refreshMode,
142
- performed: false,
143
- reason: 'failed',
144
- message: `Project scan failed: ${error instanceof Error ? error.message : String(error)}`,
145
- };
135
+ if (input.enqueueRefresh) {
136
+ try {
137
+ await input.enqueueRefresh();
138
+ refresh = {
139
+ mode: refreshMode,
140
+ performed: false,
141
+ reason: 'queued',
142
+ message: 'Code Memory refresh queued; this brief uses the latest completed scan.',
143
+ };
144
+ } catch (error) {
145
+ refresh = {
146
+ mode: refreshMode,
147
+ performed: false,
148
+ reason: 'failed',
149
+ message: `Could not queue Code Memory refresh: ${error instanceof Error ? error.message : String(error)}`,
150
+ };
151
+ }
152
+ } else {
153
+ try {
154
+ await refreshProjectLite(store, {
155
+ projectId: input.project.id,
156
+ projectRoot: input.project.rootPath,
157
+ exclude,
158
+ maxFileBytes,
159
+ });
160
+ const backfill = await backfillMissingObservationCodeRefs(
161
+ store,
162
+ activeProjectObservations(input.observations, input.project.id) as any,
163
+ );
164
+ refresh = { ...refresh, backfill };
165
+ } catch (error) {
166
+ refresh = {
167
+ mode: refreshMode,
168
+ performed: false,
169
+ reason: 'failed',
170
+ message: `Project scan failed: ${error instanceof Error ? error.message : String(error)}`,
171
+ };
172
+ }
146
173
  }
147
174
  }
148
175
 
@@ -9,6 +9,16 @@ export const DEFAULT_CODEGRAPH_EXCLUDES = [
9
9
  '.tmp/**',
10
10
  '.worktrees/**',
11
11
  '.claude/worktrees/**',
12
+ 'vendor/**',
13
+ '.venv/**',
14
+ 'venv/**',
15
+ '.tox/**',
16
+ 'target/**',
17
+ '.gradle/**',
18
+ '.mvn/**',
19
+ 'obj/**',
20
+ 'Pods/**',
21
+ 'DerivedData/**',
12
22
  ];
13
23
 
14
24
  export function normalizeCodeGraphExcludePatterns(exclude?: string[]): string[] {