@pcircle/memesh 4.3.0 → 4.5.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/README.de.md +35 -12
- package/README.es.md +13 -9
- package/README.fr.md +33 -10
- package/README.ja.md +13 -9
- package/README.ko.md +14 -10
- package/README.md +18 -11
- package/README.pt.md +32 -9
- package/README.th.md +18 -8
- package/README.vi.md +34 -11
- package/README.zh-CN.md +14 -10
- package/README.zh-TW.md +14 -10
- package/dashboard/dist/index.html +10 -10
- package/dist/cli/view-live.js +2 -2
- package/dist/cli/view.js +1 -1
- package/dist/core/analytics.d.ts +4 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js +8 -8
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/auto-tagger.d.ts.map +1 -1
- package/dist/core/auto-tagger.js.map +1 -1
- package/dist/core/config.d.ts +5 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +28 -13
- package/dist/core/config.js.map +1 -1
- package/dist/core/demo.d.ts +1 -0
- package/dist/core/demo.d.ts.map +1 -1
- package/dist/core/demo.js +24 -0
- package/dist/core/demo.js.map +1 -1
- package/dist/core/digest-validator.d.ts.map +1 -1
- package/dist/core/digest-validator.js +3 -1
- package/dist/core/digest-validator.js.map +1 -1
- package/dist/core/doctor.d.ts +2 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +70 -60
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +21 -1
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +86 -8
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts +1 -4
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +5 -92
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +2 -1
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/llm-client.d.ts.map +1 -1
- package/dist/core/llm-client.js.map +1 -1
- package/dist/core/llm-validator.d.ts +1 -0
- package/dist/core/llm-validator.d.ts.map +1 -1
- package/dist/core/llm-validator.js +33 -10
- package/dist/core/llm-validator.js.map +1 -1
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +10 -3
- package/dist/core/operations.js.map +1 -1
- package/dist/core/output-language.d.ts +6 -0
- package/dist/core/output-language.d.ts.map +1 -0
- package/dist/core/output-language.js +25 -0
- package/dist/core/output-language.js.map +1 -0
- package/dist/core/patterns.d.ts +0 -1
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js +1 -5
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/transcript-extractor.d.ts +89 -0
- package/dist/core/transcript-extractor.d.ts.map +1 -0
- package/dist/core/transcript-extractor.js +437 -0
- package/dist/core/transcript-extractor.js.map +1 -0
- package/dist/core/transcript-source.d.ts +21 -0
- package/dist/core/transcript-source.d.ts.map +1 -0
- package/dist/core/transcript-source.js +142 -0
- package/dist/core/transcript-source.js.map +1 -0
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/verifier.js +1 -1
- package/dist/core/verifier.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +10 -0
- package/dist/db.js.map +1 -1
- package/dist/skills-manifest.json +11 -11
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +229 -65
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/server.d.ts +7 -0
- package/dist/transports/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +145 -50
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +1 -1
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +3 -2
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js.map +1 -1
- package/package.json +2 -11
- package/scripts/hooks/post-commit.js +8 -2
- package/scripts/hooks/pre-compact.js +9 -0
- package/scripts/hooks/session-start.js +1 -1
- package/skills/agentic-orchestration/SKILL.md +1 -1
- package/skills/memesh/SKILL.md +2 -0
package/dist/core/doctor.js
CHANGED
|
@@ -3,14 +3,15 @@ import path from 'path';
|
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
5
|
import { pathToFileURL } from 'url';
|
|
6
|
-
import { detectCapabilities, getConfigPath } from './config.js';
|
|
7
|
-
import { embedText
|
|
6
|
+
import { detectCapabilities, getConfigPath, isTranscriptMiningEnabled } from './config.js';
|
|
7
|
+
import { embedText } from './embedder.js';
|
|
8
8
|
import { probeProvider } from './llm-validator.js';
|
|
9
9
|
import { openDatabase, closeDatabase, getPendingReindexInfo, isDatabaseOpen } from '../db.js';
|
|
10
10
|
import { getUpdateCheck } from './version-check.js';
|
|
11
11
|
import { getCurrentInstallChannel, getInstallChannelSupport } from './install-channel.js';
|
|
12
12
|
import { getInstallRecord } from './install-id.js';
|
|
13
|
-
import { getDbPath, memeshDir } from './paths.js';
|
|
13
|
+
import { getDbPath, memeshDir, getProjectName } from './paths.js';
|
|
14
|
+
import { lastTranscriptMineAt } from './transcript-source.js';
|
|
14
15
|
import { UNSPACED_SCRIPT_GLOB_RUN3 } from '../storage/fts-index.js';
|
|
15
16
|
const EMBEDDING_PROBE_TIMEOUT_MS = 15000;
|
|
16
17
|
const EXPECTED_HOOK_TYPES = ['PreToolUse', 'SessionStart', 'PostToolUse', 'Stop', 'PreCompact'];
|
|
@@ -53,7 +54,7 @@ function inspectLocaleReadmeParity(packageRoot, existsSyncImpl, readFileSyncImpl
|
|
|
53
54
|
englishCount = countH2Headings(readFileSyncImpl(englishPath, 'utf8'));
|
|
54
55
|
}
|
|
55
56
|
catch (err) {
|
|
56
|
-
return createCheck('readme_locale_parity', 'README locale parity', 'warn', `Could not read README.md: ${err instanceof Error ? err.message : String(err)}
|
|
57
|
+
return createCheck('readme_locale_parity', 'README locale parity', 'warn', `Could not read README.md: ${err instanceof Error ? err.message : String(err)}`, undefined, { code: 'readme-parity.unreadable', params: { detail: err instanceof Error ? err.message : String(err) } });
|
|
57
58
|
}
|
|
58
59
|
const missing = [];
|
|
59
60
|
const drift = [];
|
|
@@ -86,13 +87,13 @@ function inspectLocaleReadmeParity(packageRoot, existsSyncImpl, readFileSyncImpl
|
|
|
86
87
|
.join(', ');
|
|
87
88
|
parts.push(`H2 count drift (English=${englishCount}): ${driftDetail}`);
|
|
88
89
|
}
|
|
89
|
-
return createCheck('readme_locale_parity', 'README locale parity', 'warn', parts.join('; '), `Re-sync the listed READMEs against README.md so section structure matches (±${LOCALE_H2_TOLERANCE} H2 tolerated to absorb translation collapse)
|
|
90
|
+
return createCheck('readme_locale_parity', 'README locale parity', 'warn', parts.join('; '), `Re-sync the listed READMEs against README.md so section structure matches (±${LOCALE_H2_TOLERANCE} H2 tolerated to absorb translation collapse).`, { code: 'readme-parity.drift', params: { detail: parts.join('; '), tolerance: LOCALE_H2_TOLERANCE } });
|
|
90
91
|
}
|
|
91
92
|
function resolveDatabasePath() {
|
|
92
93
|
return getDbPath();
|
|
93
94
|
}
|
|
94
|
-
function createCheck(id, label, status, summary, fix) {
|
|
95
|
-
return { id, label, status, summary, fix };
|
|
95
|
+
function createCheck(id, label, status, summary, fix, i18n) {
|
|
96
|
+
return { id, label, status, summary, fix, code: i18n?.code, params: i18n?.params };
|
|
96
97
|
}
|
|
97
98
|
function createInfo(id, label, summary, fix) {
|
|
98
99
|
return { id, label, status: 'pass', summary, fix, informational: true };
|
|
@@ -120,22 +121,22 @@ function inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl)
|
|
|
120
121
|
}
|
|
121
122
|
const parsed = parseJsonFile(configPath, readFileSyncImpl);
|
|
122
123
|
if (!parsed.ok) {
|
|
123
|
-
return createCheck('config', 'Config', 'fail', `Config file is invalid JSON at ${configPath}.`, `Fix or remove ${configPath}, then run \`memesh config list\` to confirm it loads cleanly
|
|
124
|
+
return createCheck('config', 'Config', 'fail', `Config file is invalid JSON at ${configPath}.`, `Fix or remove ${configPath}, then run \`memesh config list\` to confirm it loads cleanly.`, { code: 'config.invalid-json', params: { path: configPath } });
|
|
124
125
|
}
|
|
125
126
|
return createCheck('config', 'Config', 'pass', `Config file is readable at ${configPath}.`);
|
|
126
127
|
}
|
|
127
128
|
function inspectMcpConfig(packageRoot, existsSyncImpl, readFileSyncImpl) {
|
|
128
129
|
const mcpPath = path.join(packageRoot, '.mcp.json');
|
|
129
130
|
if (!existsSyncImpl(mcpPath)) {
|
|
130
|
-
return createCheck('mcp-config', 'MCP config', 'fail', '.mcp.json is missing.', 'Restore `.mcp.json` from the package or reinstall MeMesh.');
|
|
131
|
+
return createCheck('mcp-config', 'MCP config', 'fail', '.mcp.json is missing.', 'Restore `.mcp.json` from the package or reinstall MeMesh.', { code: 'mcp-config.missing' });
|
|
131
132
|
}
|
|
132
133
|
const parsed = parseJsonFile(mcpPath, readFileSyncImpl);
|
|
133
134
|
if (!parsed.ok) {
|
|
134
|
-
return createCheck('mcp-config', 'MCP config', 'fail', '.mcp.json is not valid JSON.', `Fix ${mcpPath} so Claude Code can read the MCP server definition
|
|
135
|
+
return createCheck('mcp-config', 'MCP config', 'fail', '.mcp.json is not valid JSON.', `Fix ${mcpPath} so Claude Code can read the MCP server definition.`, { code: 'mcp-config.invalid-json', params: { path: mcpPath } });
|
|
135
136
|
}
|
|
136
137
|
const server = parsed.value.mcpServers?.memesh;
|
|
137
138
|
if (!server || typeof server.command !== 'string') {
|
|
138
|
-
return createCheck('mcp-config', 'MCP config', 'fail', '.mcp.json does not define a usable `memesh` MCP server entry.', 'Reinstall MeMesh or restore the `mcpServers.memesh` entry in `.mcp.json`.');
|
|
139
|
+
return createCheck('mcp-config', 'MCP config', 'fail', '.mcp.json does not define a usable `memesh` MCP server entry.', 'Reinstall MeMesh or restore the `mcpServers.memesh` entry in `.mcp.json`.', { code: 'mcp-config.no-entry' });
|
|
139
140
|
}
|
|
140
141
|
return createCheck('mcp-config', 'MCP config', 'pass', '.mcp.json is present and defines the memesh MCP server.');
|
|
141
142
|
}
|
|
@@ -160,32 +161,32 @@ function inspectHooksConfig(packageRoot, platform, existsSyncImpl, readFileSyncI
|
|
|
160
161
|
const hooksPath = path.join(packageRoot, 'hooks', 'hooks.json');
|
|
161
162
|
if (!existsSyncImpl(hooksPath)) {
|
|
162
163
|
return [
|
|
163
|
-
createCheck('hooks-config', 'Hooks config', 'fail', 'hooks/hooks.json is missing.', 'Restore `hooks/hooks.json` from the package or reinstall MeMesh.'),
|
|
164
|
+
createCheck('hooks-config', 'Hooks config', 'fail', 'hooks/hooks.json is missing.', 'Restore `hooks/hooks.json` from the package or reinstall MeMesh.', { code: 'hooks-config.missing' }),
|
|
164
165
|
];
|
|
165
166
|
}
|
|
166
167
|
const parsed = parseJsonFile(hooksPath, readFileSyncImpl);
|
|
167
168
|
if (!parsed.ok) {
|
|
168
169
|
return [
|
|
169
|
-
createCheck('hooks-config', 'Hooks config', 'fail', 'hooks/hooks.json is not valid JSON.', `Fix ${hooksPath} so Claude Code can load the hook definitions
|
|
170
|
+
createCheck('hooks-config', 'Hooks config', 'fail', 'hooks/hooks.json is not valid JSON.', `Fix ${hooksPath} so Claude Code can load the hook definitions.`, { code: 'hooks-config.invalid-json', params: { path: hooksPath } }),
|
|
170
171
|
];
|
|
171
172
|
}
|
|
172
173
|
const hookTypes = Object.keys(parsed.value.hooks ?? {});
|
|
173
174
|
const missingTypes = EXPECTED_HOOK_TYPES.filter((type) => !hookTypes.includes(type));
|
|
174
175
|
const configCheck = missingTypes.length > 0
|
|
175
|
-
? createCheck('hooks-config', 'Hooks config', 'fail', `hooks/hooks.json is missing expected hook types: ${missingTypes.join(', ')}.`, 'Restore the shipped hook configuration or reinstall MeMesh.')
|
|
176
|
+
? createCheck('hooks-config', 'Hooks config', 'fail', `hooks/hooks.json is missing expected hook types: ${missingTypes.join(', ')}.`, 'Restore the shipped hook configuration or reinstall MeMesh.', { code: 'hooks-config.missing-types', params: { types: missingTypes.join(', ') } })
|
|
176
177
|
: createCheck('hooks-config', 'Hooks config', 'pass', `hooks/hooks.json is present with ${hookTypes.length} hook types configured.`);
|
|
177
178
|
const scriptPaths = extractHookScriptPaths(parsed.value, packageRoot);
|
|
178
179
|
if (scriptPaths.length === 0) {
|
|
179
180
|
return [
|
|
180
181
|
configCheck,
|
|
181
|
-
createCheck('hook-scripts', 'Hook scripts', 'fail', 'hooks/hooks.json parsed, but yields zero hook script commands — hooks can never fire.', 'Restore the shipped hook configuration or reinstall MeMesh.'),
|
|
182
|
+
createCheck('hook-scripts', 'Hook scripts', 'fail', 'hooks/hooks.json parsed, but yields zero hook script commands — hooks can never fire.', 'Restore the shipped hook configuration or reinstall MeMesh.', { code: 'hook-scripts.none' }),
|
|
182
183
|
];
|
|
183
184
|
}
|
|
184
185
|
const missingScripts = scriptPaths.filter((scriptPath) => !existsSyncImpl(scriptPath));
|
|
185
186
|
if (missingScripts.length > 0) {
|
|
186
187
|
return [
|
|
187
188
|
configCheck,
|
|
188
|
-
createCheck('hook-scripts', 'Hook scripts', 'fail', `Missing hook scripts: ${missingScripts.map((entry) => path.relative(packageRoot, entry)).join(', ')}.`, 'Restore the missing files from the package or reinstall MeMesh.'),
|
|
189
|
+
createCheck('hook-scripts', 'Hook scripts', 'fail', `Missing hook scripts: ${missingScripts.map((entry) => path.relative(packageRoot, entry)).join(', ')}.`, 'Restore the missing files from the package or reinstall MeMesh.', { code: 'hook-scripts.missing', params: { files: missingScripts.map((entry) => path.relative(packageRoot, entry)).join(', ') } }),
|
|
189
190
|
];
|
|
190
191
|
}
|
|
191
192
|
if (platform !== 'win32') {
|
|
@@ -196,7 +197,7 @@ function inspectHooksConfig(packageRoot, platform, existsSyncImpl, readFileSyncI
|
|
|
196
197
|
if (nonExecutable.length > 0) {
|
|
197
198
|
return [
|
|
198
199
|
configCheck,
|
|
199
|
-
createCheck('hook-scripts', 'Hook scripts', 'fail', `Hook scripts are not executable: ${nonExecutable.map((entry) => path.relative(packageRoot, entry)).join(', ')}.`, 'Run `npm run build` from the repo checkout or `chmod +x scripts/hooks/*.js` for a local repair.'),
|
|
200
|
+
createCheck('hook-scripts', 'Hook scripts', 'fail', `Hook scripts are not executable: ${nonExecutable.map((entry) => path.relative(packageRoot, entry)).join(', ')}.`, 'Run `npm run build` from the repo checkout or `chmod +x scripts/hooks/*.js` for a local repair.', { code: 'hook-scripts.not-executable', params: { files: nonExecutable.map((entry) => path.relative(packageRoot, entry)).join(', ') } }),
|
|
200
201
|
];
|
|
201
202
|
}
|
|
202
203
|
}
|
|
@@ -214,22 +215,22 @@ function inspectHookWiring(existsSyncImpl, readFileSyncImpl, memeshDir, packageR
|
|
|
214
215
|
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'pass', 'Wired via Claude Code plugin runtime (.claude-plugin/plugin.json present). The install-hooks marker is not used on this install path.');
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
|
-
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'warn', '
|
|
218
|
+
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'warn', 'memesh is not connected to Claude Code yet, so nothing gets remembered automatically from your sessions.', 'Run `memesh install-hooks` once to connect it, then `memesh doctor` to confirm.', { code: 'hook-wiring.no-marker' });
|
|
218
219
|
}
|
|
219
220
|
const parsed = parseJsonFile(markerPath, readFileSyncImpl);
|
|
220
221
|
if (!parsed.ok) {
|
|
221
|
-
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'warn', `install-hooks marker at ${markerPath} is unreadable.`, 'Re-run `memesh install-hooks` to refresh the marker.');
|
|
222
|
+
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'warn', `install-hooks marker at ${markerPath} is unreadable.`, 'Re-run `memesh install-hooks` to refresh the marker.', { code: 'hook-wiring.marker-unreadable', params: { path: markerPath } });
|
|
222
223
|
}
|
|
223
224
|
const marker = parsed.value;
|
|
224
225
|
if (typeof marker.settings_path !== 'string') {
|
|
225
|
-
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'warn', 'install-hooks marker is malformed (missing settings_path).', 'Re-run `memesh install-hooks`.');
|
|
226
|
+
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'warn', 'install-hooks marker is malformed (missing settings_path).', 'Re-run `memesh install-hooks`.', { code: 'hook-wiring.marker-malformed' });
|
|
226
227
|
}
|
|
227
228
|
if (!existsSyncImpl(marker.settings_path)) {
|
|
228
|
-
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `Marker recorded settings at ${marker.settings_path} but the file no longer exists. Hooks are not wired.`, 'Re-run `memesh install-hooks`.');
|
|
229
|
+
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `Marker recorded settings at ${marker.settings_path} but the file no longer exists. Hooks are not wired.`, 'Re-run `memesh install-hooks`.', { code: 'hook-wiring.settings-missing', params: { path: String(marker.settings_path) } });
|
|
229
230
|
}
|
|
230
231
|
const settingsParsed = parseJsonFile(marker.settings_path, readFileSyncImpl);
|
|
231
232
|
if (!settingsParsed.ok) {
|
|
232
|
-
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `${marker.settings_path} is no longer valid JSON.`, 'Restore from your ~/.claude backups or re-create with `memesh install-hooks`.');
|
|
233
|
+
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `${marker.settings_path} is no longer valid JSON.`, 'Restore from your ~/.claude backups or re-create with `memesh install-hooks`.', { code: 'hook-wiring.settings-invalid', params: { path: String(marker.settings_path) } });
|
|
233
234
|
}
|
|
234
235
|
const hooks = settingsParsed.value.hooks;
|
|
235
236
|
let hasMemeshHook = false;
|
|
@@ -251,10 +252,10 @@ function inspectHookWiring(existsSyncImpl, readFileSyncImpl, memeshDir, packageR
|
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
254
|
if (!hasMemeshHook) {
|
|
254
|
-
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `Marker recorded a memesh install at ${marker.settings_path}, but no _memesh:true hook entries are present anymore. Settings drifted (manual edit?) or memesh was uninstalled out-of-band.`, 'Re-run `memesh install-hooks` to re-wire.');
|
|
255
|
+
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `Marker recorded a memesh install at ${marker.settings_path}, but no _memesh:true hook entries are present anymore. Settings drifted (manual edit?) or memesh was uninstalled out-of-band.`, 'Re-run `memesh install-hooks` to re-wire.', { code: 'hook-wiring.entries-removed', params: { path: String(marker.settings_path) } });
|
|
255
256
|
}
|
|
256
257
|
if (typeof marker.plugin_root === 'string' && !existsSyncImpl(marker.plugin_root)) {
|
|
257
|
-
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `Hook commands point at ${marker.plugin_root}, which no longer exists (likely after an npm-global path change).`, 'Re-run `memesh install-hooks` to refresh paths.');
|
|
258
|
+
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'fail', `Hook commands point at ${marker.plugin_root}, which no longer exists (likely after an npm-global path change).`, 'Re-run `memesh install-hooks` to refresh paths.', { code: 'hook-wiring.root-moved', params: { path: String(marker.plugin_root) } });
|
|
258
259
|
}
|
|
259
260
|
return createCheck('hook-wiring', 'Hooks wired into Claude Code', 'pass', `Wired in ${marker.settings_path} (scope: ${marker.scope ?? 'user'}, version: ${marker.version ?? 'unknown'}).`);
|
|
260
261
|
}
|
|
@@ -277,12 +278,12 @@ function inspectHookActivity(openDatabaseImpl, closeDatabaseImpl, existsSyncImpl
|
|
|
277
278
|
}
|
|
278
279
|
catch { }
|
|
279
280
|
}
|
|
280
|
-
return createCheck('hook-activity', 'Hook activity (last 24h)', 'warn', '
|
|
281
|
+
return createCheck('hook-activity', 'Hook activity (last 24h)', 'warn', 'memesh has not saved anything automatically in the last 24 hours. Everything is set up — it just has not seen a work session to remember yet. If you HAVE been working all day, Claude Code may need a restart to pick the connection up.', 'Do a normal Claude Code work session (or make a git commit), then check again with `memesh doctor`.', { code: 'hook-activity.quiet' });
|
|
281
282
|
}
|
|
282
283
|
return createCheck('hook-activity', 'Hook activity (last 24h)', 'pass', `${count} memesh-attributed entit${count === 1 ? 'y' : 'ies'} captured in the past 24h — auto-capture loop is alive.`);
|
|
283
284
|
}
|
|
284
285
|
catch (err) {
|
|
285
|
-
return createCheck('hook-activity', 'Hook activity (last 24h)', 'warn', `Could not query the database: ${err instanceof Error ? err.message : String(err)}
|
|
286
|
+
return createCheck('hook-activity', 'Hook activity (last 24h)', 'warn', `Could not query the database: ${err instanceof Error ? err.message : String(err)}`, undefined, { code: 'hook-activity.query-failed', params: { detail: err instanceof Error ? err.message : String(err) } });
|
|
286
287
|
}
|
|
287
288
|
finally {
|
|
288
289
|
try {
|
|
@@ -356,7 +357,7 @@ export function inspectNodeRuntime(packageRoot, existsSyncImpl, readFileSyncImpl
|
|
|
356
357
|
}
|
|
357
358
|
if (!ok) {
|
|
358
359
|
return createCheck('node-runtime', 'Node runtime', 'fail', `${facts} This package requires Node ${declared}, so this runtime is BELOW the ` +
|
|
359
|
-
`supported floor. Native modules and hooks may fail in ways that look unrelated.`, `Upgrade Node to ${declared.replace(/^>=\s*/, '')} or newer, then run \`memesh doctor\` again
|
|
360
|
+
`supported floor. Native modules and hooks may fail in ways that look unrelated.`, `Upgrade Node to ${declared.replace(/^>=\s*/, '')} or newer, then run \`memesh doctor\` again.`, { code: 'node-runtime.too-old', params: { detail: facts, required: declared.replace(/^>=\s*/, '') } });
|
|
360
361
|
}
|
|
361
362
|
return createCheck('node-runtime', 'Node runtime', 'pass', `${facts} Meets the required range ${declared}.`);
|
|
362
363
|
}
|
|
@@ -378,14 +379,14 @@ function inspectNativeBinding(packageRoot, _existsSyncImpl, probeImpl = defaultN
|
|
|
378
379
|
const isMissingPackage = /MODULE_NOT_FOUND|Cannot find module/i.test(result.message);
|
|
379
380
|
if (isMissingPackage) {
|
|
380
381
|
return createCheck('native-binding', 'Native SQLite binding', 'fail', 'better-sqlite3 is not installed (Node could not resolve the module from any '
|
|
381
|
-
+ 'parent node_modules). Memesh hooks and database operations will not work.', `Run: npm install (in the directory that depends on @pcircle/memesh)
|
|
382
|
+
+ 'parent node_modules). Memesh hooks and database operations will not work.', `Run: npm install (in the directory that depends on @pcircle/memesh)`, { code: 'native-binding.not-installed' });
|
|
382
383
|
}
|
|
383
384
|
if (isMissingBinding) {
|
|
384
385
|
return createCheck('native-binding', 'Native SQLite binding', 'fail', 'better-sqlite3 is installed but the native binding (.node file) is missing. '
|
|
385
386
|
+ 'Hooks will silently skip-and-exit, and auto-capture will NOT write any entities. '
|
|
386
|
-
+ 'This is the plugin-marketplace silent-dropout class of bug.', `Run: cd "${packageRoot}" && npm rebuild better-sqlite3 (or "npm install --omit=dev" for a clean reinstall)
|
|
387
|
+
+ 'This is the plugin-marketplace silent-dropout class of bug.', `Run: cd "${packageRoot}" && npm rebuild better-sqlite3 (or "npm install --omit=dev" for a clean reinstall)`, { code: 'native-binding.missing', params: { root: packageRoot } });
|
|
387
388
|
}
|
|
388
|
-
return createCheck('native-binding', 'Native SQLite binding', 'fail', `better-sqlite3 failed to load: ${result.message}`, `Run: cd "${packageRoot}" && npm rebuild better-sqlite3
|
|
389
|
+
return createCheck('native-binding', 'Native SQLite binding', 'fail', `better-sqlite3 failed to load: ${result.message}`, `Run: cd "${packageRoot}" && npm rebuild better-sqlite3`, { code: 'native-binding.load-failed', params: { detail: result.message, root: packageRoot } });
|
|
389
390
|
}
|
|
390
391
|
function inspectShellCli(installChannel, packageRoot, resolveShellMemeshImpl) {
|
|
391
392
|
const shellPath = resolveShellMemeshImpl();
|
|
@@ -401,7 +402,7 @@ function inspectShellCli(installChannel, packageRoot, resolveShellMemeshImpl) {
|
|
|
401
402
|
}
|
|
402
403
|
if (installChannel === 'plugin-marketplace') {
|
|
403
404
|
return createCheck('shell-cli', 'Shell CLI on PATH', 'warn', 'Plugin is installed but `memesh` is not on the shell PATH. Typing `memesh` in a regular terminal will report `command not found`. '
|
|
404
|
-
+ 'Claude Code MCP / hooks / `/memesh` skill still work — this only affects standalone shell usage and other MCP clients (Cursor, Cline, etc.).', 'Run `npm install -g @pcircle/memesh` to add the shell CLI. Both paths coexist; they share the same `~/.memesh/knowledge-graph.db`.');
|
|
405
|
+
+ 'Claude Code MCP / hooks / `/memesh` skill still work — this only affects standalone shell usage and other MCP clients (Cursor, Cline, etc.).', 'Run `npm install -g @pcircle/memesh` to add the shell CLI. Both paths coexist; they share the same `~/.memesh/knowledge-graph.db`.', { code: 'shell-cli.not-on-path' });
|
|
405
406
|
}
|
|
406
407
|
return createCheck('shell-cli', 'Shell CLI on PATH', 'pass', shellPath
|
|
407
408
|
? `\`memesh\` resolves to ${shellPath}.`
|
|
@@ -410,14 +411,14 @@ function inspectShellCli(installChannel, packageRoot, resolveShellMemeshImpl) {
|
|
|
410
411
|
function inspectDashboardArtifact(packageRoot, existsSyncImpl) {
|
|
411
412
|
const dashboardPath = path.join(packageRoot, 'dashboard', 'dist', 'index.html');
|
|
412
413
|
if (!existsSyncImpl(dashboardPath)) {
|
|
413
|
-
return createCheck('dashboard', 'Dashboard artifact', 'fail', 'dashboard/dist/index.html is missing.', 'Build the dashboard with `cd dashboard && npm install && npm run build`, then run `npm run build` at the repo root if needed.');
|
|
414
|
+
return createCheck('dashboard', 'Dashboard artifact', 'fail', 'dashboard/dist/index.html is missing.', 'Build the dashboard with `cd dashboard && npm install && npm run build`, then run `npm run build` at the repo root if needed.', { code: 'dashboard.missing' });
|
|
414
415
|
}
|
|
415
416
|
return createCheck('dashboard', 'Dashboard artifact', 'pass', 'dashboard/dist/index.html is present.');
|
|
416
417
|
}
|
|
417
418
|
async function inspectUpdateStatus(packageVersion, getUpdateCheckImpl, installSupport) {
|
|
418
419
|
const update = await getUpdateCheckImpl(packageVersion, { preferFresh: false });
|
|
419
420
|
if (!update) {
|
|
420
|
-
return createCheck('update-status', 'Update status', 'warn', '
|
|
421
|
+
return createCheck('update-status', 'Update status', 'warn', 'memesh has not been able to check for newer versions yet, so it cannot tell you whether an update exists.', 'Run `memesh status` once while connected to the internet — that stores the answer and this notice goes away.', { code: 'update-status.no-cache' });
|
|
421
422
|
}
|
|
422
423
|
if (update.currentVersionDeprecated && update.deprecationMessage) {
|
|
423
424
|
const target = update.latestVersion && update.latestVersion !== packageVersion
|
|
@@ -439,10 +440,10 @@ async function inspectUpdateStatus(packageVersion, getUpdateCheckImpl, installSu
|
|
|
439
440
|
else {
|
|
440
441
|
fix = `Upgrade via your install method (see \`memesh status\`).`;
|
|
441
442
|
}
|
|
442
|
-
return createCheck('update-status', 'Update status', 'fail', `Installed version ${packageVersion} is DEPRECATED by maintainers: ${update.deprecationMessage}`, fix);
|
|
443
|
+
return createCheck('update-status', 'Update status', 'fail', `Installed version ${packageVersion} is DEPRECATED by maintainers: ${update.deprecationMessage}`, fix, { code: 'update-status.deprecated', params: { version: packageVersion, detail: update.deprecationMessage ?? '' } });
|
|
443
444
|
}
|
|
444
445
|
if (update.freshness === 'unavailable') {
|
|
445
|
-
return createCheck('update-status', 'Update status', 'warn', '
|
|
446
|
+
return createCheck('update-status', 'Update status', 'warn', 'memesh has not been able to check for newer versions yet, so it cannot tell you whether an update exists.', 'Run `memesh status` once while connected to the internet — that stores the answer and this notice goes away.', { code: 'update-status.no-cache' });
|
|
446
447
|
}
|
|
447
448
|
if (update.checkSucceeded && update.lastError) {
|
|
448
449
|
const hasUpdate = Boolean(update.updateAvailable && update.latestVersion);
|
|
@@ -465,11 +466,14 @@ async function inspectUpdateStatus(packageVersion, getUpdateCheckImpl, installSu
|
|
|
465
466
|
upgradeHint = '';
|
|
466
467
|
}
|
|
467
468
|
const fix = `Run \`memesh status\` while online to retry the deprecation lookup${upgradeHint}.`;
|
|
468
|
-
return createCheck('update-status', 'Update status', 'warn', summary, fix
|
|
469
|
+
return createCheck('update-status', 'Update status', 'warn', summary, fix, {
|
|
470
|
+
code: 'update-status.deprecation-unknown',
|
|
471
|
+
params: { version: packageVersion, detail: update.lastError ?? '' },
|
|
472
|
+
});
|
|
469
473
|
}
|
|
470
474
|
if (update.updateAvailable && update.latestVersion) {
|
|
471
475
|
if (packageVersion < update.latestVersion) {
|
|
472
|
-
return createCheck('update-status', 'Update status', 'warn', `Update available: ${update.latestVersion} (current: ${packageVersion})`, `Run 'memesh update' to upgrade
|
|
476
|
+
return createCheck('update-status', 'Update status', 'warn', `Update available: ${update.latestVersion} (current: ${packageVersion})`, `Run 'memesh update' to upgrade`, { code: 'update-status.update-available', params: { latest: update.latestVersion, current: packageVersion } });
|
|
473
477
|
}
|
|
474
478
|
else {
|
|
475
479
|
return createCheck('update-status', 'Update status', 'pass', `Running pre-release version (${packageVersion}), npm latest is ${update.latestVersion}`);
|
|
@@ -477,35 +481,37 @@ async function inspectUpdateStatus(packageVersion, getUpdateCheckImpl, installSu
|
|
|
477
481
|
}
|
|
478
482
|
return createCheck('update-status', 'Update status', update.freshness === 'stale' ? 'warn' : 'pass', `Version ${packageVersion} is current${update.freshness === 'stale' ? ', but cached update data is stale.' : '.'}`, update.freshness === 'stale'
|
|
479
483
|
? 'Run `memesh status` while online to refresh cached update metadata.'
|
|
484
|
+
: undefined, update.freshness === 'stale'
|
|
485
|
+
? { code: 'update-status.stale', params: { version: packageVersion } }
|
|
480
486
|
: undefined);
|
|
481
487
|
}
|
|
482
488
|
async function inspectHttpProbe(httpBaseUrl, fetchImpl) {
|
|
483
489
|
try {
|
|
484
490
|
const response = await fetchImpl(`${httpBaseUrl.replace(/\/$/, '')}/v1/health`);
|
|
485
491
|
if (!response.ok) {
|
|
486
|
-
return createCheck('http-probe', 'HTTP probe', 'warn', `HTTP server responded with ${response.status} at ${httpBaseUrl}.`, 'Run `memesh serve` and check the logs, then retry `memesh doctor --probe-http`.');
|
|
492
|
+
return createCheck('http-probe', 'HTTP probe', 'warn', `HTTP server responded with ${response.status} at ${httpBaseUrl}.`, 'Run `memesh serve` and check the logs, then retry `memesh doctor --probe-http`.', { code: 'http-probe.bad-status', params: { status: response.status, url: httpBaseUrl } });
|
|
487
493
|
}
|
|
488
494
|
return createCheck('http-probe', 'HTTP probe', 'pass', `HTTP server is reachable at ${httpBaseUrl}.`);
|
|
489
495
|
}
|
|
490
496
|
catch {
|
|
491
|
-
return createCheck('http-probe', 'HTTP probe', 'warn', `No running HTTP server detected at ${httpBaseUrl}.`, 'Start the local server with `memesh serve` if you want dashboard and HTTP API verification.');
|
|
497
|
+
return createCheck('http-probe', 'HTTP probe', 'warn', `No running HTTP server detected at ${httpBaseUrl}.`, 'Start the local server with `memesh serve` if you want dashboard and HTTP API verification.', { code: 'http-probe.no-server', params: { url: httpBaseUrl } });
|
|
492
498
|
}
|
|
493
499
|
}
|
|
494
500
|
function verifySkillsManifest(packageRoot, existsSyncImpl, readFileSyncImpl) {
|
|
495
501
|
const manifestPath = path.join(packageRoot, 'dist', 'skills-manifest.json');
|
|
496
502
|
if (!existsSyncImpl(manifestPath)) {
|
|
497
|
-
return createCheck('skills-manifest', 'Skills + hooks integrity', 'warn', 'No skills-manifest.json found. This is normal for source checkouts — packaged installs ship the manifest.', 'Run `npm run build` to regenerate, or reinstall via `npm install -g @pcircle/memesh`.');
|
|
503
|
+
return createCheck('skills-manifest', 'Skills + hooks integrity', 'warn', 'No skills-manifest.json found. This is normal for source checkouts — packaged installs ship the manifest.', 'Run `npm run build` to regenerate, or reinstall via `npm install -g @pcircle/memesh`.', { code: 'skills-manifest.missing-dev' });
|
|
498
504
|
}
|
|
499
505
|
let manifest;
|
|
500
506
|
try {
|
|
501
507
|
manifest = JSON.parse(readFileSyncImpl(manifestPath, 'utf8'));
|
|
502
508
|
}
|
|
503
509
|
catch (err) {
|
|
504
|
-
return createCheck('skills-manifest', 'Skills + hooks integrity', 'fail', `skills-manifest.json is unreadable (${err instanceof Error ? err.message : 'parse error'}).`, 'Reinstall the package: `npm install -g @pcircle/memesh`. If the problem persists open an issue.');
|
|
510
|
+
return createCheck('skills-manifest', 'Skills + hooks integrity', 'fail', `skills-manifest.json is unreadable (${err instanceof Error ? err.message : 'parse error'}).`, 'Reinstall the package: `npm install -g @pcircle/memesh`. If the problem persists open an issue.', { code: 'skills-manifest.unreadable', params: { detail: err instanceof Error ? err.message : 'parse error' } });
|
|
505
511
|
}
|
|
506
512
|
const entries = manifest.entries ?? [];
|
|
507
513
|
if (entries.length === 0) {
|
|
508
|
-
return createCheck('skills-manifest', 'Skills + hooks integrity', 'fail', 'skills-manifest.json contains zero entries.', 'Reinstall the package: `npm install -g @pcircle/memesh`.');
|
|
514
|
+
return createCheck('skills-manifest', 'Skills + hooks integrity', 'fail', 'skills-manifest.json contains zero entries.', 'Reinstall the package: `npm install -g @pcircle/memesh`.', { code: 'skills-manifest.empty' });
|
|
509
515
|
}
|
|
510
516
|
const mismatches = [];
|
|
511
517
|
const missing = [];
|
|
@@ -534,7 +540,7 @@ function verifySkillsManifest(packageRoot, existsSyncImpl, readFileSyncImpl) {
|
|
|
534
540
|
missing.length > 0 ? `${missing.length} missing: ${missing.slice(0, 3).join(', ')}${missing.length > 3 ? ` (+${missing.length - 3} more)` : ''}` : null,
|
|
535
541
|
mismatches.length > 0 ? `${mismatches.length} tampered: ${mismatches.slice(0, 3).join(', ')}${mismatches.length > 3 ? ` (+${mismatches.length - 3} more)` : ''}` : null,
|
|
536
542
|
].filter(Boolean).join('; ');
|
|
537
|
-
return createCheck('skills-manifest', 'Skills + hooks integrity', 'fail', `Manifest verification failed: ${detail}.`, 'Reinstall the package: `npm install -g @pcircle/memesh`. If the problem reproduces on a fresh install, open a security issue at https://github.com/PCIRCLE-AI/memesh-llm-memory/security.');
|
|
543
|
+
return createCheck('skills-manifest', 'Skills + hooks integrity', 'fail', `Manifest verification failed: ${detail}.`, 'Reinstall the package: `npm install -g @pcircle/memesh`. If the problem reproduces on a fresh install, open a security issue at https://github.com/PCIRCLE-AI/memesh-llm-memory/security.', { code: 'skills-manifest.verify-failed', params: { detail } });
|
|
538
544
|
}
|
|
539
545
|
async function inspectConfigParse(getConfigPathImpl, existsSyncImpl, readFileSyncImpl) {
|
|
540
546
|
const configPath = getConfigPathImpl();
|
|
@@ -545,13 +551,13 @@ async function inspectConfigParse(getConfigPathImpl, existsSyncImpl, readFileSyn
|
|
|
545
551
|
const raw = readFileSyncImpl(configPath, 'utf8');
|
|
546
552
|
const parsed = JSON.parse(raw);
|
|
547
553
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
548
|
-
return createCheck('config_parse', 'Config parses', 'fail', `${configPath} parsed but is not a JSON object — every setting is being ignored.`, `Fix or remove ${configPath}, then re-run memesh doctor
|
|
554
|
+
return createCheck('config_parse', 'Config parses', 'fail', `${configPath} parsed but is not a JSON object — every setting is being ignored.`, `Fix or remove ${configPath}, then re-run memesh doctor.`, { code: 'config-parse.not-object', params: { path: configPath } });
|
|
549
555
|
}
|
|
550
556
|
return createCheck('config_parse', 'Config parses', 'pass', `${configPath} is valid JSON and its settings are in effect.`);
|
|
551
557
|
}
|
|
552
558
|
catch (err) {
|
|
553
559
|
const msg = err instanceof Error ? err.message : String(err);
|
|
554
|
-
return createCheck('config_parse', 'Config parses', 'fail', `${configPath} could not be read or parsed (${msg}). Every setting in it — LLM provider, fallbacks, embedder — is being silently ignored right now.`, `Fix the JSON or remove the file to fall back to defaults: mv ${configPath} ${configPath}.bak
|
|
560
|
+
return createCheck('config_parse', 'Config parses', 'fail', `${configPath} could not be read or parsed (${msg}). Every setting in it — LLM provider, fallbacks, embedder — is being silently ignored right now.`, `Fix the JSON or remove the file to fall back to defaults: mv ${configPath} ${configPath}.bak`, { code: 'config-parse.unreadable', params: { path: configPath, detail: msg } });
|
|
555
561
|
}
|
|
556
562
|
}
|
|
557
563
|
async function inspectEmbeddingProbe(capabilities, probeCapabilities, embedTextImpl) {
|
|
@@ -559,13 +565,7 @@ async function inspectEmbeddingProbe(capabilities, probeCapabilities, embedTextI
|
|
|
559
565
|
return createInfo('embeddings_probe', 'Embeddings work', 'No neural embedder configured — recall runs on FTS5 keyword search alone. That is a supported mode, not a fault.');
|
|
560
566
|
}
|
|
561
567
|
if (!probeCapabilities) {
|
|
562
|
-
|
|
563
|
-
if (!isLocal) {
|
|
564
|
-
return createInfo('embeddings_probe', 'Embeddings work', `NOT VERIFIED. Config names "${capabilities.embeddings}", but generating a test embedding is a network call (billed on hosted providers) so it was not made — a revoked key or an unreachable host would look identical to a healthy setup here.`, 'Run: memesh doctor --probe (generates one test embedding to confirm)');
|
|
565
|
-
}
|
|
566
|
-
if (!isOnnxModelCached()) {
|
|
567
|
-
return createInfo('embeddings_probe', 'Embeddings work', `NOT VERIFIED. Config names "onnx" but the model is not in the local cache yet, and probing would download ~90 MB — which a diagnostic command must not do on its own.`, 'Run: memesh doctor --probe (downloads the model once, then verifies)');
|
|
568
|
-
}
|
|
568
|
+
return createInfo('embeddings_probe', 'Embeddings work', `NOT VERIFIED. Config names "${capabilities.embeddings}", but generating a test embedding is a network call (billed on hosted providers) so it was not made — a revoked key or an unreachable host would look identical to a healthy setup here.`, 'Run: memesh doctor --probe (generates one test embedding to confirm)');
|
|
569
569
|
}
|
|
570
570
|
let timer;
|
|
571
571
|
try {
|
|
@@ -576,13 +576,13 @@ async function inspectEmbeddingProbe(capabilities, probeCapabilities, embedTextI
|
|
|
576
576
|
}),
|
|
577
577
|
]);
|
|
578
578
|
if (!vector || vector.length === 0) {
|
|
579
|
-
return createCheck('embeddings_probe', 'Embeddings work', 'warn', `Config selects "${capabilities.embeddings}" but generating a test embedding returned nothing. Semantic recall is degraded to FTS5-only; keyword search still works.`, 'Run: memesh doctor --probe for detail, or check network access to the embedding provider.');
|
|
579
|
+
return createCheck('embeddings_probe', 'Embeddings work', 'warn', `Config selects "${capabilities.embeddings}" but generating a test embedding returned nothing. Semantic recall is degraded to FTS5-only; keyword search still works.`, 'Run: memesh doctor --probe for detail, or check network access to the embedding provider.', { code: 'embeddings.empty', params: { provider: String(capabilities.embeddings) } });
|
|
580
580
|
}
|
|
581
581
|
return createCheck('embeddings_probe', 'Embeddings work', 'pass', `Generated a ${vector.length}-dim test embedding via "${capabilities.embeddings}".`);
|
|
582
582
|
}
|
|
583
583
|
catch (err) {
|
|
584
584
|
const msg = err instanceof Error ? err.message : String(err);
|
|
585
|
-
return createCheck('embeddings_probe', 'Embeddings work', 'warn', `Config selects "${capabilities.embeddings}" but the embedder threw (${msg}). Semantic recall is degraded to FTS5-only.`, 'Check the embedding provider is reachable, or
|
|
585
|
+
return createCheck('embeddings_probe', 'Embeddings work', 'warn', `Config selects "${capabilities.embeddings}" but the embedder threw (${msg}). Semantic recall is degraded to FTS5-only.`, 'Check the embedding provider is reachable (e.g. run `ollama serve`), or remove embedder config to use keyword-only search.', { code: 'embeddings.threw', params: { provider: String(capabilities.embeddings), detail: msg } });
|
|
586
586
|
}
|
|
587
587
|
finally {
|
|
588
588
|
clearTimeout(timer);
|
|
@@ -601,11 +601,11 @@ async function inspectLlmProbe(capabilities, probeCapabilities, probeProviderImp
|
|
|
601
601
|
if (result.valid) {
|
|
602
602
|
return createCheck('llm_probe', 'LLM reachable', 'pass', `${llm.provider} answered a live probe.`);
|
|
603
603
|
}
|
|
604
|
-
return createCheck('llm_probe', 'LLM reachable', 'fail', `${llm.provider} is configured but did not answer: ${result.error ?? 'unknown error'}. Every LLM-backed feature is silently doing nothing.`, 'Check the API key / host, then re-run: memesh doctor --probe');
|
|
604
|
+
return createCheck('llm_probe', 'LLM reachable', 'fail', `${llm.provider} is configured but did not answer: ${result.error ?? 'unknown error'}. Every LLM-backed feature is silently doing nothing.`, 'Check the API key / host, then re-run: memesh doctor --probe', { code: 'llm.unreachable', params: { provider: llm.provider, detail: result.error ?? 'unknown error' } });
|
|
605
605
|
}
|
|
606
606
|
catch (err) {
|
|
607
607
|
const msg = err instanceof Error ? err.message : String(err);
|
|
608
|
-
return createCheck('llm_probe', 'LLM reachable', 'fail', `${llm.provider} probe threw: ${msg}. Every LLM-backed feature is silently doing nothing.`, 'Check the API key / host, then re-run: memesh doctor --probe');
|
|
608
|
+
return createCheck('llm_probe', 'LLM reachable', 'fail', `${llm.provider} probe threw: ${msg}. Every LLM-backed feature is silently doing nothing.`, 'Check the API key / host, then re-run: memesh doctor --probe', { code: 'llm.threw', params: { provider: llm.provider, detail: msg } });
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
611
|
function summarizeOverallStatus(checks) {
|
|
@@ -627,7 +627,7 @@ export async function runDoctor(options) {
|
|
|
627
627
|
const installSupport = getInstallChannelSupportImpl(install);
|
|
628
628
|
checks.push(createCheck('install-channel', 'Install method', install === 'unknown' ? 'warn' : 'pass', `Install method detected: ${installSupport.label}.`, install === 'unknown'
|
|
629
629
|
? 'If this is a source checkout, run MeMesh from the repo root. If this is a packaged install, reinstall with `npm install -g @pcircle/memesh`.'
|
|
630
|
-
: undefined));
|
|
630
|
+
: undefined, install === 'unknown' ? { code: 'install-channel.unknown' } : undefined));
|
|
631
631
|
const databasePath = resolveDatabasePath();
|
|
632
632
|
const dbChecks = [];
|
|
633
633
|
try {
|
|
@@ -647,12 +647,12 @@ export async function runDoctor(options) {
|
|
|
647
647
|
dbChecks.push(createCheck('fts_segmentation', 'Keyword index segmentation', 'warn', `The keyword index holds ${unsegmented.c} unsegmented term(s), so some memories are only ` +
|
|
648
648
|
`findable by their exact full text. This happens when an older build wrote to a database ` +
|
|
649
649
|
`that a newer one had already migrated — the version marker only moves forward, so the ` +
|
|
650
|
-
`automatic rebuild cannot notice. Re-run doctor after the rebuild: this count should be 0.`, `Run 'memesh reindex --fts' to rebuild the keyword index
|
|
650
|
+
`automatic rebuild cannot notice. Re-run doctor after the rebuild: this count should be 0.`, `Run 'memesh reindex --fts' to rebuild the keyword index.`, { code: 'fts.unsegmented', params: { count: unsegmented.c } }));
|
|
651
651
|
}
|
|
652
652
|
}
|
|
653
653
|
const pendingReindex = getPendingReindexInfo();
|
|
654
654
|
if (pendingReindex) {
|
|
655
|
-
dbChecks.push(createCheck('vector_index', 'Vector Index', 'warn', `Search index needs rebuilding (embedding configuration changed)`, `Run 'memesh reindex' to fix. This will restore full search functionality
|
|
655
|
+
dbChecks.push(createCheck('vector_index', 'Vector Index', 'warn', `Search index needs rebuilding (embedding configuration changed)`, `Run 'memesh reindex' to fix. This will restore full search functionality.`, { code: 'vector-index.stale' }));
|
|
656
656
|
}
|
|
657
657
|
}
|
|
658
658
|
catch (err) {
|
|
@@ -708,7 +708,7 @@ export async function runDoctor(options) {
|
|
|
708
708
|
}
|
|
709
709
|
}
|
|
710
710
|
dbChecks.length = 0;
|
|
711
|
-
dbChecks.push(createCheck('database', 'Database', 'fail', diagnosis, fix));
|
|
711
|
+
dbChecks.push(createCheck('database', 'Database', 'fail', diagnosis, fix, { code: 'database.broken', params: { detail: diagnosis } }));
|
|
712
712
|
}
|
|
713
713
|
finally {
|
|
714
714
|
checks.push(...dbChecks);
|
|
@@ -730,6 +730,16 @@ export async function runDoctor(options) {
|
|
|
730
730
|
checks.push(verifySkillsManifest(packageRoot, existsSyncImpl, readFileSyncImpl));
|
|
731
731
|
const capabilities = detectCapabilitiesImpl();
|
|
732
732
|
checks.push(createInfo('capabilities', 'Capabilities (configured)', `Search level ${capabilities.searchLevel} (${capabilities.searchLevel === 1 ? 'Smart Mode' : 'Core'}); embeddings: ${capabilities.embeddings}; LLM: ${capabilities.llm ? `${capabilities.llm.provider} (${capabilities.llm.model ?? 'default'})` : 'not configured'}. Configured values only — see the probe rows below for what actually works.`));
|
|
733
|
+
if (!isTranscriptMiningEnabled()) {
|
|
734
|
+
checks.push(createInfo('transcript-mining', 'Scheduled transcript mining', 'Off (opt-in). memesh can mine this project\'s Claude Code session transcripts for decisions and lessons and STAGE them for your review. Turn it on with `memesh config set transcriptMining true`, then have a scheduler (cron/launchd) run `memesh dream run --from-transcripts --if-due` — it self-throttles and stages only, so nothing enters your graph without `dream accept`.'));
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
const last = lastTranscriptMineAt(getProjectName(process.cwd()));
|
|
738
|
+
const when = last === null
|
|
739
|
+
? 'not yet run for this project'
|
|
740
|
+
: `last mined ${((Date.now() - last) / 3600_000).toFixed(1)}h ago`;
|
|
741
|
+
checks.push(createInfo('transcript-mining', 'Scheduled transcript mining', `On for this project — ${when}. Have a scheduler run \`memesh dream run --from-transcripts --if-due\`; it mines when due (default every 24h) and stages proposals. Review the queue with \`memesh dream list\`.`));
|
|
742
|
+
}
|
|
733
743
|
checks.push(await inspectConfigParse(getConfigPathImpl, existsSyncImpl, readFileSyncImpl));
|
|
734
744
|
checks.push(await inspectEmbeddingProbe(capabilities, probeCapabilities, embedTextImpl));
|
|
735
745
|
checks.push(await inspectLlmProbe(capabilities, probeCapabilities, probeProviderImpl));
|