@pcircle/memesh 4.8.5 → 4.9.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 (226) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/mcp.json +6 -4
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/AGENTS.md +60 -14
  6. package/README.de.md +83 -556
  7. package/README.md +83 -582
  8. package/README.zh-TW.md +84 -573
  9. package/dashboard/dist/index.html +10 -10
  10. package/dist/cli/view-live.d.ts.map +1 -1
  11. package/dist/cli/view-live.js +154 -384
  12. package/dist/cli/view-live.js.map +1 -1
  13. package/dist/core/agent-router.d.ts +11 -10
  14. package/dist/core/agent-router.d.ts.map +1 -1
  15. package/dist/core/agent-router.js +29 -14
  16. package/dist/core/agent-router.js.map +1 -1
  17. package/dist/core/agent-scope-id.d.ts.map +1 -1
  18. package/dist/core/agent-scope-id.js.map +1 -1
  19. package/dist/core/analytics.d.ts.map +1 -1
  20. package/dist/core/analytics.js.map +1 -1
  21. package/dist/core/config.d.ts +4 -40
  22. package/dist/core/config.d.ts.map +1 -1
  23. package/dist/core/config.js +75 -141
  24. package/dist/core/config.js.map +1 -1
  25. package/dist/core/demo.d.ts.map +1 -1
  26. package/dist/core/demo.js +6 -6
  27. package/dist/core/demo.js.map +1 -1
  28. package/dist/core/doctor.d.ts +1 -6
  29. package/dist/core/doctor.d.ts.map +1 -1
  30. package/dist/core/doctor.js +55 -227
  31. package/dist/core/doctor.js.map +1 -1
  32. package/dist/core/dreamer.d.ts +32 -47
  33. package/dist/core/dreamer.d.ts.map +1 -1
  34. package/dist/core/dreamer.js +211 -703
  35. package/dist/core/dreamer.js.map +1 -1
  36. package/dist/core/install-channel.d.ts.map +1 -1
  37. package/dist/core/install-channel.js +4 -47
  38. package/dist/core/install-channel.js.map +1 -1
  39. package/dist/core/install-id.d.ts.map +1 -1
  40. package/dist/core/install-id.js.map +1 -1
  41. package/dist/core/kg-backfill.d.ts.map +1 -1
  42. package/dist/core/kg-backfill.js.map +1 -1
  43. package/dist/core/lesson-engine.d.ts +0 -5
  44. package/dist/core/lesson-engine.d.ts.map +1 -1
  45. package/dist/core/lesson-engine.js +0 -25
  46. package/dist/core/lesson-engine.js.map +1 -1
  47. package/dist/core/lifecycle.d.ts.map +1 -1
  48. package/dist/core/lifecycle.js +54 -48
  49. package/dist/core/lifecycle.js.map +1 -1
  50. package/dist/core/memory-tool.d.ts.map +1 -1
  51. package/dist/core/memory-tool.js +17 -17
  52. package/dist/core/memory-tool.js.map +1 -1
  53. package/dist/core/operations.d.ts +2 -26
  54. package/dist/core/operations.d.ts.map +1 -1
  55. package/dist/core/operations.js +9 -244
  56. package/dist/core/operations.js.map +1 -1
  57. package/dist/core/paths.d.ts +4 -1
  58. package/dist/core/paths.d.ts.map +1 -1
  59. package/dist/core/paths.js +88 -14
  60. package/dist/core/paths.js.map +1 -1
  61. package/dist/core/product-improvements.js +2 -2
  62. package/dist/core/product-improvements.js.map +1 -1
  63. package/dist/core/schema-export.d.ts.map +1 -1
  64. package/dist/core/schema-export.js +10 -0
  65. package/dist/core/schema-export.js.map +1 -1
  66. package/dist/core/semver.d.ts +7 -0
  67. package/dist/core/semver.d.ts.map +1 -0
  68. package/dist/core/semver.js +49 -0
  69. package/dist/core/semver.js.map +1 -0
  70. package/dist/core/serializer.d.ts.map +1 -1
  71. package/dist/core/serializer.js +69 -57
  72. package/dist/core/serializer.js.map +1 -1
  73. package/dist/core/signal-scorer.d.ts.map +1 -1
  74. package/dist/core/signal-scorer.js.map +1 -1
  75. package/dist/core/transcript-extractor.d.ts +1 -85
  76. package/dist/core/transcript-extractor.d.ts.map +1 -1
  77. package/dist/core/transcript-extractor.js +5 -364
  78. package/dist/core/transcript-extractor.js.map +1 -1
  79. package/dist/core/transcript-source.d.ts +22 -6
  80. package/dist/core/transcript-source.d.ts.map +1 -1
  81. package/dist/core/transcript-source.js +108 -69
  82. package/dist/core/transcript-source.js.map +1 -1
  83. package/dist/core/types.d.ts +1 -17
  84. package/dist/core/types.d.ts.map +1 -1
  85. package/dist/core/version-check.d.ts.map +1 -1
  86. package/dist/core/version-check.js +11 -1
  87. package/dist/core/version-check.js.map +1 -1
  88. package/dist/db.d.ts +0 -34
  89. package/dist/db.d.ts.map +1 -1
  90. package/dist/db.js +3 -287
  91. package/dist/db.js.map +1 -1
  92. package/dist/host-runtime/acp.d.ts.map +1 -1
  93. package/dist/host-runtime/acp.js +2 -2
  94. package/dist/host-runtime/acp.js.map +1 -1
  95. package/dist/host-runtime/claude.d.ts.map +1 -1
  96. package/dist/host-runtime/claude.js +9 -7
  97. package/dist/host-runtime/claude.js.map +1 -1
  98. package/dist/host-runtime/codex-session.d.ts +9 -1
  99. package/dist/host-runtime/codex-session.d.ts.map +1 -1
  100. package/dist/host-runtime/codex-session.js +474 -29
  101. package/dist/host-runtime/codex-session.js.map +1 -1
  102. package/dist/host-runtime/codex.d.ts.map +1 -1
  103. package/dist/host-runtime/codex.js +2 -2
  104. package/dist/host-runtime/codex.js.map +1 -1
  105. package/dist/host-runtime/config.d.ts +1 -0
  106. package/dist/host-runtime/config.d.ts.map +1 -1
  107. package/dist/host-runtime/config.js +4 -0
  108. package/dist/host-runtime/config.js.map +1 -1
  109. package/dist/host-runtime/router-client.d.ts.map +1 -1
  110. package/dist/host-runtime/router-client.js +62 -15
  111. package/dist/host-runtime/router-client.js.map +1 -1
  112. package/dist/host-runtime/router.js +2 -2
  113. package/dist/host-runtime/router.js.map +1 -1
  114. package/dist/knowledge-graph.d.ts.map +1 -1
  115. package/dist/knowledge-graph.js +54 -45
  116. package/dist/knowledge-graph.js.map +1 -1
  117. package/dist/mcp/THIRD_PARTY_NOTICES.txt +217 -0
  118. package/dist/mcp/server.js +30685 -38
  119. package/dist/mcp/server.js.map +6 -1
  120. package/dist/skills-manifest.json +22 -22
  121. package/dist/storage/entity-index.d.ts +0 -1
  122. package/dist/storage/entity-index.d.ts.map +1 -1
  123. package/dist/storage/entity-index.js +0 -7
  124. package/dist/storage/entity-index.js.map +1 -1
  125. package/dist/storage/fts-index.d.ts.map +1 -1
  126. package/dist/storage/fts-index.js +6 -7
  127. package/dist/storage/fts-index.js.map +1 -1
  128. package/dist/storage/graph-repairs.d.ts +0 -5
  129. package/dist/storage/graph-repairs.d.ts.map +1 -1
  130. package/dist/storage/graph-repairs.js +4 -91
  131. package/dist/storage/graph-repairs.js.map +1 -1
  132. package/dist/storage/schema.d.ts +1 -1
  133. package/dist/storage/schema.d.ts.map +1 -1
  134. package/dist/storage/schema.js +1 -2
  135. package/dist/storage/schema.js.map +1 -1
  136. package/dist/storage/sqlite.d.ts +0 -1
  137. package/dist/storage/sqlite.d.ts.map +1 -1
  138. package/dist/storage/sqlite.js.map +1 -1
  139. package/dist/transports/agent-messaging.d.ts.map +1 -1
  140. package/dist/transports/agent-messaging.js +5 -7
  141. package/dist/transports/agent-messaging.js.map +1 -1
  142. package/dist/transports/cli/cli.d.ts.map +1 -1
  143. package/dist/transports/cli/cli.js +52 -674
  144. package/dist/transports/cli/cli.js.map +1 -1
  145. package/dist/transports/http/retired-routes.js +1 -1
  146. package/dist/transports/http/retired-routes.js.map +1 -1
  147. package/dist/transports/http/server.d.ts.map +1 -1
  148. package/dist/transports/http/server.js +13 -232
  149. package/dist/transports/http/server.js.map +1 -1
  150. package/dist/transports/mcp/handlers.d.ts +127 -1
  151. package/dist/transports/mcp/handlers.d.ts.map +1 -1
  152. package/dist/transports/mcp/handlers.js +53 -4
  153. package/dist/transports/mcp/handlers.js.map +1 -1
  154. package/dist/transports/schemas.d.ts +52 -0
  155. package/dist/transports/schemas.d.ts.map +1 -1
  156. package/dist/transports/schemas.js +39 -0
  157. package/dist/transports/schemas.js.map +1 -1
  158. package/docs/platforms/README.md +5 -4
  159. package/docs/platforms/agent-messaging.md +124 -60
  160. package/hooks/hooks.json +13 -2
  161. package/llms-install.md +62 -30
  162. package/package.json +4 -5
  163. package/scripts/hooks/_generated/core-paths.js +88 -14
  164. package/scripts/hooks/_generated/fts-index.js +6 -7
  165. package/scripts/hooks/_generated/schema.js +1 -2
  166. package/scripts/hooks/_shared.js +14 -20
  167. package/scripts/hooks/session-start.js +8 -6
  168. package/scripts/hooks/session-summary.js +19 -374
  169. package/skills/memesh/SKILL.md +19 -14
  170. package/skills/memesh-review/SKILL.md +7 -6
  171. package/dist/core/auto-tagger.d.ts +0 -10
  172. package/dist/core/auto-tagger.d.ts.map +0 -1
  173. package/dist/core/auto-tagger.js +0 -63
  174. package/dist/core/auto-tagger.js.map +0 -1
  175. package/dist/core/conflict-candidates.d.ts +0 -20
  176. package/dist/core/conflict-candidates.d.ts.map +0 -1
  177. package/dist/core/conflict-candidates.js +0 -71
  178. package/dist/core/conflict-candidates.js.map +0 -1
  179. package/dist/core/conflict-judge.d.ts +0 -58
  180. package/dist/core/conflict-judge.d.ts.map +0 -1
  181. package/dist/core/conflict-judge.js +0 -189
  182. package/dist/core/conflict-judge.js.map +0 -1
  183. package/dist/core/digest-validator.d.ts +0 -18
  184. package/dist/core/digest-validator.d.ts.map +0 -1
  185. package/dist/core/digest-validator.js +0 -85
  186. package/dist/core/digest-validator.js.map +0 -1
  187. package/dist/core/embedder.d.ts +0 -20
  188. package/dist/core/embedder.d.ts.map +0 -1
  189. package/dist/core/embedder.js +0 -245
  190. package/dist/core/embedder.js.map +0 -1
  191. package/dist/core/failure-analyzer.d.ts +0 -19
  192. package/dist/core/failure-analyzer.d.ts.map +0 -1
  193. package/dist/core/failure-analyzer.js +0 -83
  194. package/dist/core/failure-analyzer.js.map +0 -1
  195. package/dist/core/json-utils.d.ts +0 -3
  196. package/dist/core/json-utils.d.ts.map +0 -1
  197. package/dist/core/json-utils.js +0 -46
  198. package/dist/core/json-utils.js.map +0 -1
  199. package/dist/core/llm-client.d.ts +0 -22
  200. package/dist/core/llm-client.d.ts.map +0 -1
  201. package/dist/core/llm-client.js +0 -203
  202. package/dist/core/llm-client.js.map +0 -1
  203. package/dist/core/llm-telemetry.d.ts +0 -47
  204. package/dist/core/llm-telemetry.d.ts.map +0 -1
  205. package/dist/core/llm-telemetry.js +0 -117
  206. package/dist/core/llm-telemetry.js.map +0 -1
  207. package/dist/core/llm-validator.d.ts +0 -20
  208. package/dist/core/llm-validator.d.ts.map +0 -1
  209. package/dist/core/llm-validator.js +0 -231
  210. package/dist/core/llm-validator.js.map +0 -1
  211. package/dist/core/ollama-host.d.ts +0 -6
  212. package/dist/core/ollama-host.d.ts.map +0 -1
  213. package/dist/core/ollama-host.js +0 -62
  214. package/dist/core/ollama-host.js.map +0 -1
  215. package/dist/core/output-language.d.ts +0 -6
  216. package/dist/core/output-language.d.ts.map +0 -1
  217. package/dist/core/output-language.js +0 -25
  218. package/dist/core/output-language.js.map +0 -1
  219. package/dist/core/prompt-safety.d.ts +0 -4
  220. package/dist/core/prompt-safety.d.ts.map +0 -1
  221. package/dist/core/prompt-safety.js +0 -20
  222. package/dist/core/prompt-safety.js.map +0 -1
  223. package/dist/storage/vector-index.d.ts +0 -3
  224. package/dist/storage/vector-index.d.ts.map +0 -1
  225. package/dist/storage/vector-index.js +0 -13
  226. package/dist/storage/vector-index.js.map +0 -1
