@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.
- package/.agents/skills/openmem/SKILL.md +34 -0
- package/LICENSE +190 -0
- package/README.md +140 -2
- package/bin/hindsight-project-index +530 -0
- package/cli/coding-agent/access-policy.mjs +38 -0
- package/cli/coding-agent/agents.mjs +34 -0
- package/cli/coding-agent/bank-from-cwd.mjs +140 -0
- package/cli/coding-agent/capabilities.mjs +34 -0
- package/cli/coding-agent/context-store.mjs +81 -0
- package/cli/coding-agent/context-trim.mjs +897 -0
- package/cli/coding-agent/file-io.mjs +72 -0
- package/cli/coding-agent/hook-harnesses.mjs +88 -0
- package/cli/coding-agent/hook-runner.mjs +284 -0
- package/cli/coding-agent/install-access.mjs +119 -0
- package/cli/coding-agent/install-context-proxy.mjs +333 -0
- package/cli/coding-agent/install-rtk.mjs +60 -0
- package/cli/coding-agent/install-shim.mjs +201 -0
- package/cli/coding-agent/opencode-config.mjs +107 -0
- package/cli/coding-agent/opencode-plugin.mjs +132 -0
- package/cli/coding-agent/opencode2-plugin/index.js +299 -0
- package/cli/coding-agent/recall-frame.mjs +29 -0
- package/cli/coding-agent/remove-upstream-access.mjs +122 -0
- package/cli/coding-agent/runtime.mjs +40 -0
- package/cli/coding-agent/session-db.mjs +626 -0
- package/cli/coding-agent/session-facade.mjs +86 -0
- package/cli/coding-agent/toml-edit.mjs +161 -0
- package/cli/cognee-mcp.mjs +260 -0
- package/cli/context-management.mjs +254 -0
- package/cli/feed-sync.mjs +27 -0
- package/cli/install-coding-agent.mjs +298 -0
- package/cli/install-memory-mcp.mjs +426 -0
- package/cli/is-main.mjs +18 -0
- package/cli/mcp/context-sql-runner.mjs +58 -0
- package/cli/mcp/context-store.mjs +670 -0
- package/cli/mcp/context.mjs +61 -0
- package/cli/mcp/export/cognee-dataset-name.mjs +12 -0
- package/cli/mcp/export/cognee.mjs +119 -0
- package/cli/mcp/export/hindsight.mjs +48 -0
- package/cli/mcp/export/index.mjs +26 -0
- package/cli/mcp/export/mem0.mjs +31 -0
- package/cli/mcp/knowledge.mjs +333 -0
- package/cli/mcp/llm-synthesize.mjs +50 -0
- package/cli/mcp/mem0-scope-id.mjs +22 -0
- package/cli/mcp/memory-facade.mjs +521 -0
- package/cli/mcp/memory-reflect-consolidate.mjs +120 -0
- package/cli/mcp/notebooklm-banks.mjs +21 -0
- package/cli/mcp/notebooklm-hindsight-rest.mjs +45 -0
- package/cli/mcp/notebooklm-provider.mjs +225 -0
- package/cli/mcp/pmm-records.mjs +23 -0
- package/cli/mcp/portability.mjs +158 -0
- package/cli/mcp/stdio.mjs +65 -0
- package/cli/mcp/unified-mcp-install.mjs +65 -0
- package/cli/mem0-mcp.mjs +108 -0
- package/cli/notebooklm-mcp.mjs +236 -0
- package/cli/openmem-mcp.mjs +38 -0
- package/cli/openmem.mjs +222 -0
- package/cli/roots.mjs +85 -0
- package/cli/schedule.mjs +176 -0
- package/cli/stack.mjs +438 -0
- package/cli/switch-memory-provider.mjs +358 -0
- package/cli/wiring-report.mjs +125 -0
- package/compose.bench.yaml +12 -0
- package/compose.yaml +227 -0
- package/config/coding-agent.json +13 -0
- package/config/hindsight.env.example +26 -0
- package/config/index.yml +54 -0
- package/config/litellm.lemond.yaml +40 -0
- package/config/litellm.ollama.yaml +33 -0
- package/config/notebooklm.yml +33 -0
- package/config/openmem.toml.example +124 -0
- package/docs/OPENMEM.md +23 -0
- package/docs/architecture.md +245 -0
- package/docs/assets/README.md +10 -0
- package/docs/assets/setup-demo.gif +0 -0
- package/docs/coding-agent-hooks.md +75 -0
- package/docs/coding-agent-scopes.md +22 -0
- package/docs/coding-agents.md +71 -0
- package/docs/cognee.md +37 -0
- package/docs/compatibility.md +80 -0
- package/docs/concepts.md +135 -0
- package/docs/context-management.md +255 -0
- package/docs/context-proxy.md +115 -0
- package/docs/context-research.md +215 -0
- package/docs/context-store.md +277 -0
- package/docs/hindsight.md +77 -0
- package/docs/index-sources-roadmap.md +38 -0
- package/docs/index-sources.md +39 -0
- package/docs/indexer-sinks.md +122 -0
- package/docs/indexing.md +57 -0
- package/docs/manual-stack-setup.md +74 -0
- package/docs/mcp-install-guide.md +4 -0
- package/docs/mcp-setup.md +104 -0
- package/docs/mem0.md +141 -0
- package/docs/memory-portability.md +73 -0
- package/docs/memory-providers.md +246 -0
- package/docs/meridian-litellm.md +198 -0
- package/docs/model-configuration.md +106 -0
- package/docs/notebooklm.md +83 -0
- package/docs/npx-install.md +172 -0
- package/docs/openmem-config.md +194 -0
- package/docs/provider-wizard-hooks.md +222 -0
- package/docs/quickstart.md +107 -0
- package/docs/setup-wizard.md +117 -0
- package/docs/sources-feeds.md +39 -0
- package/docs/sources-readwise.md +23 -0
- package/docs/sources-zotero.md +22 -0
- package/glama.json +4 -0
- package/indexer/Dockerfile +38 -0
- package/indexer/code_outline.py +146 -0
- package/indexer/feed_sync.py +107 -0
- package/indexer/index_config.py +290 -0
- package/indexer/pytest.ini +5 -0
- package/indexer/sinks/__init__.py +148 -0
- package/indexer/sinks/_http.py +44 -0
- package/indexer/sinks/cognee.py +329 -0
- package/indexer/sinks/hindsight.py +107 -0
- package/indexer/sinks/mem0.py +247 -0
- package/indexer/watch.py +240 -0
- package/package.json +96 -4
- package/providers/_template/compose.yaml +52 -0
- package/providers/cognee/OPERATOR.md +82 -0
- package/providers/cognee/compose.yaml +89 -0
- package/providers/cognee/lib/clack.mjs +157 -0
- package/providers/cognee/lib/coding-agent.mjs +26 -0
- package/providers/cognee/lib/cognee-mcp-install.mjs +153 -0
- package/providers/cognee/lib/config.mjs +98 -0
- package/providers/cognee/lib/env-file.mjs +194 -0
- package/providers/cognee/lib/env-setup.mjs +83 -0
- package/providers/cognee/lib/llm-env.mjs +382 -0
- package/providers/cognee/lib/ports.mjs +25 -0
- package/providers/cognee/lib/smoke.mjs +246 -0
- package/providers/cognee/lib/stack-env.mjs +134 -0
- package/providers/cognee/lib/state.mjs +101 -0
- package/providers/cognee/lib/summary.mjs +56 -0
- package/providers/cognee/lib/validate.mjs +97 -0
- package/providers/cognee/manifest.json +56 -0
- package/providers/cognee/wizard.mjs +134 -0
- package/providers/graphiti/manifest.json +15 -0
- package/providers/hindsight/lib/clack.mjs +347 -0
- package/providers/hindsight/lib/coding-agent.mjs +204 -0
- package/providers/hindsight/lib/config.mjs +79 -0
- package/providers/hindsight/lib/env-setup.mjs +72 -0
- package/providers/hindsight/lib/litellm-routes.mjs +196 -0
- package/providers/hindsight/lib/meridian.mjs +307 -0
- package/providers/hindsight/lib/model-backend.mjs +243 -0
- package/providers/hindsight/lib/notebooklm-mcp-install.mjs +127 -0
- package/providers/hindsight/lib/ollama-compose.mjs +51 -0
- package/providers/hindsight/lib/ports.mjs +30 -0
- package/providers/hindsight/lib/probe.mjs +54 -0
- package/providers/hindsight/lib/stack-env.mjs +125 -0
- package/providers/hindsight/lib/state.mjs +80 -0
- package/providers/hindsight/lib/summary.mjs +57 -0
- package/providers/hindsight/lib/validate.mjs +81 -0
- package/providers/hindsight/manifest.json +58 -0
- package/providers/hindsight/wizard.mjs +150 -0
- package/providers/letta/manifest.json +29 -0
- package/providers/mem0/OPERATOR.md +143 -0
- package/providers/mem0/compose.yaml +79 -0
- package/providers/mem0/lib/coding-agent.mjs +58 -0
- package/providers/mem0/lib/config.mjs +45 -0
- package/providers/mem0/lib/configure.mjs +201 -0
- package/providers/mem0/lib/dotenv.mjs +71 -0
- package/providers/mem0/lib/env-file.mjs +110 -0
- package/providers/mem0/lib/http.mjs +69 -0
- package/providers/mem0/lib/litellm.mjs +457 -0
- package/providers/mem0/lib/mcp-install.mjs +165 -0
- package/providers/mem0/lib/secret.mjs +30 -0
- package/providers/mem0/lib/smoke.mjs +179 -0
- package/providers/mem0/lib/stack-env.mjs +255 -0
- package/providers/mem0/lib/state.mjs +67 -0
- package/providers/mem0/lib/summary.mjs +88 -0
- package/providers/mem0/lib/url.mjs +106 -0
- package/providers/mem0/lib/validate.mjs +158 -0
- package/providers/mem0/manifest.json +47 -0
- package/providers/mem0/wizard.mjs +146 -0
- package/providers/zep/manifest.json +29 -0
- package/proxy/config.yaml +33 -0
- package/proxy/openmem_context/__init__.py +6 -0
- package/proxy/openmem_context/adapters.py +504 -0
- package/proxy/openmem_context/compress.py +53 -0
- package/proxy/openmem_context/core.py +606 -0
- package/proxy/openmem_context/embed.py +212 -0
- package/proxy/openmem_context/recall.py +49 -0
- package/proxy/openmem_context/session.py +70 -0
- package/proxy/openmem_context/sessiondb.py +276 -0
- package/proxy/openmem_context/settings.py +134 -0
- package/proxy/openmem_context/store.py +129 -0
- package/proxy/openmem_context/tokens.py +123 -0
- package/proxy/openmem_context/ttl.py +78 -0
- package/proxy/openmem_context_hook.py +34 -0
- package/scripts/check-pack.mjs +219 -0
- package/scripts/gen-masks.mjs +83 -0
- package/scripts/gen-masks.sh +4 -0
- package/scripts/promotion-provider-smoke.mjs +34 -0
- package/scripts/run-setup.sh +30 -0
- package/scripts/setup.mjs +107 -0
- package/scripts/setup.sh +5 -0
- package/scripts/spin-up.sh +45 -0
- package/scripts/wizard.sh +5 -0
- package/server.json +34 -0
- package/sync/Dockerfile +27 -0
- package/sync/memory_backend.py +87 -0
- package/sync/notebooklm_sync.py +664 -0
- package/wizard/bin/pm.mjs +97 -0
- package/wizard/bin/setup.mjs +33 -0
- package/wizard/lib/compose-version.mjs +32 -0
- package/wizard/lib/compose.mjs +70 -0
- package/wizard/lib/context-step.mjs +140 -0
- package/wizard/lib/curl.mjs +103 -0
- package/wizard/lib/docker-build.mjs +148 -0
- package/wizard/lib/exec.mjs +41 -0
- package/wizard/lib/indexer.mjs +6 -0
- package/wizard/lib/litellm-env.mjs +55 -0
- package/wizard/lib/load-machine-env.mjs +4 -0
- package/wizard/lib/log.mjs +32 -0
- package/wizard/lib/notebooklm-agent-access.mjs +92 -0
- package/wizard/lib/notebooklm.mjs +214 -0
- package/wizard/lib/parse-args.mjs +87 -0
- package/wizard/lib/paths.mjs +39 -0
- package/wizard/lib/pm-config.mjs +371 -0
- package/wizard/lib/ports.mjs +94 -0
- package/wizard/lib/prompts.mjs +85 -0
- package/wizard/lib/provider-wizard/helpers.mjs +98 -0
- package/wizard/lib/provider-wizard/run-steps.mjs +129 -0
- package/wizard/lib/providers/registry.mjs +262 -0
- package/wizard/lib/run-clack.mjs +108 -0
- package/wizard/lib/run-wizard.mjs +750 -0
- package/wizard/lib/validate-setup.mjs +62 -0
- package/wizard/lib/wizard-action.mjs +34 -0
- package/wizard/lib/wizard-conf.mjs +71 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Install memory-related MCP servers into ~/.cursor/mcp.json only.
|
|
4
|
+
*
|
|
5
|
+
* openmem-install-mcp [--yes] [--dry-run] [--notebooklm] [--notebooklm-only] [--provider-only]
|
|
6
|
+
* npm run install-mcp -- …
|
|
7
|
+
*
|
|
8
|
+
* Provider: MEMORY_PROVIDER → ~/.config/hindsight/wizard.conf → hindsight.
|
|
9
|
+
* NotebookLM MCP: notebooklm.yml enabled:true and/or --notebooklm (unless --provider-only).
|
|
10
|
+
* Never writes machine paths into the shipped config/coding-agent.json template.
|
|
11
|
+
*/
|
|
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 { setNotebooklmAccessEnabled } from "../wizard/lib/notebooklm-agent-access.mjs";
|
|
19
|
+
import { envValue } from "../wizard/lib/paths.mjs";
|
|
20
|
+
import { installNotebooklmMcp } from "../providers/hindsight/lib/notebooklm-mcp-install.mjs";
|
|
21
|
+
import {
|
|
22
|
+
cogneeMcpEntry,
|
|
23
|
+
installCogneeMcp as installCogneeMcpFromProvider,
|
|
24
|
+
resolveCogneeApiUrl,
|
|
25
|
+
} from "../providers/cognee/lib/cognee-mcp-install.mjs";
|
|
26
|
+
import {
|
|
27
|
+
installMem0Mcp as installMem0McpFromProvider,
|
|
28
|
+
mem0McpEntry,
|
|
29
|
+
mem0McpScriptPath,
|
|
30
|
+
mergeMem0McpServer,
|
|
31
|
+
resolveMem0ApiUrl,
|
|
32
|
+
} from "../providers/mem0/lib/mcp-install.mjs";
|
|
33
|
+
import { installUnifiedMcp } from "./mcp/unified-mcp-install.mjs";
|
|
34
|
+
import { resolveStackRoot } from "./stack.mjs";
|
|
35
|
+
|
|
36
|
+
export { mem0McpEntry, mergeMem0McpServer, resolveMem0ApiUrl };
|
|
37
|
+
|
|
38
|
+
const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
39
|
+
const BIN = "openmem-install-mcp";
|
|
40
|
+
const DEFAULT_PROVIDER = "hindsight";
|
|
41
|
+
/** Sibling MCP servers this tool must never rewrite (merge targets mem0/cognee stay off this list). */
|
|
42
|
+
export const PROTECTED_MCP_SERVERS = Object.freeze(["hindsight", "codegraph", "notebooklm"]);
|
|
43
|
+
|
|
44
|
+
const HELP = `${BIN}: merge MCP servers into ~/.cursor/mcp.json
|
|
45
|
+
|
|
46
|
+
By default merges the openmem unified MCP (memory_add/search/recall/forget on the
|
|
47
|
+
active provider). Also NotebookLM when sync is enabled or --notebooklm. Native mem0/Cognee
|
|
48
|
+
MCP: --provider-only. Skip unified: --no-unified-mcp. Hindsight session memory still uses
|
|
49
|
+
the hindsight-coding-agents plugin.
|
|
50
|
+
|
|
51
|
+
Usage:
|
|
52
|
+
${BIN} [flags]
|
|
53
|
+
npx openmem install-mcp [flags]
|
|
54
|
+
npm run install-mcp -- [flags] # checkout
|
|
55
|
+
|
|
56
|
+
Flags:
|
|
57
|
+
--yes no confirmation prompts
|
|
58
|
+
--dry-run print actions; write nothing
|
|
59
|
+
--notebooklm force NotebookLM MCP + notebooklmAccess.enabled
|
|
60
|
+
--notebooklm-only only NotebookLM step (skip provider MCP)
|
|
61
|
+
--provider-only only provider MCP (skip NotebookLM)
|
|
62
|
+
--no-unified-mcp skip merging the openmem unified MCP server
|
|
63
|
+
-h, --help this help
|
|
64
|
+
|
|
65
|
+
Provider resolution: MEMORY_PROVIDER, else wizard.conf, else hindsight.
|
|
66
|
+
NotebookLM is also detected when ~/.config/hindsight/notebooklm.yml has enabled: true.
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @typedef {{
|
|
71
|
+
* yes: boolean,
|
|
72
|
+
* dry: boolean,
|
|
73
|
+
* notebooklm: boolean,
|
|
74
|
+
* notebooklmOnly: boolean,
|
|
75
|
+
* providerOnly: boolean,
|
|
76
|
+
* noUnifiedMcp: boolean,
|
|
77
|
+
* help: boolean,
|
|
78
|
+
* unknown: string[],
|
|
79
|
+
* }} InstallMcpFlags
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {string[]} argv
|
|
84
|
+
* @returns {InstallMcpFlags}
|
|
85
|
+
*/
|
|
86
|
+
export function parseInstallMcpArgs(argv) {
|
|
87
|
+
/** @type {InstallMcpFlags} */
|
|
88
|
+
const flags = {
|
|
89
|
+
yes: false,
|
|
90
|
+
dry: false,
|
|
91
|
+
notebooklm: false,
|
|
92
|
+
notebooklmOnly: false,
|
|
93
|
+
providerOnly: false,
|
|
94
|
+
noUnifiedMcp: false,
|
|
95
|
+
help: false,
|
|
96
|
+
unknown: [],
|
|
97
|
+
};
|
|
98
|
+
for (const arg of argv) {
|
|
99
|
+
switch (arg) {
|
|
100
|
+
case "--yes":
|
|
101
|
+
case "-y":
|
|
102
|
+
flags.yes = true;
|
|
103
|
+
break;
|
|
104
|
+
case "--dry-run":
|
|
105
|
+
flags.dry = true;
|
|
106
|
+
break;
|
|
107
|
+
case "--notebooklm":
|
|
108
|
+
flags.notebooklm = true;
|
|
109
|
+
break;
|
|
110
|
+
case "--notebooklm-only":
|
|
111
|
+
flags.notebooklmOnly = true;
|
|
112
|
+
break;
|
|
113
|
+
case "--provider-only":
|
|
114
|
+
flags.providerOnly = true;
|
|
115
|
+
break;
|
|
116
|
+
case "--no-unified-mcp":
|
|
117
|
+
flags.noUnifiedMcp = true;
|
|
118
|
+
break;
|
|
119
|
+
case "-h":
|
|
120
|
+
case "--help":
|
|
121
|
+
flags.help = true;
|
|
122
|
+
break;
|
|
123
|
+
default:
|
|
124
|
+
flags.unknown.push(arg);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return flags;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {string} home
|
|
133
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
134
|
+
* @returns {string}
|
|
135
|
+
*/
|
|
136
|
+
export function resolveMemoryProvider(home, env = process.env) {
|
|
137
|
+
const fromEnv = String(env.MEMORY_PROVIDER || "").trim();
|
|
138
|
+
if (fromEnv) return fromEnv;
|
|
139
|
+
const conf = path.join(home, ".config/hindsight/wizard.conf");
|
|
140
|
+
const fromConf = envValue(conf, "MEMORY_PROVIDER").trim();
|
|
141
|
+
if (fromConf) return fromConf;
|
|
142
|
+
return DEFAULT_PROVIDER;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* True when notebooklm.yml has a top-level `enabled: true` (same regex as the wizard).
|
|
147
|
+
* @param {string} notebookFile
|
|
148
|
+
*/
|
|
149
|
+
export function notebooklmEnabledInYml(notebookFile) {
|
|
150
|
+
if (!fs.existsSync(notebookFile)) return false;
|
|
151
|
+
try {
|
|
152
|
+
return /^enabled:\s*true\b/m.test(fs.readFileSync(notebookFile, "utf8"));
|
|
153
|
+
} catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {{ home: string, flags: InstallMcpFlags }} opts
|
|
160
|
+
*/
|
|
161
|
+
export function shouldInstallNotebooklm({ home, flags }) {
|
|
162
|
+
if (flags.providerOnly) return false;
|
|
163
|
+
if (flags.notebooklm || flags.notebooklmOnly) return true;
|
|
164
|
+
return notebooklmEnabledInYml(path.join(home, ".config/hindsight/notebooklm.yml"));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Merge one named MCP server; refuse to mutate protected sibling keys.
|
|
169
|
+
* @param {Record<string, any>} mcp
|
|
170
|
+
* @param {string} name
|
|
171
|
+
* @param {Record<string, any>} entry
|
|
172
|
+
* @param {{ protect?: string[] }} [opts]
|
|
173
|
+
* @returns {{ mcp: Record<string, any>, changed: boolean, skipped?: string, refused?: string }}
|
|
174
|
+
*/
|
|
175
|
+
export function mergeNamedMcpServer(mcp, name, entry, opts = {}) {
|
|
176
|
+
const protect = opts.protect || PROTECTED_MCP_SERVERS;
|
|
177
|
+
if (protect.includes(name) && name !== "notebooklm") {
|
|
178
|
+
// notebooklm is updated only via mergeNotebooklmMcpServer / installNotebooklmMcp.
|
|
179
|
+
return { mcp, changed: false, refused: `refusing to merge protected MCP name "${name}"` };
|
|
180
|
+
}
|
|
181
|
+
const out = { ...mcp };
|
|
182
|
+
const servers = { ...(mcp.mcpServers && typeof mcp.mcpServers === "object" ? mcp.mcpServers : {}) };
|
|
183
|
+
const before = Object.fromEntries(protect.map((k) => [k, servers[k]]));
|
|
184
|
+
const prev = servers[name];
|
|
185
|
+
const same = prev && JSON.stringify(prev) === JSON.stringify(entry);
|
|
186
|
+
if (same) {
|
|
187
|
+
return { mcp: { ...out, mcpServers: servers }, changed: false, skipped: "already configured" };
|
|
188
|
+
}
|
|
189
|
+
servers[name] = entry;
|
|
190
|
+
for (const k of protect) {
|
|
191
|
+
if (k === name) continue;
|
|
192
|
+
if (before[k] !== undefined && servers[k] !== before[k]) {
|
|
193
|
+
return { mcp, changed: false, refused: `refusing to alter ${k} MCP entry` };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return { mcp: { ...out, mcpServers: servers }, changed: true };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Back-compat alias: real mem0 MCP entry (node launcher → npx @yeyuan98/mem0-mcp).
|
|
201
|
+
* Prefer mem0McpEntry({ apiUrl, scriptPath, envFile }) for new code.
|
|
202
|
+
* @param {string} apiUrl
|
|
203
|
+
*/
|
|
204
|
+
export function mem0McpPlaceholderEntry(apiUrl) {
|
|
205
|
+
return mem0McpEntry({
|
|
206
|
+
apiUrl,
|
|
207
|
+
scriptPath: mem0McpScriptPath(PKG_ROOT),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Cognee stdio MCP entry (node + script + COGNEE_API_URL). No API key.
|
|
213
|
+
* @param {string} scriptPath absolute path
|
|
214
|
+
* @param {string} [apiUrl]
|
|
215
|
+
*/
|
|
216
|
+
export function cogneeMcpStubEntry(scriptPath, apiUrl) {
|
|
217
|
+
return cogneeMcpEntry(scriptPath, apiUrl);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @param {string} mcpPath
|
|
222
|
+
* @returns {Record<string, any>}
|
|
223
|
+
*/
|
|
224
|
+
export function readMcpJson(mcpPath) {
|
|
225
|
+
if (!fs.existsSync(mcpPath)) return {};
|
|
226
|
+
return JSON.parse(fs.readFileSync(mcpPath, "utf8"));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {string} mcpPath
|
|
231
|
+
* @param {Record<string, any>} mcp
|
|
232
|
+
* @param {{ dry?: boolean }} [opts]
|
|
233
|
+
*/
|
|
234
|
+
export function writeMcpJson(mcpPath, mcp, opts = {}) {
|
|
235
|
+
if (opts.dry) return;
|
|
236
|
+
fs.mkdirSync(path.dirname(mcpPath), { recursive: true });
|
|
237
|
+
fs.writeFileSync(mcpPath, `${JSON.stringify(mcp, null, 2)}\n`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** @param {{ info: Function, ok: Function, warn: Function }} h */
|
|
241
|
+
function makeLogger() {
|
|
242
|
+
return {
|
|
243
|
+
info: (/** @type {string} */ m) => console.log(` ${m}`),
|
|
244
|
+
ok: (/** @type {string} */ m) => console.log(`✓ ${m}`),
|
|
245
|
+
warn: (/** @type {string} */ m) => console.warn(`! ${m}`),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* @param {string} question
|
|
251
|
+
* @returns {Promise<boolean>}
|
|
252
|
+
*/
|
|
253
|
+
function confirmProceed(question) {
|
|
254
|
+
if (!process.stdin.isTTY) return Promise.resolve(true);
|
|
255
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
256
|
+
return new Promise((resolve) => {
|
|
257
|
+
rl.question(`${question} [Y/n] `, (answer) => {
|
|
258
|
+
rl.close();
|
|
259
|
+
const a = String(answer || "").trim().toLowerCase();
|
|
260
|
+
resolve(a === "" || a === "y" || a === "yes");
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @param {{
|
|
267
|
+
* home: string,
|
|
268
|
+
* repoRoot: string,
|
|
269
|
+
* stackRoot: string,
|
|
270
|
+
* flags: InstallMcpFlags,
|
|
271
|
+
* env?: NodeJS.ProcessEnv,
|
|
272
|
+
* h?: { info: Function, ok: Function, warn: Function },
|
|
273
|
+
* confirm?: (q: string) => Promise<boolean>,
|
|
274
|
+
* }} opts
|
|
275
|
+
*/
|
|
276
|
+
export async function runInstallMemoryMcp(opts) {
|
|
277
|
+
const home = opts.home;
|
|
278
|
+
const repoRoot = opts.repoRoot;
|
|
279
|
+
const stackRoot = opts.stackRoot;
|
|
280
|
+
const flags = opts.flags;
|
|
281
|
+
const env = opts.env || process.env;
|
|
282
|
+
const h = opts.h || makeLogger();
|
|
283
|
+
const ask = opts.confirm || confirmProceed;
|
|
284
|
+
|
|
285
|
+
if (flags.notebooklmOnly && flags.providerOnly) {
|
|
286
|
+
throw new Error("--notebooklm-only and --provider-only are mutually exclusive");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const provider = resolveMemoryProvider(home, env);
|
|
290
|
+
const doNotebooklm = shouldInstallNotebooklm({ home, flags });
|
|
291
|
+
const doProvider = !flags.notebooklmOnly;
|
|
292
|
+
|
|
293
|
+
const plan = [];
|
|
294
|
+
if (doNotebooklm) plan.push("notebooklmAccess + notebooklm MCP");
|
|
295
|
+
if (doProvider) {
|
|
296
|
+
if (provider === "mem0") plan.push("mem0 MCP (@yeyuan98/mem0-mcp via cli/mem0-mcp.mjs)");
|
|
297
|
+
else if (provider === "cognee") plan.push("cognee MCP (read-only health/search)");
|
|
298
|
+
else if (provider === "hindsight") plan.push("hindsight note (coding-agents plugin separate)");
|
|
299
|
+
else plan.push(`provider=${provider} (no MCP handler)`);
|
|
300
|
+
}
|
|
301
|
+
if (!flags.noUnifiedMcp) plan.push("openmem unified MCP (memory_add/search/recall/forget)");
|
|
302
|
+
|
|
303
|
+
h.info(`provider=${provider}`);
|
|
304
|
+
h.info(`plan: ${plan.length ? plan.join("; ") : "(nothing)"}`);
|
|
305
|
+
|
|
306
|
+
if (!plan.length) {
|
|
307
|
+
h.info("nothing to install");
|
|
308
|
+
return { provider, didNotebooklm: false, didProvider: false };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!flags.yes && !flags.dry) {
|
|
312
|
+
const ok = await ask(`Apply MCP changes to ${path.join(home, ".cursor/mcp.json")}?`);
|
|
313
|
+
if (!ok) {
|
|
314
|
+
h.warn("aborted");
|
|
315
|
+
return { provider, didNotebooklm: false, didProvider: false, aborted: true };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
let didNotebooklm = false;
|
|
320
|
+
if (doNotebooklm) {
|
|
321
|
+
const agentPath = path.join(home, ".hindsight", "coding-agent.json");
|
|
322
|
+
const templatePath = path.join(repoRoot, "config/coding-agent.json");
|
|
323
|
+
if (flags.dry) {
|
|
324
|
+
const planAccess = setNotebooklmAccessEnabled(agentPath, templatePath, true, { dry: true });
|
|
325
|
+
h.info(
|
|
326
|
+
`[dry-run] ${planAccess.action} ${agentPath} with notebooklmAccess.enabled=true; merge notebooklm MCP into ~/.cursor/mcp.json`,
|
|
327
|
+
);
|
|
328
|
+
} else {
|
|
329
|
+
const result = setNotebooklmAccessEnabled(agentPath, templatePath, true);
|
|
330
|
+
h.ok(
|
|
331
|
+
`notebooklmAccess.enabled=true in ${agentPath}` +
|
|
332
|
+
(result.action === "materialized" ? " (copied from symlink → regular file)" : ""),
|
|
333
|
+
);
|
|
334
|
+
await installNotebooklmMcp({ home, repoRoot, agentJson: agentPath, dry: false }, h);
|
|
335
|
+
}
|
|
336
|
+
didNotebooklm = true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let didProvider = false;
|
|
340
|
+
if (doProvider) {
|
|
341
|
+
if (provider === "hindsight") {
|
|
342
|
+
h.info(
|
|
343
|
+
"hindsight coding-agents plugin is separate: run `openmem setup` / `npx @vectorize-io/hindsight-coding-agents install …`",
|
|
344
|
+
);
|
|
345
|
+
didProvider = true;
|
|
346
|
+
} else if (provider === "mem0") {
|
|
347
|
+
await installMem0ProviderMcp({ home, repoRoot, stackRoot, flags, env, h });
|
|
348
|
+
didProvider = true;
|
|
349
|
+
} else if (provider === "cognee") {
|
|
350
|
+
await installCogneeMcp({ home, repoRoot, stackRoot, flags, env, h });
|
|
351
|
+
didProvider = true;
|
|
352
|
+
} else {
|
|
353
|
+
h.warn(`no MCP install handler for provider "${provider}"`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let didUnified = false;
|
|
358
|
+
if (!flags.noUnifiedMcp) {
|
|
359
|
+
const u = installUnifiedMcp(
|
|
360
|
+
{ home, repoRoot, stackRoot, provider, dry: flags.dry },
|
|
361
|
+
h,
|
|
362
|
+
);
|
|
363
|
+
didUnified = u.changed || flags.dry;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!flags.dry && (didNotebooklm || didUnified || (didProvider && provider !== "hindsight"))) {
|
|
367
|
+
h.ok("Restart Cursor Agent so it picks up MCP changes");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return { provider, didNotebooklm, didProvider, didUnified };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @param {{ home: string, stackRoot: string, flags: InstallMcpFlags, env: NodeJS.ProcessEnv, h: any, repoRoot: string }} opts
|
|
375
|
+
*/
|
|
376
|
+
async function installMem0ProviderMcp({ home, stackRoot, flags, env, h, repoRoot }) {
|
|
377
|
+
await installMem0McpFromProvider({ home, repoRoot, stackRoot, dry: flags.dry, env }, h);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @param {{
|
|
382
|
+
* home: string,
|
|
383
|
+
* repoRoot: string,
|
|
384
|
+
* stackRoot: string,
|
|
385
|
+
* flags: InstallMcpFlags,
|
|
386
|
+
* env: NodeJS.ProcessEnv,
|
|
387
|
+
* h: any,
|
|
388
|
+
* }} opts
|
|
389
|
+
*/
|
|
390
|
+
async function installCogneeMcp({ home, repoRoot, stackRoot, flags, env, h }) {
|
|
391
|
+
const apiUrl = resolveCogneeApiUrl({ home, stackRoot, repoRoot, env });
|
|
392
|
+
await installCogneeMcpFromProvider(
|
|
393
|
+
{ home, repoRoot, dry: flags.dry, cogneeUrl: apiUrl },
|
|
394
|
+
h,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function main(argv) {
|
|
399
|
+
const flags = parseInstallMcpArgs(argv);
|
|
400
|
+
if (flags.help) {
|
|
401
|
+
console.log(HELP);
|
|
402
|
+
process.exit(0);
|
|
403
|
+
}
|
|
404
|
+
if (flags.unknown.length) {
|
|
405
|
+
console.error(`${BIN}: unknown option(s): ${flags.unknown.join(", ")}`);
|
|
406
|
+
console.error(HELP);
|
|
407
|
+
process.exit(2);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const home = process.env.HOME || os.homedir();
|
|
411
|
+
const stack = resolveStackRoot({ pkgRoot: PKG_ROOT, env: process.env, home });
|
|
412
|
+
// Code (MCP scripts, templates) comes from the package; .env lives in the stack root.
|
|
413
|
+
const repoRoot = PKG_ROOT;
|
|
414
|
+
const stackRoot = stack.root;
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
await runInstallMemoryMcp({ home, repoRoot, stackRoot, flags });
|
|
418
|
+
} catch (err) {
|
|
419
|
+
console.error(`${BIN}: ${err instanceof Error ? err.message : err}`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (isMainModule(import.meta.url)) {
|
|
425
|
+
main(process.argv.slice(2));
|
|
426
|
+
}
|
package/cli/is-main.mjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* True when this module is the process entrypoint. Compares realpaths so npm
|
|
7
|
+
* `.bin/*` symlinks into `node_modules/<pkg>/cli/*.mjs` still count as main.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} metaUrl import.meta.url of the caller
|
|
10
|
+
*/
|
|
11
|
+
export function isMainModule(metaUrl) {
|
|
12
|
+
if (!process.argv[1]) return false;
|
|
13
|
+
try {
|
|
14
|
+
return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(metaUrl));
|
|
15
|
+
} catch {
|
|
16
|
+
return path.resolve(process.argv[1]) === path.resolve(fileURLToPath(metaUrl));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child process for smart_compact_sql (cli/mcp/context-store.mjs): reads {file, sql, cap, cellMax} as JSON on
|
|
3
|
+
* stdin, runs the statement on a read-only connection and prints {columns, rows, truncated} or {error}. The parent
|
|
4
|
+
* kills it after its time limit, which also stops a long-running SQLite step.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
|
|
8
|
+
const SQLITE_OK = 0;
|
|
9
|
+
const SQLITE_DENY = 1;
|
|
10
|
+
// sqlite3_set_authorizer action codes allowed for a query: SELECT, READ, FUNCTION, RECURSIVE.
|
|
11
|
+
const ALLOWED_ACTIONS = new Set([21, 20, 31, 33]);
|
|
12
|
+
// SQLITE_PRAGMA; FTS5 reads `PRAGMA data_version` internally on a MATCH. User SQL never reaches here with a
|
|
13
|
+
// PRAGMA (the parent's statement check rejects it), and an assignment carries a value in arg2.
|
|
14
|
+
const SQLITE_PRAGMA = 19;
|
|
15
|
+
const READ_PRAGMAS = new Set(["data_version"]);
|
|
16
|
+
|
|
17
|
+
function cell(value, cellMax) {
|
|
18
|
+
if (value instanceof Uint8Array) return `<blob ${value.byteLength} bytes>`;
|
|
19
|
+
if (typeof value === "bigint") return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
|
|
20
|
+
if (typeof value === "string" && value.length > cellMax) return `${value.slice(0, cellMax)}…`;
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function run({ file, sql, cap, cellMax }) {
|
|
25
|
+
const { DatabaseSync } = process.getBuiltinModule("node:sqlite");
|
|
26
|
+
const db = new DatabaseSync(file, { readOnly: true });
|
|
27
|
+
try {
|
|
28
|
+
db.exec("PRAGMA busy_timeout = 1000");
|
|
29
|
+
// Node >= 24.10 (setAuthorizer): deny every action a query does not need, on top of the read-only
|
|
30
|
+
// connection and the parent's statement check.
|
|
31
|
+
if (typeof db.setAuthorizer === "function") {
|
|
32
|
+
db.setAuthorizer((action, arg1, arg2) => (ALLOWED_ACTIONS.has(action)
|
|
33
|
+
|| (action === SQLITE_PRAGMA && READ_PRAGMAS.has(String(arg1).toLowerCase()) && arg2 === null) ? SQLITE_OK : SQLITE_DENY));
|
|
34
|
+
}
|
|
35
|
+
const rows = [];
|
|
36
|
+
let truncated = false;
|
|
37
|
+
let columns = [];
|
|
38
|
+
for (const row of db.prepare(sql).iterate()) {
|
|
39
|
+
if (rows.length === cap) {
|
|
40
|
+
truncated = true;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
if (!columns.length) columns = Object.keys(row);
|
|
44
|
+
rows.push(Object.fromEntries(Object.entries(row).map(([k, v]) => [k, cell(v, cellMax)])));
|
|
45
|
+
}
|
|
46
|
+
return { columns, rows, truncated };
|
|
47
|
+
} finally {
|
|
48
|
+
db.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let out;
|
|
53
|
+
try {
|
|
54
|
+
out = run(JSON.parse(fs.readFileSync(0, "utf8")));
|
|
55
|
+
} catch (err) {
|
|
56
|
+
out = { error: err instanceof Error ? err.message : String(err) };
|
|
57
|
+
}
|
|
58
|
+
process.stdout.write(JSON.stringify(out));
|