@bcmyguest/openmem 0.0.0-stage → 0.8.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 (230) hide show
  1. package/.agents/skills/openmem/SKILL.md +34 -0
  2. package/LICENSE +190 -0
  3. package/README.md +140 -2
  4. package/bin/hindsight-project-index +530 -0
  5. package/cli/coding-agent/access-policy.mjs +38 -0
  6. package/cli/coding-agent/agents.mjs +34 -0
  7. package/cli/coding-agent/bank-from-cwd.mjs +140 -0
  8. package/cli/coding-agent/capabilities.mjs +34 -0
  9. package/cli/coding-agent/context-store.mjs +81 -0
  10. package/cli/coding-agent/context-trim.mjs +897 -0
  11. package/cli/coding-agent/file-io.mjs +72 -0
  12. package/cli/coding-agent/hook-harnesses.mjs +88 -0
  13. package/cli/coding-agent/hook-runner.mjs +284 -0
  14. package/cli/coding-agent/install-access.mjs +119 -0
  15. package/cli/coding-agent/install-context-proxy.mjs +333 -0
  16. package/cli/coding-agent/install-rtk.mjs +60 -0
  17. package/cli/coding-agent/install-shim.mjs +201 -0
  18. package/cli/coding-agent/opencode-config.mjs +107 -0
  19. package/cli/coding-agent/opencode-plugin.mjs +132 -0
  20. package/cli/coding-agent/opencode2-plugin/index.js +299 -0
  21. package/cli/coding-agent/recall-frame.mjs +29 -0
  22. package/cli/coding-agent/remove-upstream-access.mjs +122 -0
  23. package/cli/coding-agent/runtime.mjs +40 -0
  24. package/cli/coding-agent/session-db.mjs +626 -0
  25. package/cli/coding-agent/session-facade.mjs +86 -0
  26. package/cli/coding-agent/toml-edit.mjs +161 -0
  27. package/cli/cognee-mcp.mjs +260 -0
  28. package/cli/context-management.mjs +254 -0
  29. package/cli/feed-sync.mjs +27 -0
  30. package/cli/install-coding-agent.mjs +298 -0
  31. package/cli/install-memory-mcp.mjs +426 -0
  32. package/cli/is-main.mjs +18 -0
  33. package/cli/mcp/context-sql-runner.mjs +58 -0
  34. package/cli/mcp/context-store.mjs +670 -0
  35. package/cli/mcp/context.mjs +61 -0
  36. package/cli/mcp/export/cognee-dataset-name.mjs +12 -0
  37. package/cli/mcp/export/cognee.mjs +119 -0
  38. package/cli/mcp/export/hindsight.mjs +48 -0
  39. package/cli/mcp/export/index.mjs +26 -0
  40. package/cli/mcp/export/mem0.mjs +31 -0
  41. package/cli/mcp/knowledge.mjs +333 -0
  42. package/cli/mcp/llm-synthesize.mjs +50 -0
  43. package/cli/mcp/mem0-scope-id.mjs +22 -0
  44. package/cli/mcp/memory-facade.mjs +521 -0
  45. package/cli/mcp/memory-reflect-consolidate.mjs +120 -0
  46. package/cli/mcp/notebooklm-banks.mjs +21 -0
  47. package/cli/mcp/notebooklm-hindsight-rest.mjs +45 -0
  48. package/cli/mcp/notebooklm-provider.mjs +225 -0
  49. package/cli/mcp/pmm-records.mjs +23 -0
  50. package/cli/mcp/portability.mjs +158 -0
  51. package/cli/mcp/stdio.mjs +65 -0
  52. package/cli/mcp/unified-mcp-install.mjs +65 -0
  53. package/cli/mem0-mcp.mjs +108 -0
  54. package/cli/notebooklm-mcp.mjs +236 -0
  55. package/cli/openmem-mcp.mjs +38 -0
  56. package/cli/openmem.mjs +222 -0
  57. package/cli/roots.mjs +85 -0
  58. package/cli/schedule.mjs +176 -0
  59. package/cli/stack.mjs +438 -0
  60. package/cli/switch-memory-provider.mjs +358 -0
  61. package/cli/wiring-report.mjs +125 -0
  62. package/compose.bench.yaml +12 -0
  63. package/compose.yaml +227 -0
  64. package/config/coding-agent.json +13 -0
  65. package/config/hindsight.env.example +26 -0
  66. package/config/index.yml +54 -0
  67. package/config/litellm.lemond.yaml +40 -0
  68. package/config/litellm.ollama.yaml +33 -0
  69. package/config/notebooklm.yml +33 -0
  70. package/config/openmem.toml.example +124 -0
  71. package/docs/OPENMEM.md +23 -0
  72. package/docs/architecture.md +245 -0
  73. package/docs/assets/README.md +10 -0
  74. package/docs/assets/setup-demo.gif +0 -0
  75. package/docs/coding-agent-hooks.md +75 -0
  76. package/docs/coding-agent-scopes.md +22 -0
  77. package/docs/coding-agents.md +71 -0
  78. package/docs/cognee.md +37 -0
  79. package/docs/compatibility.md +80 -0
  80. package/docs/concepts.md +135 -0
  81. package/docs/context-management.md +255 -0
  82. package/docs/context-proxy.md +115 -0
  83. package/docs/context-research.md +215 -0
  84. package/docs/context-store.md +277 -0
  85. package/docs/hindsight.md +77 -0
  86. package/docs/index-sources-roadmap.md +38 -0
  87. package/docs/index-sources.md +39 -0
  88. package/docs/indexer-sinks.md +122 -0
  89. package/docs/indexing.md +57 -0
  90. package/docs/manual-stack-setup.md +74 -0
  91. package/docs/mcp-install-guide.md +4 -0
  92. package/docs/mcp-setup.md +104 -0
  93. package/docs/mem0.md +141 -0
  94. package/docs/memory-portability.md +73 -0
  95. package/docs/memory-providers.md +246 -0
  96. package/docs/meridian-litellm.md +198 -0
  97. package/docs/model-configuration.md +106 -0
  98. package/docs/notebooklm.md +83 -0
  99. package/docs/npx-install.md +172 -0
  100. package/docs/openmem-config.md +194 -0
  101. package/docs/provider-wizard-hooks.md +222 -0
  102. package/docs/quickstart.md +107 -0
  103. package/docs/setup-wizard.md +117 -0
  104. package/docs/sources-feeds.md +39 -0
  105. package/docs/sources-readwise.md +23 -0
  106. package/docs/sources-zotero.md +22 -0
  107. package/glama.json +4 -0
  108. package/indexer/Dockerfile +38 -0
  109. package/indexer/code_outline.py +146 -0
  110. package/indexer/feed_sync.py +107 -0
  111. package/indexer/index_config.py +290 -0
  112. package/indexer/pytest.ini +5 -0
  113. package/indexer/sinks/__init__.py +148 -0
  114. package/indexer/sinks/_http.py +44 -0
  115. package/indexer/sinks/cognee.py +329 -0
  116. package/indexer/sinks/hindsight.py +107 -0
  117. package/indexer/sinks/mem0.py +247 -0
  118. package/indexer/watch.py +240 -0
  119. package/package.json +96 -4
  120. package/providers/_template/compose.yaml +52 -0
  121. package/providers/cognee/OPERATOR.md +82 -0
  122. package/providers/cognee/compose.yaml +89 -0
  123. package/providers/cognee/lib/clack.mjs +157 -0
  124. package/providers/cognee/lib/coding-agent.mjs +26 -0
  125. package/providers/cognee/lib/cognee-mcp-install.mjs +153 -0
  126. package/providers/cognee/lib/config.mjs +98 -0
  127. package/providers/cognee/lib/env-file.mjs +194 -0
  128. package/providers/cognee/lib/env-setup.mjs +83 -0
  129. package/providers/cognee/lib/llm-env.mjs +382 -0
  130. package/providers/cognee/lib/ports.mjs +25 -0
  131. package/providers/cognee/lib/smoke.mjs +246 -0
  132. package/providers/cognee/lib/stack-env.mjs +134 -0
  133. package/providers/cognee/lib/state.mjs +101 -0
  134. package/providers/cognee/lib/summary.mjs +56 -0
  135. package/providers/cognee/lib/validate.mjs +97 -0
  136. package/providers/cognee/manifest.json +56 -0
  137. package/providers/cognee/wizard.mjs +134 -0
  138. package/providers/graphiti/manifest.json +15 -0
  139. package/providers/hindsight/lib/clack.mjs +347 -0
  140. package/providers/hindsight/lib/coding-agent.mjs +204 -0
  141. package/providers/hindsight/lib/config.mjs +79 -0
  142. package/providers/hindsight/lib/env-setup.mjs +72 -0
  143. package/providers/hindsight/lib/litellm-routes.mjs +196 -0
  144. package/providers/hindsight/lib/meridian.mjs +307 -0
  145. package/providers/hindsight/lib/model-backend.mjs +243 -0
  146. package/providers/hindsight/lib/notebooklm-mcp-install.mjs +127 -0
  147. package/providers/hindsight/lib/ollama-compose.mjs +51 -0
  148. package/providers/hindsight/lib/ports.mjs +30 -0
  149. package/providers/hindsight/lib/probe.mjs +54 -0
  150. package/providers/hindsight/lib/stack-env.mjs +125 -0
  151. package/providers/hindsight/lib/state.mjs +80 -0
  152. package/providers/hindsight/lib/summary.mjs +57 -0
  153. package/providers/hindsight/lib/validate.mjs +81 -0
  154. package/providers/hindsight/manifest.json +58 -0
  155. package/providers/hindsight/wizard.mjs +150 -0
  156. package/providers/letta/manifest.json +29 -0
  157. package/providers/mem0/OPERATOR.md +143 -0
  158. package/providers/mem0/compose.yaml +79 -0
  159. package/providers/mem0/lib/coding-agent.mjs +58 -0
  160. package/providers/mem0/lib/config.mjs +45 -0
  161. package/providers/mem0/lib/configure.mjs +201 -0
  162. package/providers/mem0/lib/dotenv.mjs +71 -0
  163. package/providers/mem0/lib/env-file.mjs +110 -0
  164. package/providers/mem0/lib/http.mjs +69 -0
  165. package/providers/mem0/lib/litellm.mjs +457 -0
  166. package/providers/mem0/lib/mcp-install.mjs +165 -0
  167. package/providers/mem0/lib/secret.mjs +30 -0
  168. package/providers/mem0/lib/smoke.mjs +179 -0
  169. package/providers/mem0/lib/stack-env.mjs +255 -0
  170. package/providers/mem0/lib/state.mjs +67 -0
  171. package/providers/mem0/lib/summary.mjs +88 -0
  172. package/providers/mem0/lib/url.mjs +106 -0
  173. package/providers/mem0/lib/validate.mjs +158 -0
  174. package/providers/mem0/manifest.json +47 -0
  175. package/providers/mem0/wizard.mjs +146 -0
  176. package/providers/zep/manifest.json +29 -0
  177. package/proxy/config.yaml +33 -0
  178. package/proxy/openmem_context/__init__.py +6 -0
  179. package/proxy/openmem_context/adapters.py +504 -0
  180. package/proxy/openmem_context/compress.py +53 -0
  181. package/proxy/openmem_context/core.py +606 -0
  182. package/proxy/openmem_context/embed.py +212 -0
  183. package/proxy/openmem_context/recall.py +49 -0
  184. package/proxy/openmem_context/session.py +70 -0
  185. package/proxy/openmem_context/sessiondb.py +276 -0
  186. package/proxy/openmem_context/settings.py +134 -0
  187. package/proxy/openmem_context/store.py +129 -0
  188. package/proxy/openmem_context/tokens.py +123 -0
  189. package/proxy/openmem_context/ttl.py +78 -0
  190. package/proxy/openmem_context_hook.py +34 -0
  191. package/scripts/check-pack.mjs +219 -0
  192. package/scripts/gen-masks.mjs +83 -0
  193. package/scripts/gen-masks.sh +4 -0
  194. package/scripts/promotion-provider-smoke.mjs +34 -0
  195. package/scripts/run-setup.sh +30 -0
  196. package/scripts/setup.mjs +107 -0
  197. package/scripts/setup.sh +5 -0
  198. package/scripts/spin-up.sh +45 -0
  199. package/scripts/wizard.sh +5 -0
  200. package/server.json +34 -0
  201. package/sync/Dockerfile +27 -0
  202. package/sync/memory_backend.py +87 -0
  203. package/sync/notebooklm_sync.py +664 -0
  204. package/wizard/bin/pm.mjs +97 -0
  205. package/wizard/bin/setup.mjs +33 -0
  206. package/wizard/lib/compose-version.mjs +32 -0
  207. package/wizard/lib/compose.mjs +70 -0
  208. package/wizard/lib/context-step.mjs +140 -0
  209. package/wizard/lib/curl.mjs +103 -0
  210. package/wizard/lib/docker-build.mjs +148 -0
  211. package/wizard/lib/exec.mjs +41 -0
  212. package/wizard/lib/indexer.mjs +6 -0
  213. package/wizard/lib/litellm-env.mjs +55 -0
  214. package/wizard/lib/load-machine-env.mjs +4 -0
  215. package/wizard/lib/log.mjs +32 -0
  216. package/wizard/lib/notebooklm-agent-access.mjs +92 -0
  217. package/wizard/lib/notebooklm.mjs +214 -0
  218. package/wizard/lib/parse-args.mjs +87 -0
  219. package/wizard/lib/paths.mjs +39 -0
  220. package/wizard/lib/pm-config.mjs +371 -0
  221. package/wizard/lib/ports.mjs +94 -0
  222. package/wizard/lib/prompts.mjs +85 -0
  223. package/wizard/lib/provider-wizard/helpers.mjs +98 -0
  224. package/wizard/lib/provider-wizard/run-steps.mjs +129 -0
  225. package/wizard/lib/providers/registry.mjs +262 -0
  226. package/wizard/lib/run-clack.mjs +108 -0
  227. package/wizard/lib/run-wizard.mjs +750 -0
  228. package/wizard/lib/validate-setup.mjs +62 -0
  229. package/wizard/lib/wizard-action.mjs +34 -0
  230. package/wizard/lib/wizard-conf.mjs +71 -0
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Switch the active memory provider (wizard.conf + optional stack restart + MCP).
4
+ *
5
+ * npx openmem switch <hindsight|mem0|cognee> [--yes] [--dry-run] [--no-stack] [--no-mcp]
6
+ * openmem-switch-provider …
7
+ * node cli/openmem.mjs switch <id> … / ./pm switch <id> …
8
+ *
9
+ * Does not migrate data between providers. No secrets in logs.
10
+ */
11
+ import { spawnSync } from "node:child_process";
12
+ import fs from "node:fs";
13
+ import os from "node:os";
14
+ import path from "node:path";
15
+ import readline from "node:readline";
16
+ import { fileURLToPath } from "node:url";
17
+ import { isMainModule } from "./is-main.mjs";
18
+ import { resolveMemoryProvider } from "./install-memory-mcp.mjs";
19
+ import { resolveStackRoot } from "./stack.mjs";
20
+ import { envValue } from "../wizard/lib/paths.mjs";
21
+ import { updatePmConfigProvider, userConfigPath } from "../wizard/lib/pm-config.mjs";
22
+ import { listSelectableProviders, loadManifest } from "../wizard/lib/providers/registry.mjs";
23
+ import { upsertWizardConf } from "../wizard/lib/wizard-conf.mjs";
24
+ import { resolveMem0EnvFilePath, resolveMem0ApiUrl } from "../providers/mem0/lib/mcp-install.mjs";
25
+ import { envFileProblem } from "../providers/mem0/lib/state.mjs";
26
+ import { urlProblem } from "../providers/mem0/lib/url.mjs";
27
+
28
+ const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
29
+ const BIN = "openmem-switch-provider";
30
+
31
+ const HELP = `${BIN}: switch the active memory provider
32
+
33
+ Usage:
34
+ ${BIN} <provider> [flags]
35
+ npm run switch-provider -- <provider> [flags]
36
+ ./pm switch <provider> [flags]
37
+
38
+ Providers (selectable): hindsight, mem0, cognee (see providers/*/manifest.json).
39
+
40
+ Flags:
41
+ --yes no confirmation prompts
42
+ --dry-run print actions; write nothing / skip stack+MCP side effects
43
+ --no-stack skip compose down/up for the new provider
44
+ --no-mcp skip install-mcp --provider-only
45
+ -h, --help this help
46
+
47
+ Current provider: MEMORY_PROVIDER, else ~/.config/hindsight/wizard.conf, else hindsight.
48
+ No automatic data migration between providers.
49
+ `;
50
+
51
+ /**
52
+ * @typedef {{
53
+ * yes: boolean,
54
+ * dry: boolean,
55
+ * noStack: boolean,
56
+ * noMcp: boolean,
57
+ * help: boolean,
58
+ * provider: string,
59
+ * unknown: string[],
60
+ * }} SwitchProviderFlags
61
+ */
62
+
63
+ /**
64
+ * @param {string[]} argv
65
+ * @returns {SwitchProviderFlags}
66
+ */
67
+ export function parseSwitchProviderArgs(argv) {
68
+ /** @type {SwitchProviderFlags} */
69
+ const flags = {
70
+ yes: false,
71
+ dry: false,
72
+ noStack: false,
73
+ noMcp: false,
74
+ help: false,
75
+ provider: "",
76
+ unknown: [],
77
+ };
78
+ for (const arg of argv) {
79
+ switch (arg) {
80
+ case "--yes":
81
+ case "-y":
82
+ flags.yes = true;
83
+ break;
84
+ case "--dry-run":
85
+ flags.dry = true;
86
+ break;
87
+ case "--no-stack":
88
+ flags.noStack = true;
89
+ break;
90
+ case "--no-mcp":
91
+ flags.noMcp = true;
92
+ break;
93
+ case "-h":
94
+ case "--help":
95
+ flags.help = true;
96
+ break;
97
+ default:
98
+ if (arg.startsWith("-")) flags.unknown.push(arg);
99
+ else if (!flags.provider) flags.provider = arg;
100
+ else flags.unknown.push(arg);
101
+ break;
102
+ }
103
+ }
104
+ return flags;
105
+ }
106
+
107
+ /** @returns {string[]} */
108
+ export function selectableProviderIds() {
109
+ return listSelectableProviders().map((m) => m.id);
110
+ }
111
+
112
+ export { updatePmConfigProvider };
113
+
114
+ /**
115
+ * mem0 must have a usable API URL (and a valid env-file path). Never logs secrets.
116
+ * @param {{ home: string, stackRoot: string, env?: NodeJS.ProcessEnv }} opts
117
+ */
118
+ export function ensureMem0SwitchReady(opts) {
119
+ const env = opts.env || process.env;
120
+ const apiUrl = resolveMem0ApiUrl({ home: opts.home, stackRoot: opts.stackRoot, env });
121
+ const issue = urlProblem(apiUrl);
122
+ if (issue) {
123
+ throw new Error(`mem0 API URL: ${issue} — see providers/mem0/OPERATOR.md`);
124
+ }
125
+ const envFile = resolveMem0EnvFilePath({ home: opts.home, stackRoot: opts.stackRoot, env });
126
+ const fileIssue = envFileProblem(envFile);
127
+ if (fileIssue) {
128
+ throw new Error(`mem0 env file: ${fileIssue} — see providers/mem0/OPERATOR.md`);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Cognee needs a sensible COGNEE_PORT (env / stack .env / wizard.conf / default 8766).
134
+ * @param {{ home: string, stackRoot: string, env?: NodeJS.ProcessEnv }} opts
135
+ */
136
+ export function ensureCogneeSwitchReady(opts) {
137
+ const env = opts.env || process.env;
138
+ const stackEnv = path.join(opts.stackRoot, ".env");
139
+ const wizardConf = path.join(opts.home, ".config/hindsight/wizard.conf");
140
+ const candidates = [
141
+ env.COGNEE_PORT,
142
+ envValue(stackEnv, "COGNEE_PORT"),
143
+ envValue(wizardConf, "COGNEE_PORT"),
144
+ "8766",
145
+ ];
146
+ const port = candidates.map((v) => String(v || "").trim()).find(Boolean) || "";
147
+ if (!/^\d+$/.test(port) || Number(port) < 1 || Number(port) > 65535) {
148
+ throw new Error(
149
+ `COGNEE_PORT must be 1–65535 (got ${JSON.stringify(port)}) — see providers/cognee/OPERATOR.md`,
150
+ );
151
+ }
152
+ }
153
+
154
+ /** @param {{ info: Function, ok: Function, warn: Function }} h */
155
+ function makeLogger() {
156
+ return {
157
+ info: (/** @type {string} */ m) => console.log(` ${m}`),
158
+ ok: (/** @type {string} */ m) => console.log(`✓ ${m}`),
159
+ warn: (/** @type {string} */ m) => console.warn(`! ${m}`),
160
+ };
161
+ }
162
+
163
+ /**
164
+ * @param {string} question
165
+ * @returns {Promise<boolean>}
166
+ */
167
+ function confirmProceed(question) {
168
+ if (!process.stdin.isTTY) return Promise.resolve(true);
169
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
170
+ return new Promise((resolve) => {
171
+ rl.question(`${question} [Y/n] `, (answer) => {
172
+ rl.close();
173
+ const a = String(answer || "").trim().toLowerCase();
174
+ resolve(a === "" || a === "y" || a === "yes");
175
+ });
176
+ });
177
+ }
178
+
179
+ /**
180
+ * @param {string} script relative to pkgRoot
181
+ * @param {string[]} args
182
+ * @param {{ pkgRoot: string, stackRoot: string, env: NodeJS.ProcessEnv, dry: boolean, h: any, label: string }} opts
183
+ */
184
+ function spawnNodeScript(script, args, opts) {
185
+ const { pkgRoot, stackRoot, env, dry, h, label } = opts;
186
+ const abs = path.join(pkgRoot, script);
187
+ if (dry) {
188
+ h.info(`[dry-run] ${label}: node ${script} ${args.join(" ")}`);
189
+ return;
190
+ }
191
+ const r = spawnSync(process.execPath, [abs, ...args], {
192
+ cwd: stackRoot,
193
+ stdio: "inherit",
194
+ env,
195
+ });
196
+ if (r.error) throw new Error(`${label}: ${r.error.message}`);
197
+ if (r.status !== 0) throw new Error(`${label} exited ${r.status ?? "null"}`);
198
+ }
199
+
200
+ /**
201
+ * @param {{
202
+ * home: string,
203
+ * repoRoot: string,
204
+ * stackRoot: string,
205
+ * flags: SwitchProviderFlags,
206
+ * env?: NodeJS.ProcessEnv,
207
+ * h?: { info: Function, ok: Function, warn: Function },
208
+ * confirm?: (q: string) => Promise<boolean>,
209
+ * }} opts
210
+ */
211
+ export async function runSwitchMemoryProvider(opts) {
212
+ const home = opts.home;
213
+ const stackRoot = opts.stackRoot;
214
+ const repoRoot = opts.repoRoot;
215
+ const flags = opts.flags;
216
+ const env = opts.env || process.env;
217
+ const h = opts.h || makeLogger();
218
+ const ask = opts.confirm || confirmProceed;
219
+
220
+ const target = String(flags.provider || "").trim();
221
+ if (!target) {
222
+ throw new Error(`provider id required\n\n${HELP}`);
223
+ }
224
+
225
+ const selectable = selectableProviderIds();
226
+ if (!selectable.includes(target)) {
227
+ throw new Error(
228
+ `unknown or non-selectable provider "${target}" (want one of: ${selectable.join(", ")})`,
229
+ );
230
+ }
231
+
232
+ const current = resolveMemoryProvider(home, env);
233
+ h.info(`current=${current} → target=${target}`);
234
+
235
+ if (current === target) {
236
+ h.ok(`provider "${target}" is already active`);
237
+ return { current, target, same: true, aborted: false };
238
+ }
239
+
240
+ h.warn("Switching providers does not migrate memories or banks between backends.");
241
+
242
+ if (!flags.yes && !flags.dry) {
243
+ const ok = await ask(`Switch memory provider from ${current} to ${target}?`);
244
+ if (!ok) {
245
+ h.warn("aborted");
246
+ return { current, target, same: false, aborted: true };
247
+ }
248
+ }
249
+
250
+ const wizardConf = path.join(home, ".config/hindsight/wizard.conf");
251
+ const hsDir = path.join(home, ".config/hindsight");
252
+ const manifest = loadManifest(target);
253
+ const ctx = {
254
+ home,
255
+ hsDir,
256
+ wizardConf,
257
+ dry: flags.dry,
258
+ memoryProvider: manifest || { id: target },
259
+ };
260
+
261
+ if (flags.dry) {
262
+ h.info(`[dry-run] upsert MEMORY_PROVIDER=${target} in ${wizardConf}`);
263
+ } else {
264
+ upsertWizardConf(ctx, {});
265
+ h.ok(`MEMORY_PROVIDER=${target} in ${wizardConf}`);
266
+ }
267
+
268
+ const pmResult = updatePmConfigProvider(home, target, { dry: flags.dry });
269
+ if (pmResult === "updated") h.ok(`memory.provider=${target} in ${userConfigPath(home)}`);
270
+ else if (pmResult === "dry-run") h.info(`[dry-run] would update memory.provider in ${userConfigPath(home)}`);
271
+ else h.info(`${userConfigPath(home)} absent — skipped`);
272
+
273
+ const childEnv = {
274
+ ...env,
275
+ MEMORY_PROVIDER: target,
276
+ PM_STACK_ROOT: stackRoot,
277
+ PM_PACKAGE_ROOT: repoRoot,
278
+ };
279
+
280
+ if (!flags.noStack) {
281
+ if (target === "mem0") {
282
+ ensureMem0SwitchReady({ home, stackRoot, env: childEnv });
283
+ h.ok("mem0 URL/env-file path look usable");
284
+ } else if (target === "cognee") {
285
+ ensureCogneeSwitchReady({ home, stackRoot, env: childEnv });
286
+ h.ok("cognee port looks usable");
287
+ }
288
+
289
+ spawnNodeScript(
290
+ "wizard/bin/setup.mjs",
291
+ ["--down", "--yes"],
292
+ { pkgRoot: repoRoot, stackRoot, env: childEnv, dry: flags.dry, h, label: "setup --down" },
293
+ );
294
+ spawnNodeScript(
295
+ "wizard/bin/setup.mjs",
296
+ ["--up", "--yes"],
297
+ { pkgRoot: repoRoot, stackRoot, env: childEnv, dry: flags.dry, h, label: "setup --up" },
298
+ );
299
+ if (!flags.dry) h.ok(`stack restarted for provider=${target}`);
300
+ } else {
301
+ h.info("skipped stack restart (--no-stack)");
302
+ }
303
+
304
+ if (!flags.noMcp) {
305
+ const mcpArgs = ["--yes", "--provider-only"];
306
+ if (flags.dry) mcpArgs.push("--dry-run");
307
+ spawnNodeScript("cli/install-memory-mcp.mjs", mcpArgs, {
308
+ pkgRoot: repoRoot,
309
+ stackRoot,
310
+ env: childEnv,
311
+ dry: false, // dry-run is passed through to install-mcp
312
+ h,
313
+ label: "install-mcp --provider-only",
314
+ });
315
+ } else {
316
+ h.info("skipped MCP install (--no-mcp)");
317
+ }
318
+
319
+ h.ok("Restart Cursor Agent so it picks up the new provider / MCP wiring");
320
+ h.info("Then run: ./pm doctor");
321
+
322
+ return { current, target, same: false, aborted: false };
323
+ }
324
+
325
+ async function main(argv) {
326
+ const flags = parseSwitchProviderArgs(argv);
327
+ if (flags.help) {
328
+ console.log(HELP);
329
+ process.exit(0);
330
+ }
331
+ if (flags.unknown.length) {
332
+ console.error(`${BIN}: unknown option(s): ${flags.unknown.join(", ")}`);
333
+ console.error(HELP);
334
+ process.exit(2);
335
+ }
336
+ if (!flags.provider) {
337
+ console.error(`${BIN}: provider id required`);
338
+ console.error(HELP);
339
+ process.exit(2);
340
+ }
341
+
342
+ const home = process.env.HOME || os.homedir();
343
+ const stack = resolveStackRoot({ pkgRoot: PKG_ROOT, env: process.env, home });
344
+ const repoRoot = PKG_ROOT;
345
+ const stackRoot = stack.root;
346
+
347
+ try {
348
+ const result = await runSwitchMemoryProvider({ home, repoRoot, stackRoot, flags });
349
+ if (result.aborted) process.exit(1);
350
+ } catch (err) {
351
+ console.error(`${BIN}: ${err instanceof Error ? err.message : err}`);
352
+ process.exit(1);
353
+ }
354
+ }
355
+
356
+ if (isMainModule(import.meta.url)) {
357
+ main(process.argv.slice(2));
358
+ }
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ /** Agent wiring report: provider, API URLs, MCP entries vs expectations. */
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { isMainModule } from "./is-main.mjs";
7
+ import { PROVIDER_CAPABILITIES } from "./coding-agent/capabilities.mjs";
8
+ import { resolveMcpContext } from "./mcp/context.mjs";
9
+ import { resolveMemoryProvider, shouldInstallNotebooklm } from "./install-memory-mcp.mjs";
10
+ import { resolveStackRoot } from "./stack.mjs";
11
+
12
+ const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
13
+
14
+ /**
15
+ * @param {{ home: string, stackRoot: string, repoRoot: string, env?: NodeJS.ProcessEnv }} opts
16
+ */
17
+ export function buildWiringReport(opts) {
18
+ const home = opts.home;
19
+ const env = opts.env || process.env;
20
+ const provider = resolveMemoryProvider(home, env);
21
+ const ctx = resolveMcpContext({ ...opts, home, env });
22
+ const mcpPath = path.join(home, ".cursor/mcp.json");
23
+ /** @type {Record<string, unknown>} */
24
+ let mcp = {};
25
+ if (fs.existsSync(mcpPath)) {
26
+ try {
27
+ mcp = JSON.parse(fs.readFileSync(mcpPath, "utf8"));
28
+ } catch {
29
+ mcp = { _parseError: true };
30
+ }
31
+ }
32
+ const servers = mcp.mcpServers && typeof mcp.mcpServers === "object" ? mcp.mcpServers : {};
33
+ const names = Object.keys(servers);
34
+ const expectUnified = true;
35
+ const expectProviderNative = provider === "mem0" || provider === "cognee";
36
+ const nlm = shouldInstallNotebooklm({
37
+ home,
38
+ flags: { notebooklm: false, notebooklmOnly: false, providerOnly: false },
39
+ });
40
+ const issues = [];
41
+ if (expectUnified && !servers["openmem"]) {
42
+ issues.push("missing unified MCP server `openmem` in ~/.cursor/mcp.json (run install-mcp)");
43
+ }
44
+ if (expectProviderNative && !servers[provider]) {
45
+ issues.push(`active provider is ${provider} but mcp.json has no \`${provider}\` entry (optional: install-mcp --provider-only)`);
46
+ }
47
+ if (provider === "hindsight" && servers.mem0) {
48
+ issues.push("MEMORY_PROVIDER is hindsight but mcp.json still has mem0 (stale after switch?)");
49
+ }
50
+ if (provider === "mem0" && servers.hindsight && servers.hindsight.command !== "keep") {
51
+ /* hindsight key often kept intentionally */
52
+ }
53
+ const manifestPath = path.join(opts.repoRoot, "providers", provider, "manifest.json");
54
+ let sinkDriver = null;
55
+ if (fs.existsSync(manifestPath)) {
56
+ try {
57
+ sinkDriver = JSON.parse(fs.readFileSync(manifestPath, "utf8"))?.indexer?.sinkDriver ?? null;
58
+ } catch {
59
+ sinkDriver = null;
60
+ }
61
+ }
62
+ return {
63
+ memory_provider: provider,
64
+ api: {
65
+ hindsightUrl: ctx.hindsightUrl ?? null,
66
+ mem0BaseUrl: ctx.mem0?.baseUrl ?? null,
67
+ cogneeBaseUrl: ctx.cognee?.baseUrl ?? null,
68
+ },
69
+ indexer: { sink_driver: sinkDriver },
70
+ mcp: {
71
+ path: mcpPath,
72
+ servers: names,
73
+ notebooklm_recommended: nlm,
74
+ has_notebooklm: Boolean(servers.notebooklm),
75
+ },
76
+ coding_agent: {
77
+ hindsight_plugin: provider === "hindsight" ? "@vectorize-io/hindsight-coding-agents" : null,
78
+ non_hindsight_session:
79
+ provider !== "hindsight" ? "openmem session shim (install-coding-agent) + memory_* MCP" : null,
80
+ session_capabilities: PROVIDER_CAPABILITIES[provider] ?? {},
81
+ },
82
+ issues,
83
+ ok: issues.length === 0,
84
+ };
85
+ }
86
+
87
+ function main() {
88
+ const json = process.argv.includes("--json");
89
+ const home = process.env.HOME || "";
90
+ const sr = resolveStackRoot({ pkgRoot: PKG_ROOT });
91
+ const report = buildWiringReport({ home, stackRoot: sr.root, repoRoot: PKG_ROOT, env: process.env });
92
+ if (json) {
93
+ console.log(JSON.stringify(report, null, 2));
94
+ process.exit(report.ok ? 0 : 1);
95
+ }
96
+ console.log(`Memory provider: ${report.memory_provider}`);
97
+ if (report.api.hindsightUrl) console.log(` Hindsight: ${report.api.hindsightUrl}`);
98
+ if (report.api.mem0BaseUrl) console.log(` mem0: ${report.api.mem0BaseUrl}`);
99
+ if (report.api.cogneeBaseUrl) console.log(` Cognee: ${report.api.cogneeBaseUrl}`);
100
+ if (report.indexer.sink_driver) console.log(` Indexer sink: ${report.indexer.sink_driver}`);
101
+ console.log(`MCP (${report.mcp.path}): ${report.mcp.servers.join(", ") || "(none)"}`);
102
+ if (report.mcp.notebooklm_recommended && !report.mcp.has_notebooklm) {
103
+ console.log(" NotebookLM sync enabled but notebooklm MCP not in mcp.json");
104
+ }
105
+ if (report.coding_agent.hindsight_plugin) {
106
+ console.log(`Session plugin: ${report.coding_agent.hindsight_plugin}`);
107
+ } else if (report.coding_agent.non_hindsight_session) {
108
+ console.log(`Session memory: ${report.coding_agent.non_hindsight_session}`);
109
+ }
110
+ const caps = report.coding_agent.session_capabilities;
111
+ if (caps && Object.keys(caps).length) {
112
+ const on = Object.entries(caps)
113
+ .filter(([, v]) => v)
114
+ .map(([k]) => k);
115
+ const off = Object.entries(caps)
116
+ .filter(([, v]) => !v)
117
+ .map(([k]) => k);
118
+ if (on.length) console.log(` Session features: ${on.join(", ")}`);
119
+ if (off.length) console.log(` Unavailable for ${report.memory_provider}: ${off.join(", ")}`);
120
+ }
121
+ for (const i of report.issues) console.log(`Issue: ${i}`);
122
+ process.exit(report.ok ? 0 : 1);
123
+ }
124
+
125
+ if (isMainModule(import.meta.url)) main();
@@ -0,0 +1,12 @@
1
+ # Bench overlay: also publish Hindsight on the docker0 gateway so bridge-network
2
+ # fabro sandboxes reach it via host.docker.internal:8888. Loopback binding is kept.
3
+ # Needs the hindsight profile (COMPOSE_PROFILES=hindsight in .env, or add --profile hindsight).
4
+ # Not applied. Apply by recreating only the hindsight service:
5
+ # cd ~/openmem && docker compose -f compose.yaml -f compose.override.yaml -f compose.bench.yaml up -d hindsight
6
+ # The explicit -f list keeps compose.override.yaml, which compose skips when -f is given.
7
+ services:
8
+ hindsight:
9
+ ports: !override
10
+ - "127.0.0.1:${HINDSIGHT_PORT:-8888}:8888"
11
+ - "127.0.0.1:${HINDSIGHT_CP_PORT:-9999}:9999"
12
+ - "172.17.0.1:${HINDSIGHT_PORT:-8888}:8888"