@@ -4,22 +4,16 @@ import path from 'path';
4
4
  import net from 'node:net';
5
5
  import { createHash } from 'crypto';
6
6
  import { createRequire } from 'module';
7
- import { pathToFileURL } from 'url';
8
7
  import { execFileSync } from 'child_process';
9
- import { detectCapabilities, getConfigPath, isTranscriptMiningEnabled, readConfig } from './config.js';
10
- import { embedText } from './embedder.js';
11
- import { probeProvider } from './llm-validator.js';
12
- import { openDatabase, closeDatabase, getPendingReindexInfo, isDatabaseOpen, readVectorGeneration, generationRowIds, } from '../db.js';
8
+ import { findRetiredConfigKeys, getConfigPath, readConfig } from './config.js';
9
+ import { openDatabase, closeDatabase, isDatabaseOpen, } from '../db.js';
13
10
  import { getUpdateCheck } from './version-check.js';
14
11
  import { classifyBump } from './updater.js';
15
12
  import { getCurrentInstallChannel, getInstallChannelSupport, detectPluginHost, pluginHostConfigRoot, versionedPluginCacheRoots, PLUGIN_REFRESH_COMMANDS, } from './install-channel.js';
16
13
  import { getInstallRecord } from './install-id.js';
17
14
  import { citationRulePath, citationRuleState } from './citation-rule.js';
18
- import { getDbPath, getMemeshDirFromDbPath, homeDir, memeshDir, getProjectName } from './paths.js';
15
+ import { getAgentRouterSocketPath, getDbPath, getMemeshDirFromDbPath, homeDir, memeshDir } from './paths.js';
19
16
  import { detectPluginRuntime, readInstallMarker } from './install-hooks.js';
20
- import { lastTranscriptMineAt } from './transcript-source.js';
21
- import { countMissingVectors } from './operations.js';
22
- import { hasVectorIndex } from '../storage/vector-index.js';
23
17
  import { UNSPACED_SCRIPT_GLOB_RUN3 } from '../storage/fts-index.js';
24
18
  import { MemeshDatabase } from '../storage/sqlite.js';
25
19
  import { AUTO_CAPTURE_TAG } from './types.js';
@@ -28,8 +22,6 @@ import { autoCaptureDecision } from './capture-flag.js';
28
22
  import { guardFromMetadata } from './guards.js';
29
23
  import { getAgentMessageStorageReport } from './agent-message-storage.js';
30
24
  import { readHostConfigFile } from '../host-runtime/config.js';
31
- import { summariseTelemetry } from './llm-telemetry.js';
32
- const EMBEDDING_PROBE_TIMEOUT_MS = 15000;
33
25
  const EXPECTED_HOOK_TYPES = ['PreToolUse', 'SessionStart', 'PostToolUse', 'Stop', 'PreCompact'];
34
26
  const AGENT_MESSAGE_STORAGE_QUOTA_ENV = 'MEMESH_AGENT_MESSAGE_STORAGE_QUOTA_BYTES';
35
27
  const LOCALE_README_FILES = [
@@ -154,9 +146,9 @@ function inspectCodexSessionSetup(codexPluginCacheDetected, existsSyncImpl) {
154
146
  return null;
155
147
  const configPath = path.join(getMemeshDirFromDbPath(), 'hosts', 'codex-session.json');
156
148
  if (existsSyncImpl(configPath)) {
157
- return createCheck('codex-session-setup', 'Codex ordinary-session notification setup', 'pass', 'A Codex plugin cache copy was detected, but this proves only that cached source exists, not that the plugin is enabled or registered. The explicit opt-in ordinary-session notification setup is present; durable inbox remains available, and MeMesh will not auto-attach.');
149
+ return createInfo('codex-session-setup', 'Codex ordinary-session notifications', 'A Codex identity override file is present and will be validated at SessionStart. A valid override applies only in its configured workspace; other Codex plugin sessions use automatic thread-scoped identities. A cached plugin copy does not prove a live registration; use `memesh message discover --project <project>` to read current presence.');
158
150
  }
159
- return createCheck('codex-session-setup', 'Codex ordinary-session notification setup', 'warn', 'A Codex plugin cache copy was detected, but this proves only that cached source exists, not that the plugin is enabled or registered. Durable inbox remains available, but live ordinary-session wakeup is inactive. Setup is explicit opt-in; MeMesh will not auto-attach.', 'Run `memesh agent setup codex-session --project <project> --principal <principal> --workspace <exact-workspace>`, then restart Codex.', { code: 'codex-session.config-missing' });
151
+ return createInfo('codex-session-setup', 'Codex ordinary-session notifications', 'No explicit identity override is configured. When the Codex plugin is enabled, each startup or resumed thread registers automatically under a thread-scoped identity. A cached plugin copy does not prove a live registration; use `memesh message discover --project <project>` to read current presence.');
160
152
  }
161
153
  function configuredAgentMessageStoragePolicy(explicit) {
162
154
  if (explicit !== undefined)
@@ -195,14 +187,10 @@ function parseJsonFile(filePath, readFileSyncImpl) {
195
187
  };
196
188
  }
197
189
  }
198
- function inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl, envLlm) {
190
+ function inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl) {
199
191
  const configPath = getConfigPathImpl();
200
192
  if (!existsSyncImpl(configPath)) {
201
- return createCheck('config', 'Config', 'pass', envLlm
202
- ? `No config file yet (${configPath}), but your environment names ${envLlm.provider}${envLlm.apiKey ? ' (via its API key)' : ' (via OLLAMA_HOST)'}, which enables Smart Mode. A file is only needed to pin a provider or change defaults.`
203
- : `No config file yet (${configPath}). MeMesh will run in Core mode until you configure Smart Mode.`, envLlm
204
- ? `Optional: \`memesh config set llm.provider ${envLlm.provider}\` pins it so it does not depend on which shell you run from.`
205
- : 'Optional: run `memesh config list` or set an LLM with `memesh config set llm.provider anthropic`.');
193
+ return createCheck('config', 'Config', 'pass', `No config file yet (${configPath}); default settings are in effect.`, 'Optional: run memesh config list to inspect settings.');
206
194
  }
207
195
  try {
208
196
  const raw = readFileSyncImpl(configPath, 'utf8');
@@ -210,11 +198,21 @@ function inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl,
210
198
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
211
199
  return createCheck('config', 'Config', '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 } });
212
200
  }
201
+ const retiredKeys = findRetiredConfigKeys(parsed);
202
+ if (retiredKeys.length > 0) {
203
+ const keys = retiredKeys.join(', ');
204
+ return createCheck('config', 'Config', 'warn', `${configPath} is valid JSON but still contains ${retiredKeys.length} retired top-level setting(s) (${keys}). ` +
205
+ 'This version ignores them, but legacy provider credentials or settings may remain on disk.', `Review ${configPath} locally and remove only those retired top-level keys. ` +
206
+ 'Do not paste the file into an issue because it may contain credentials. Then run memesh doctor again.', {
207
+ code: 'config-parse.retired-settings',
208
+ params: { path: configPath, count: retiredKeys.length, keys },
209
+ });
210
+ }
213
211
  return createCheck('config', 'Config', 'pass', `${configPath} is valid JSON and its settings are in effect.`);
214
212
  }
215
213
  catch (err) {
216
214
  const msg = err instanceof Error ? err.message : String(err);
217
- return createCheck('config', 'Config', '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 } });
215
+ return createCheck('config', 'Config', 'fail', `${configPath} could not be read or parsed (${msg}). Its settings are being 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 } });
218
216
  }
219
217
  }
220
218
  const MCP_PLACEHOLDER = '${CLAUDE_PLUGIN_ROOT}';
@@ -552,19 +550,11 @@ function defaultResolveShellMemesh() {
552
550
  return null;
553
551
  }
554
552
  }
555
- const RUNTIME_TOO_OLD = 'memesh:node-sqlite-too-old';
556
- function defaultNativeBindingProbe(packageRoot) {
553
+ function defaultNativeBindingProbe(_packageRoot) {
557
554
  try {
558
- const probe = new MemeshDatabase(':memory:', { allowExtension: true });
555
+ const probe = new MemeshDatabase(':memory:');
559
556
  try {
560
- if (typeof probe.enableLoadExtension !== 'function') {
561
- return { ok: false, message: `${RUNTIME_TOO_OLD}: node:sqlite in ${process.version} has no enableLoadExtension` };
562
- }
563
- probe.enableLoadExtension(true);
564
- const localRequire = createRequire(pathToFileURL(path.join(packageRoot, 'package.json')).href);
565
- const sqliteVec = localRequire('sqlite-vec');
566
- sqliteVec.load(probe);
567
- probe.prepare('SELECT vec_version()').get();
557
+ probe.prepare('SELECT 1').get();
568
558
  }
569
559
  finally {
570
560
  probe.close();
@@ -629,17 +619,9 @@ export function hasBuiltInSqlite() {
629
619
  }
630
620
  function inspectNativeBinding(packageRoot, _existsSyncImpl, probeImpl = defaultNativeBindingProbe) {
631
621
  const result = probeImpl(packageRoot);
632
- if (result.ok) {
633
- return createCheck('native-binding', 'SQLite and vector search', 'pass', 'node:sqlite opened a database and sqlite-vec loaded (probe succeeded).');
634
- }
635
- if (result.message.startsWith(RUNTIME_TOO_OLD)) {
636
- return createCheck('native-binding', 'SQLite and vector search', 'fail', `The node:sqlite in this Node (${process.version}) is too old for memesh — it cannot load the vector-search extension. The complete version arrived in Node 22.13.`, 'Upgrade Node to 22.13 or newer, then re-run `memesh doctor`.', { code: 'native-binding.node-too-old', params: { version: process.version } });
637
- }
638
- const isMissingPackage = /MODULE_NOT_FOUND|Cannot find module/i.test(result.message);
639
- if (isMissingPackage) {
640
- return createCheck('native-binding', 'SQLite and vector search', 'warn', 'sqlite-vec is not installed, so memesh cannot search by meaning. Memories are still saved, and still found by keyword.', 'Run: npm install (in the directory that depends on @pcircle/memesh)', { code: 'native-binding.not-installed' });
641
- }
642
- return createCheck('native-binding', 'SQLite and vector search', 'warn', `sqlite-vec could not be loaded: ${result.message}. Memories are still saved and found by keyword; only search by meaning is off.`, `Run: cd "${packageRoot}" && npm install --omit=dev`, { code: 'native-binding.load-failed', params: { detail: result.message, root: packageRoot } });
622
+ return result.ok
623
+ ? createCheck('native-binding', 'SQLite', 'pass', 'node:sqlite opened a database (probe succeeded).')
624
+ : createCheck('native-binding', 'SQLite', 'fail', `SQLite could not open a database: ${result.message}`, 'Check the Node runtime and reinstall if necessary.', { code: 'native-binding.load-failed', params: { detail: result.message, root: packageRoot } });
643
625
  }
644
626
  function readVersionFromInstalledBinary(binaryPath, existsSyncImpl, readFileSyncImpl, realpathSyncImpl = fs.realpathSync) {
645
627
  let resolved;
@@ -677,29 +659,19 @@ function inspectShellCli(installChannel, packageRoot, packageVersion, resolveShe
677
659
  }
678
660
  if (hasDistinctShellCli) {
679
661
  const shellVersion = readVersionFromInstalledBinary(shellPath, existsSyncImpl, readFileSyncImpl);
680
- const shellIsBehind = shellVersion ? classifyBump(shellVersion, packageVersion) : null;
681
- const thisIsBehind = shellVersion ? classifyBump(packageVersion, shellVersion) : null;
682
- if (shellIsBehind) {
683
- return createCheck('shell-cli', 'Shell CLI on PATH', 'warn', `\`memesh\` resolves to ${shellPath} (separate from this install at ${packageRoot}), and it is running ${shellVersion} — behind this install's ${packageVersion}. Both share the same DB, but an agent using this install and a human typing \`memesh\` in a terminal are running different code.`, 'Run `npm install -g @pcircle/memesh@latest` to bring the shell CLI up to date — a separate global install is never updated automatically by the plugin marketplace.');
684
- }
685
- if (thisIsBehind) {
686
- const pluginHost = installChannel === 'plugin-marketplace' ? detectPluginHost(packageRoot) : null;
687
- const fix = installChannel !== 'plugin-marketplace'
688
- ? `Update this install (a ${installChannel}) to ${shellVersion} or newer via its own channel — see \`memesh status\`.`
689
- : pluginHost
690
- ? `Run \`${PLUGIN_REFRESH_COMMANDS[pluginHost]}\` to bring this plugin copy to ${shellVersion} (or newer).`
691
- : `Bring this plugin copy to ${shellVersion} (or newer) with your host's refresh command — `
692
- + `Claude Code: \`${PLUGIN_REFRESH_COMMANDS['claude-code']}\`; `
693
- + `Codex: \`${PLUGIN_REFRESH_COMMANDS.codex}\`.`;
694
- return createCheck('shell-cli', 'Shell CLI on PATH', 'warn', `\`memesh\` resolves to ${shellPath} (separate from this install at ${packageRoot}), and it is running ${shellVersion} — ahead of this install's ${packageVersion}.`, fix);
695
- }
696
- return createCheck('shell-cli', 'Shell CLI on PATH', 'pass', `\`memesh\` resolves to ${shellPath} (separate from this install at ${packageRoot}). Both paths coexist and share the same DB`
697
- + (shellVersion ? `, both on ${packageVersion}.` : ' — could not read the shell copy\'s own version to compare.'));
662
+ if (shellVersion) {
663
+ return {
664
+ ...createInfo('shell-cli', 'Shell CLI on PATH', `Installed versions: this interface ${packageVersion}; terminal ${shellVersion}.`),
665
+ code: 'shell-cli.versions',
666
+ params: { current: packageVersion, terminal: shellVersion, shellPath: shellPath, packageRoot },
667
+ };
668
+ }
669
+ return createCheck('shell-cli', 'Shell CLI on PATH', 'pass', `\`memesh\` resolves to ${shellPath} (separate from this install at ${packageRoot}) — could not read the shell copy's own version to compare.`);
698
670
  }
699
671
  if (installChannel === 'plugin-marketplace') {
700
672
  const host = detectPluginHost(packageRoot) === 'codex' ? 'Codex CLI' : 'Claude Code';
701
673
  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`. '
702
- + `${host} 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' });
674
+ + `${host} 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` if you want the separate shell CLI. Its database path depends on its environment and configuration.', { code: 'shell-cli.not-on-path' });
703
675
  }
704
676
  return createCheck('shell-cli', 'Shell CLI on PATH', 'pass', shellPath
705
677
  ? `\`memesh\` resolves to ${shellPath}.`
@@ -892,32 +864,23 @@ function inspectPluginCacheCurrency(installChannel, pluginHost, packageRoot, ins
892
864
  }
893
865
  return createCheck('plugin-cache', `Plugin cache source record is current (${hostLabel})`, 'warn', `The plugin cache records commit ${installedSha.slice(0, 8)}, but the marketplace has moved to ${marketplaceSha.slice(0, 8)} under the same version — ${hostLabel} does not normally refresh a cache whose version did not change, so refresh the cache before relying on the newer marketplace code.`, `Run \`${command}\` to refresh the cache in place, then restart ${hostLabel}.`, { code: 'plugin-cache.stale', params: { installed: installedSha.slice(0, 8), marketplace: marketplaceSha.slice(0, 8), host: hostLabel, command } });
894
866
  }
895
- function annotateNpmGlobalPluginCacheVersion(check, discoveredPackageRoot, hostLabel, runningVersion) {
896
- const cacheRoot = path.dirname(discoveredPackageRoot);
897
- const discoveredVersion = path.basename(discoveredPackageRoot);
898
- let amended = check;
899
- if (discoveredVersion !== runningVersion && classifyBump(runningVersion, discoveredVersion)) {
900
- const skewNote = `This npm-global install is on ${runningVersion}; the ${hostLabel} plugin cache is on ${discoveredVersion}. `
901
- + 'The plugin marketplace\'s own auto-updater only ever refreshes its plugin copy — it cannot and will not update this separate npm-global install.';
902
- const skewFix = `Run \`memesh update\` to bring this npm-global install to ${discoveredVersion} (or newer) — it does not update itself automatically.`;
903
- amended = {
904
- ...amended,
905
- status: amended.status === 'pass' ? 'warn' : amended.status,
906
- summary: `${amended.summary} ${skewNote}`,
907
- fix: amended.fix ? `${amended.fix} Also: ${skewFix}` : skewFix,
908
- code: undefined,
909
- params: undefined,
910
- };
867
+ function isClaudeChannelCommand(command) {
868
+ if (command === 'memesh-host-claude')
869
+ return true;
870
+ if (typeof command !== 'string' || !path.isAbsolute(command) || path.basename(command) !== 'memesh-host-claude') {
871
+ return false;
911
872
  }
912
- const cachedVersions = versionedPluginCacheRoots(cacheRoot);
913
- if (cachedVersions.length > 2) {
914
- amended = {
915
- ...amended,
916
- summary: `${amended.summary} ${cachedVersions.length} versioned copies of the ${hostLabel} plugin are cached under ${cacheRoot}; old ones are never removed automatically. `
917
- + `Delete ones you no longer need once no ${hostLabel} process is using them, e.g. \`rm -rf "${cachedVersions[0]}"\`.`,
918
- };
873
+ try {
874
+ const target = fs.realpathSync(command);
875
+ const stat = fs.statSync(target);
876
+ if (!stat.isFile())
877
+ return false;
878
+ fs.accessSync(target, fs.constants.X_OK);
879
+ return true;
880
+ }
881
+ catch {
882
+ return false;
919
883
  }
920
- return amended;
921
884
  }
922
885
  function inspectClaudeChannelRegistration(existsSyncImpl, readFileSyncImpl) {
923
886
  const configPath = path.join(homeDir(), '.claude.json');
@@ -967,14 +930,15 @@ function inspectClaudeChannelRegistration(existsSyncImpl, readFileSyncImpl) {
967
930
  targetConfigValid = false;
968
931
  }
969
932
  }
970
- const coherent = command === 'memesh-host-claude'
933
+ const commandValid = isClaudeChannelCommand(command);
934
+ const coherent = commandValid
971
935
  && args !== null
972
936
  && args.length === 2
973
937
  && configIndex === 0
974
938
  && target !== null
975
939
  && targetConfigValid;
976
940
  if (!coherent) {
977
- const reason = command !== 'memesh-host-claude' || args === null || configIndex !== 0 || target === null
941
+ const reason = !commandValid || args === null || configIndex !== 0 || target === null
978
942
  ? 'the command or --config declaration is malformed'
979
943
  : 'the declared owner config target is missing, insecure, malformed, or incomplete';
980
944
  return createCheck('claude-channel', 'Claude Channel registration', 'warn', `The user-scoped memesh-channel registration is present but ${reason}. Live Claude Channel notification is not established.`, 'Remove and re-register the owner-controlled memesh-channel entry with `memesh agent setup claude`; keep its generated config file owner-private.');
@@ -1197,8 +1161,7 @@ function inspectMessageCapability(packageRoot, enabled, probe) {
1197
1161
  return createCheck('message-capability', 'Message adapter imports', 'fail', `Installed message capability probe failed: ${result.message}.`, 'Reinstall or rebuild this package, then retry with MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY=1.', { code: 'message-capability.probe-failed', params: { detail: result.message } });
1198
1162
  }
1199
1163
  async function defaultMessageRouterStatusProbe() {
1200
- const socketPath = process.env.MEMESH_ROUTER_SOCKET
1201
- ?? path.join(getMemeshDirFromDbPath(), 'agent-router.sock');
1164
+ const socketPath = process.env.MEMESH_ROUTER_SOCKET ?? getAgentRouterSocketPath();
1202
1165
  let stat;
1203
1166
  try {
1204
1167
  stat = fs.lstatSync(socketPath);
@@ -1245,81 +1208,6 @@ async function inspectMessageRouterStatus(enabled, probe) {
1245
1208
  return createCheck('message-router-status', 'Live message router / host registration', 'warn', `An owner-private router socket exists at ${result.socket_path}, but it did not accept a local connection. No host registration or native delivery is verified.`, 'Check the owner-configured `memesh-router` process, then run this opt-in probe again.', { code: 'message-router.socket-unreachable', params: { path: result.socket_path } });
1246
1209
  }
1247
1210
  }
1248
- async function inspectEmbeddingProbe(capabilities, probeCapabilities, embedTextImpl) {
1249
- if (capabilities.embeddings === 'tfidf') {
1250
- 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.');
1251
- }
1252
- if (!probeCapabilities) {
1253
- 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)');
1254
- }
1255
- let timer;
1256
- try {
1257
- const vector = await Promise.race([
1258
- embedTextImpl('memesh doctor embedding probe'),
1259
- new Promise((_, reject) => {
1260
- timer = setTimeout(() => reject(new Error(`no response within ${EMBEDDING_PROBE_TIMEOUT_MS / 1000}s`)), EMBEDDING_PROBE_TIMEOUT_MS);
1261
- }),
1262
- ]);
1263
- if (!vector || vector.length === 0) {
1264
- 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) } });
1265
- }
1266
- return createCheck('embeddings_probe', 'Embeddings work', 'pass', `Generated a ${vector.length}-dim test embedding via "${capabilities.embeddings}".`);
1267
- }
1268
- catch (err) {
1269
- const msg = err instanceof Error ? err.message : String(err);
1270
- 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 } });
1271
- }
1272
- finally {
1273
- clearTimeout(timer);
1274
- }
1275
- }
1276
- const LLM_TELEMETRY_HEALTH_WINDOW_DAYS = 7;
1277
- const LLM_TELEMETRY_HEALTH_MIN_CALLS = 3;
1278
- function inspectLlmTelemetryHealth(db, windowDays = LLM_TELEMETRY_HEALTH_WINDOW_DAYS, minCalls = LLM_TELEMETRY_HEALTH_MIN_CALLS) {
1279
- let summaries;
1280
- try {
1281
- summaries = summariseTelemetry(windowDays, db);
1282
- }
1283
- catch {
1284
- return undefined;
1285
- }
1286
- if (summaries.length === 0)
1287
- return undefined;
1288
- const broken = summaries
1289
- .filter((s) => s.total_calls >= minCalls && s.successes === 0)
1290
- .sort((a, b) => b.total_calls - a.total_calls);
1291
- if (broken.length > 0) {
1292
- const detail = broken.map((s) => `${s.flow} (${s.total_calls} call${s.total_calls === 1 ? '' : 's'}, 0 succeeded)`).join(', ');
1293
- return createCheck('llm_telemetry_health', 'AI feature health', 'warn', `${broken.length} AI-backed feature${broken.length === 1 ? '' : 's'} failed every call in the last `
1294
- + `${windowDays} days: ${detail}. Those features are silently doing nothing.`, 'Run `memesh telemetry` for the full per-flow detail, then check the model/provider configured for '
1295
- + 'the failing flow. `memesh doctor --probe` confirms whether it answers a live call.', { code: 'llm-telemetry.silent-failure', params: { count: broken.length, detail, windowDays } });
1296
- }
1297
- const totalCalls = summaries.reduce((n, s) => n + s.total_calls, 0);
1298
- const totalSuccesses = summaries.reduce((n, s) => n + s.successes, 0);
1299
- const rate = totalCalls > 0 ? Math.round((totalSuccesses / totalCalls) * 100) : 100;
1300
- return createInfo('llm_telemetry_health', 'AI feature health', `${summaries.length} AI-backed flow${summaries.length === 1 ? '' : 's'} made ${totalCalls} call(s) in the `
1301
- + `last ${windowDays} days; ${rate}% succeeded.`);
1302
- }
1303
- async function inspectLlmProbe(capabilities, probeCapabilities, probeProviderImpl) {
1304
- const llm = capabilities.llm;
1305
- if (!llm) {
1306
- return createInfo('llm_probe', 'LLM reachable', 'No LLM configured — Core Mode. Write-side features (lessons, auto-tag, dream) are off by design.');
1307
- }
1308
- if (!probeCapabilities) {
1309
- return createInfo('llm_probe', 'LLM reachable', `NOT VERIFIED. Config names ${llm.provider} (${llm.model ?? 'default'}), but no live call was made — an expired key or an unreachable host would look identical to a healthy setup here.`, 'Run: memesh doctor --probe (makes one small live call to confirm)');
1310
- }
1311
- try {
1312
- const result = await probeProviderImpl(llm.provider, llm.apiKey);
1313
- if (result.valid) {
1314
- return createCheck('llm_probe', 'LLM reachable', 'pass', `${llm.provider} answered a live probe.`);
1315
- }
1316
- 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' } });
1317
- }
1318
- catch (err) {
1319
- const msg = err instanceof Error ? err.message : String(err);
1320
- 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 } });
1321
- }
1322
- }
1323
1211
  function summarizeOverallStatus(checks) {
1324
1212
  const assertions = checks.filter((check) => !check.informational);
1325
1213
  if (assertions.some((check) => check.status === 'fail'))
@@ -1329,7 +1217,7 @@ function summarizeOverallStatus(checks) {
1329
1217
  return 'PASS';
1330
1218
  }
1331
1219
  export async function runDoctor(options) {
1332
- const { packageRoot, packageVersion, probeHttp = false, probeCapabilities = false, embedTextImpl = embedText, probeProviderImpl = probeProvider, httpBaseUrl = 'http://127.0.0.1:3737', platform = process.platform, envImpl = process.env, openDatabaseImpl = openDatabase, closeDatabaseImpl = closeDatabase, isDatabaseOpenImpl = isDatabaseOpen, detectCapabilitiesImpl = detectCapabilities, getConfigPathImpl = getConfigPath, getUpdateCheckImpl = getUpdateCheck, getCurrentInstallChannelImpl = getCurrentInstallChannel, installedPluginsPathImpl, marketplaceHeadShaImpl = defaultMarketplaceHeadSha, pluginCacheDiscoveryImpl, getInstallChannelSupportImpl = getInstallChannelSupport, existsSyncImpl = fs.existsSync, readFileSyncImpl = fs.readFileSync, statSyncImpl = fs.statSync, fetchImpl = fetch, agentMessageStoragePolicy, nativeBindingProbeImpl, resolveShellMemeshImpl = defaultResolveShellMemesh, probeMessageCapability = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY === '1', messageCapabilityProbeImpl = probeInstalledMessageCapability, probeMessageRouterStatus = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_ROUTER === '1', messageRouterStatusProbeImpl = defaultMessageRouterStatusProbe, } = options;
1220
+ const { packageRoot, packageVersion, probeHttp = false, httpBaseUrl = 'http://127.0.0.1:3737', platform = process.platform, envImpl = process.env, openDatabaseImpl = openDatabase, closeDatabaseImpl = closeDatabase, isDatabaseOpenImpl = isDatabaseOpen, getConfigPathImpl = getConfigPath, getUpdateCheckImpl = getUpdateCheck, getCurrentInstallChannelImpl = getCurrentInstallChannel, installedPluginsPathImpl, marketplaceHeadShaImpl = defaultMarketplaceHeadSha, pluginCacheDiscoveryImpl, getInstallChannelSupportImpl = getInstallChannelSupport, existsSyncImpl = fs.existsSync, readFileSyncImpl = fs.readFileSync, statSyncImpl = fs.statSync, fetchImpl = fetch, agentMessageStoragePolicy, nativeBindingProbeImpl, resolveShellMemeshImpl = defaultResolveShellMemesh, probeMessageCapability = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY === '1', messageCapabilityProbeImpl = probeInstalledMessageCapability, probeMessageRouterStatus = process.env.MEMESH_DOCTOR_PROBE_MESSAGE_ROUTER === '1', messageRouterStatusProbeImpl = defaultMessageRouterStatusProbe, } = options;
1333
1221
  const wasDbOpenBeforeUs = isDatabaseOpenImpl();
1334
1222
  const safeCloseDatabaseImpl = wasDbOpenBeforeUs
1335
1223
  ? () => undefined
@@ -1365,46 +1253,6 @@ export async function runDoctor(options) {
1365
1253
  `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 } }, 'fts-rebuild'));
1366
1254
  }
1367
1255
  }
1368
- const pendingReindex = getPendingReindexInfo();
1369
- const vectorDb = db;
1370
- let missingVectors;
1371
- let vectorsPossible = true;
1372
- try {
1373
- vectorsPossible = hasVectorIndex(vectorDb);
1374
- missingVectors = vectorsPossible ? countMissingVectors(vectorDb) : 0;
1375
- }
1376
- catch {
1377
- missingVectors = null;
1378
- }
1379
- const payableDebt = pendingReindex && !(pendingReindex.reason === 'vectors-missing' && !vectorsPossible);
1380
- if (payableDebt || missingVectors === null || missingVectors > 0) {
1381
- const owed = pendingReindex && pendingReindex.reason !== 'vectors-missing'
1382
- ? 'Search index needs rebuilding (embedding configuration changed)'
1383
- : missingVectors === null
1384
- ? 'The vector index could not be read, so how much of your memory semantic recall can see is unknown'
1385
- : `${missingVectors} memor${missingVectors === 1 ? 'y has' : 'ies have'} no search vector, `
1386
- + 'so semantic recall cannot find them (keyword search still works)';
1387
- const noEmbedderConfigured = detectCapabilitiesImpl().embeddings === 'tfidf';
1388
- const vectorIndexFix = noEmbedderConfigured
1389
- ? `No embedder is configured, so reindex has nothing to embed with — run 'memesh config set embedder.provider ollama' (or 'openai') first, then 'memesh reindex'.`
1390
- : `Run 'memesh reindex' to fix. This will restore full search functionality.`;
1391
- dbChecks.push(createCheck('vector_index', 'Vector Index', 'warn', owed, vectorIndexFix, {
1392
- code: noEmbedderConfigured ? 'vector-index.stale-no-embedder' : 'vector-index.stale',
1393
- params: { missing: missingVectors ?? -1 },
1394
- }));
1395
- }
1396
- const generation = readVectorGeneration();
1397
- if (generation.state !== 'none') {
1398
- const staged = generationRowIds().size;
1399
- const detail = generation.state === 'open'
1400
- ? `${staged} vectors staged at ${generation.info.dimension} dimensions `
1401
- + `(provider ${generation.info.provider}, started ${generation.info.startedAt})`
1402
- : `${staged} vectors staged, but the marker cannot be read (${generation.detail})`;
1403
- dbChecks.push(createCheck('vector_generation', 'Half-built search index', 'warn', `An unfinished index rebuild is holding disk space: ${detail}.`, generation.state === 'open'
1404
- ? `Run 'memesh reindex' to finish it (the vectors already produced are reused), `
1405
- + `or 'memesh reindex --discard-generation' to reclaim the space.`
1406
- : `Run 'memesh reindex --discard-generation' to clear it, then 'memesh reindex'.`, { code: 'vector-generation.open', params: { staged } }));
1407
- }
1408
1256
  try {
1409
1257
  const guardRows = db
1410
1258
  .prepare(`SELECT id, name, metadata FROM entities
@@ -1472,9 +1320,6 @@ export async function runDoctor(options) {
1472
1320
  dbChecks.push(createInfo('citation_compliance', 'Memory citation rate', `${cited} of ${citationTotal} session(s) with injected memories cited at least one (${rate}%).`));
1473
1321
  }
1474
1322
  }
1475
- const llmTelemetryHealth = inspectLlmTelemetryHealth(db);
1476
- if (llmTelemetryHealth)
1477
- dbChecks.push(llmTelemetryHealth);
1478
1323
  }
1479
1324
  catch (err) {
1480
1325
  const message = err instanceof Error ? err.message : 'unknown database error';
@@ -1541,7 +1386,7 @@ export async function runDoctor(options) {
1541
1386
  catch {
1542
1387
  }
1543
1388
  }
1544
- checks.push(inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl, detectCapabilitiesImpl().llm));
1389
+ checks.push(inspectConfigFile(existsSyncImpl, readFileSyncImpl, getConfigPathImpl));
1545
1390
  checks.push(inspectMcpConfig(packageRoot, install, existsSyncImpl, readFileSyncImpl, envImpl));
1546
1391
  checks.push(...inspectHooksConfig(packageRoot, platform, existsSyncImpl, readFileSyncImpl, statSyncImpl));
1547
1392
  const pluginHost = detectPluginHost(packageRoot);
@@ -1561,12 +1406,9 @@ export async function runDoctor(options) {
1561
1406
  codexPluginCacheDetected = true;
1562
1407
  if (discovered.host === 'claude-code')
1563
1408
  claudePluginCacheDetected = true;
1564
- let check = discovered.unverifiableReason
1409
+ const check = discovered.unverifiableReason
1565
1410
  ? pluginCacheUnverifiable(discovered.host, discovered.unverifiableReason)
1566
1411
  : inspectPluginCacheCurrency('plugin-marketplace', discovered.host, discovered.packageRoot, discovered.installedPluginsPath, readFileSyncImpl, existsSyncImpl, marketplaceHeadShaImpl);
1567
- if (check && !discovered.unverifiableReason) {
1568
- check = annotateNpmGlobalPluginCacheVersion(check, discovered.packageRoot, discovered.host === 'codex' ? 'Codex' : 'Claude Code', packageVersion);
1569
- }
1570
1412
  if (check) {
1571
1413
  const hostName = discovered.host;
1572
1414
  const index = (discoveredCounts.get(discovered.host) ?? 0) + 1;
@@ -1592,20 +1434,6 @@ export async function runDoctor(options) {
1592
1434
  checks.push(verifySkillsManifest(packageRoot, existsSyncImpl, readFileSyncImpl, installSupport));
1593
1435
  checks.push(inspectMessageCapability(packageRoot, probeMessageCapability, messageCapabilityProbeImpl));
1594
1436
  checks.push(await inspectMessageRouterStatus(probeMessageRouterStatus, messageRouterStatusProbeImpl));
1595
- const capabilities = detectCapabilitiesImpl();
1596
- 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.`));
1597
- if (!isTranscriptMiningEnabled()) {
1598
- 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`.'));
1599
- }
1600
- else {
1601
- const last = lastTranscriptMineAt(getProjectName(process.cwd()));
1602
- const when = last === null
1603
- ? 'not yet run for this project'
1604
- : `last mined ${((Date.now() - last) / 3600_000).toFixed(1)}h ago`;
1605
- 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\`.`));
1606
- }
1607
- checks.push(await inspectEmbeddingProbe(capabilities, probeCapabilities, embedTextImpl));
1608
- checks.push(await inspectLlmProbe(capabilities, probeCapabilities, probeProviderImpl));
1609
1437
  checks.push(await inspectUpdateStatus(packageVersion, getUpdateCheckImpl, installSupport));
1610
1438
  try {
1611
1439
  const record = getInstallRecord();