@bcmyguest/openmem 0.0.0-stage → 0.6.3
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/LICENSE +190 -0
- package/README.md +136 -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 +23 -0
- package/cli/coding-agent/hook-harnesses.mjs +87 -0
- package/cli/coding-agent/hook-runner.mjs +260 -0
- package/cli/coding-agent/install-access.mjs +93 -0
- package/cli/coding-agent/install-shim.mjs +97 -0
- package/cli/coding-agent/opencode-config.mjs +91 -0
- package/cli/coding-agent/opencode-plugin.mjs +96 -0
- package/cli/coding-agent/runtime.mjs +39 -0
- package/cli/coding-agent/session-facade.mjs +62 -0
- package/cli/cognee-mcp.mjs +260 -0
- package/cli/feed-sync.mjs +11 -0
- package/cli/install-coding-agent.mjs +165 -0
- package/cli/install-memory-mcp.mjs +426 -0
- package/cli/is-main.mjs +18 -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/llm-synthesize.mjs +50 -0
- package/cli/mcp/mem0-scope-id.mjs +22 -0
- package/cli/mcp/memory-facade.mjs +455 -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 +35 -0
- package/cli/openmem.mjs +205 -0
- package/cli/roots.mjs +85 -0
- package/cli/stack.mjs +436 -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 +164 -0
- package/config/coding-agent.json +13 -0
- package/config/hindsight.env.example +24 -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 +62 -0
- package/docs/OPENMEM.md +23 -0
- package/docs/architecture.md +219 -0
- package/docs/assets/README.md +10 -0
- package/docs/assets/setup-demo.gif +0 -0
- package/docs/coding-agent-hooks.md +51 -0
- package/docs/coding-agent-scopes.md +22 -0
- package/docs/coding-agents.md +51 -0
- package/docs/cognee.md +37 -0
- package/docs/concepts.md +112 -0
- package/docs/hindsight.md +74 -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 +101 -0
- package/docs/mem0.md +103 -0
- package/docs/memory-portability.md +73 -0
- package/docs/memory-providers.md +246 -0
- package/docs/model-configuration.md +84 -0
- package/docs/notebooklm.md +83 -0
- package/docs/npx-install.md +170 -0
- package/docs/openmem-config.md +194 -0
- package/docs/provider-wizard-hooks.md +222 -0
- package/docs/quickstart.md +101 -0
- package/docs/setup-wizard.md +116 -0
- package/docs/sources-feeds.md +52 -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 +95 -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 +245 -0
- package/providers/hindsight/lib/coding-agent.mjs +204 -0
- package/providers/hindsight/lib/config.mjs +73 -0
- package/providers/hindsight/lib/env-setup.mjs +71 -0
- package/providers/hindsight/lib/litellm-routes.mjs +187 -0
- package/providers/hindsight/lib/model-backend.mjs +214 -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 +33 -0
- package/providers/hindsight/lib/stack-env.mjs +132 -0
- package/providers/hindsight/lib/state.mjs +75 -0
- package/providers/hindsight/lib/summary.mjs +43 -0
- package/providers/hindsight/lib/validate.mjs +75 -0
- package/providers/hindsight/manifest.json +58 -0
- package/providers/hindsight/wizard.mjs +149 -0
- package/providers/letta/manifest.json +29 -0
- package/providers/mem0/OPERATOR.md +124 -0
- package/providers/mem0/compose.yaml +57 -0
- package/providers/mem0/lib/coding-agent.mjs +58 -0
- package/providers/mem0/lib/config.mjs +45 -0
- package/providers/mem0/lib/configure.mjs +135 -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/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 +216 -0
- package/providers/mem0/lib/state.mjs +67 -0
- package/providers/mem0/lib/summary.mjs +60 -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 +133 -0
- package/providers/zep/manifest.json +29 -0
- package/scripts/check-pack.mjs +218 -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 +94 -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/skills/openmem/SKILL.md +29 -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/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/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 +163 -0
- package/wizard/lib/ports.mjs +94 -0
- package/wizard/lib/prompts.mjs +57 -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 +755 -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,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Install coding-agent session memory for Claude Code, Codex, OpenCode, Cursor CLI.
|
|
4
|
+
* All providers: unified MCP + in-repo session hooks.
|
|
5
|
+
* Optional: --hindsight-plugin also runs @vectorize-io/hindsight-coding-agents (Hindsight stacks only).
|
|
6
|
+
*/
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { CODING_AGENTS, DEFAULT_AGENT_IDS } from "./coding-agent/agents.mjs";
|
|
11
|
+
import { AGENT_IDS, PROVIDER_CAPABILITIES } from "./coding-agent/capabilities.mjs";
|
|
12
|
+
import { installSessionShim } from "./coding-agent/install-shim.mjs";
|
|
13
|
+
import { installAgentAccess } from "./coding-agent/install-access.mjs";
|
|
14
|
+
import { prepareAgentRuntime } from "./coding-agent/runtime.mjs";
|
|
15
|
+
import { SHIM_AGENT_IDS } from "./coding-agent/hook-harnesses.mjs";
|
|
16
|
+
import { resolveMemoryProvider } from "./install-memory-mcp.mjs";
|
|
17
|
+
import { isMainModule } from "./is-main.mjs";
|
|
18
|
+
import { resolveHindsightApiUrl } from "./mcp/context.mjs";
|
|
19
|
+
import { resolveStackRoot } from "./stack.mjs";
|
|
20
|
+
import { runInstallMemoryMcp } from "./install-memory-mcp.mjs";
|
|
21
|
+
|
|
22
|
+
const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
23
|
+
const BIN = "openmem-install-coding-agent";
|
|
24
|
+
|
|
25
|
+
const HELP = `${BIN}: wire session memory for coding agents
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
npx openmem install-coding-agent [--yes] [--dry-run] [--agents a,b] [--hindsight-plugin]
|
|
29
|
+
|
|
30
|
+
Default agents: ${DEFAULT_AGENT_IDS.join(", ")}
|
|
31
|
+
All known agents: ${CODING_AGENTS.map((a) => a.id).join(", ")}
|
|
32
|
+
|
|
33
|
+
Every provider gets install-mcp + openmem session hooks (SessionStart / prompt / Stop).
|
|
34
|
+
With --hindsight-plugin and MEMORY_PROVIDER=hindsight, also runs hindsight-coding-agents install.
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
function parseArgs(argv) {
|
|
38
|
+
const flags = {
|
|
39
|
+
yes: false,
|
|
40
|
+
dry: false,
|
|
41
|
+
help: false,
|
|
42
|
+
hindsightPlugin: false,
|
|
43
|
+
agents: [...DEFAULT_AGENT_IDS],
|
|
44
|
+
unknown: [],
|
|
45
|
+
};
|
|
46
|
+
for (let i = 0; i < argv.length; i++) {
|
|
47
|
+
const a = argv[i];
|
|
48
|
+
if (a === "--yes" || a === "-y") flags.yes = true;
|
|
49
|
+
else if (a === "--dry-run") flags.dry = true;
|
|
50
|
+
else if (a === "--help" || a === "-h") flags.help = true;
|
|
51
|
+
else if (a === "--hindsight-plugin") flags.hindsightPlugin = true;
|
|
52
|
+
else if (a === "--agents") flags.agents = String(argv[++i] || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
53
|
+
else flags.unknown.push(a);
|
|
54
|
+
}
|
|
55
|
+
return flags;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function h() {
|
|
59
|
+
return {
|
|
60
|
+
info: (m) => console.log(m),
|
|
61
|
+
ok: (m) => console.log(m),
|
|
62
|
+
warn: (m) => console.warn(m),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function runInstallCodingAgent(opts) {
|
|
67
|
+
const home = opts.home;
|
|
68
|
+
const env = opts.env || process.env;
|
|
69
|
+
const flags = opts.flags;
|
|
70
|
+
for (const agentId of flags.agents) {
|
|
71
|
+
if (!AGENT_IDS.includes(agentId)) {
|
|
72
|
+
opts.h?.warn(`Unknown agent "${agentId}" (known: ${AGENT_IDS.join(", ")})`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const provider = resolveMemoryProvider(home, env);
|
|
76
|
+
const caps = PROVIDER_CAPABILITIES[provider] || {};
|
|
77
|
+
opts.h?.info(`Provider ${provider}: capabilities ${JSON.stringify(caps)}`);
|
|
78
|
+
|
|
79
|
+
const shimAgents = flags.agents.filter((id) => SHIM_AGENT_IDS.includes(id) || id === "opencode");
|
|
80
|
+
const runtimeRoot = prepareAgentRuntime({
|
|
81
|
+
home, repoRoot: opts.repoRoot, stackRoot: opts.stackRoot, dry: flags.dry || !flags.yes, h: opts.h || h(),
|
|
82
|
+
});
|
|
83
|
+
if (flags.dry) {
|
|
84
|
+
opts.h?.info("dry-run: would run install-mcp --yes");
|
|
85
|
+
installSessionShim({ home, repoRoot: runtimeRoot, agents: shimAgents, dry: true, h: opts.h || h() });
|
|
86
|
+
installAgentAccess({ home, runtimeRoot, stackRoot: opts.stackRoot, agents: flags.agents, dry: true, h: opts.h || h() });
|
|
87
|
+
if (flags.hindsightPlugin && provider === "hindsight") {
|
|
88
|
+
const apiUrl = resolveHindsightApiUrl({ home, stackRoot: opts.stackRoot, env });
|
|
89
|
+
opts.h?.info(
|
|
90
|
+
`dry-run: would also run npx @vectorize-io/hindsight-coding-agents install ${flags.agents.join(" ")} --server self-hosted --api-url ${apiUrl}`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return { provider, ran: false };
|
|
94
|
+
}
|
|
95
|
+
if (!flags.yes) {
|
|
96
|
+
opts.h?.warn("Re-run with --yes to install MCP + session hooks");
|
|
97
|
+
return { provider, ran: false };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await runInstallMemoryMcp({
|
|
101
|
+
home,
|
|
102
|
+
repoRoot: runtimeRoot,
|
|
103
|
+
stackRoot: opts.stackRoot,
|
|
104
|
+
flags: { yes: true, dry: false, notebooklm: false, notebooklmOnly: false, providerOnly: false, noUnifiedMcp: false, help: false, unknown: [] },
|
|
105
|
+
env,
|
|
106
|
+
h: opts.h || h(),
|
|
107
|
+
});
|
|
108
|
+
installSessionShim({
|
|
109
|
+
home,
|
|
110
|
+
repoRoot: runtimeRoot,
|
|
111
|
+
agents: shimAgents,
|
|
112
|
+
dry: false,
|
|
113
|
+
h: opts.h || h(),
|
|
114
|
+
});
|
|
115
|
+
installAgentAccess({
|
|
116
|
+
home, runtimeRoot, stackRoot: opts.stackRoot, agents: flags.agents, dry: false, h: opts.h || h(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (flags.hindsightPlugin && provider === "hindsight") {
|
|
120
|
+
const apiUrl = resolveHindsightApiUrl({ home, stackRoot: opts.stackRoot, env });
|
|
121
|
+
const cmd = ["npx", "-y", "@vectorize-io/hindsight-coding-agents", "install", ...flags.agents, "--server", "self-hosted", "--api-url", apiUrl];
|
|
122
|
+
opts.h?.info(`Also installing upstream Hindsight plugin: ${cmd.join(" ")}`);
|
|
123
|
+
const r = spawnSync(cmd[0], cmd.slice(1), { stdio: "inherit", env });
|
|
124
|
+
return { provider, ran: true, status: r.status ?? 1 };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const agentId of flags.agents) {
|
|
128
|
+
if (shimAgents.includes(agentId)) {
|
|
129
|
+
opts.h?.info(` ${agentId}: openmem session hooks + unified MCP`);
|
|
130
|
+
} else {
|
|
131
|
+
const row = CODING_AGENTS.find((a) => a.id === agentId);
|
|
132
|
+
opts.h?.info(` ${agentId}: ${row?.sessionPath || "unified MCP memory_*"}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { provider, ran: true, status: 0 };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function main() {
|
|
139
|
+
const flags = parseArgs(process.argv.slice(2));
|
|
140
|
+
if (flags.help || flags.unknown.length) {
|
|
141
|
+
console.log(HELP);
|
|
142
|
+
process.exit(flags.unknown.length ? 2 : 0);
|
|
143
|
+
}
|
|
144
|
+
const home = process.env.HOME || "";
|
|
145
|
+
const sr = resolveStackRoot({ pkgRoot: PKG_ROOT });
|
|
146
|
+
const out = await runInstallCodingAgent({
|
|
147
|
+
home,
|
|
148
|
+
repoRoot: PKG_ROOT,
|
|
149
|
+
stackRoot: sr.root,
|
|
150
|
+
flags,
|
|
151
|
+
env: process.env,
|
|
152
|
+
h: h(),
|
|
153
|
+
});
|
|
154
|
+
return typeof out.status === "number" ? out.status : 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (isMainModule(import.meta.url)) {
|
|
158
|
+
main().then(
|
|
159
|
+
(code) => process.exit(code ?? 0),
|
|
160
|
+
(err) => {
|
|
161
|
+
console.error(err);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
}
|
|
@@ -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,61 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { envValue } from "../../wizard/lib/paths.mjs";
|
|
3
|
+
import { resolveMemoryProvider } from "../install-memory-mcp.mjs";
|
|
4
|
+
import { resolveMem0ApiUrl, resolveMem0EnvFilePath } from "../../providers/mem0/lib/mcp-install.mjs";
|
|
5
|
+
import { resolveCogneeApiUrl } from "../../providers/cognee/lib/cognee-mcp-install.mjs";
|
|
6
|
+
import { readKey } from "../../providers/mem0/lib/env-file.mjs";
|
|
7
|
+
|
|
8
|
+
const DEFAULT_HINDSIGHT_PORT = 8888;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {{ home: string, stackRoot: string, env?: NodeJS.ProcessEnv }} opts
|
|
12
|
+
*/
|
|
13
|
+
export function resolveHindsightApiUrl({ home, stackRoot, env = process.env }) {
|
|
14
|
+
const candidates = [
|
|
15
|
+
env.HINDSIGHT_URL,
|
|
16
|
+
env.PM_HINDSIGHT_URL,
|
|
17
|
+
envValue(path.join(stackRoot, ".env"), "HINDSIGHT_URL"),
|
|
18
|
+
envValue(path.join(home, ".config/hindsight/wizard.conf"), "HINDSIGHT_URL"),
|
|
19
|
+
];
|
|
20
|
+
const url = candidates.map((v) => String(v || "").trim()).find(Boolean);
|
|
21
|
+
if (url) return url.replace(/\/+$/, "");
|
|
22
|
+
const portCandidates = [
|
|
23
|
+
env.HINDSIGHT_PORT,
|
|
24
|
+
envValue(path.join(stackRoot, ".env"), "HINDSIGHT_PORT"),
|
|
25
|
+
envValue(path.join(home, ".config/hindsight/wizard.conf"), "HINDSIGHT_PORT"),
|
|
26
|
+
];
|
|
27
|
+
const port = portCandidates.map((v) => String(v || "").trim()).find(Boolean) || String(DEFAULT_HINDSIGHT_PORT);
|
|
28
|
+
return `http://127.0.0.1:${port}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {{ home: string, stackRoot: string, repoRoot: string, env?: NodeJS.ProcessEnv }} opts
|
|
33
|
+
*/
|
|
34
|
+
export function resolveMcpContext(opts) {
|
|
35
|
+
const env = opts.env || process.env;
|
|
36
|
+
const home = opts.home;
|
|
37
|
+
const stackRoot = opts.stackRoot;
|
|
38
|
+
const provider = resolveMemoryProvider(home, env);
|
|
39
|
+
/** @type {{ provider: string, hindsightUrl?: string, mem0?: { baseUrl: string, apiKey?: string }, cognee?: { baseUrl: string, apiKey?: string } }} */
|
|
40
|
+
const ctx = { provider };
|
|
41
|
+
if (provider === "hindsight") {
|
|
42
|
+
ctx.hindsightUrl = resolveHindsightApiUrl({ home, stackRoot, env });
|
|
43
|
+
} else if (provider === "mem0") {
|
|
44
|
+
const baseUrl = resolveMem0ApiUrl({ home, stackRoot, env });
|
|
45
|
+
const envFile = resolveMem0EnvFilePath({ home, stackRoot, env });
|
|
46
|
+
let apiKey = String(env.MEM0_API_KEY || "").trim();
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
try {
|
|
49
|
+
apiKey = readKey(envFile) || "";
|
|
50
|
+
} catch {
|
|
51
|
+
apiKey = "";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
ctx.mem0 = { baseUrl, apiKey: apiKey || undefined };
|
|
55
|
+
} else if (provider === "cognee") {
|
|
56
|
+
const baseUrl = resolveCogneeApiUrl({ home, stackRoot, repoRoot: opts.repoRoot, env });
|
|
57
|
+
const apiKey = String(env.COGNEE_API_KEY || "").trim() || undefined;
|
|
58
|
+
ctx.cognee = { baseUrl, apiKey };
|
|
59
|
+
}
|
|
60
|
+
return ctx;
|
|
61
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
const SLUG_MAX = 40;
|
|
4
|
+
|
|
5
|
+
/** Match `indexer/sinks/cognee.py` `dataset_name(bank)`. */
|
|
6
|
+
export function cogneeDatasetName(bank) {
|
|
7
|
+
const raw = String(bank ?? "");
|
|
8
|
+
let slug = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
9
|
+
slug = slug.slice(0, SLUG_MAX).replace(/^_+|_+$/g, "") || "bank";
|
|
10
|
+
const hash = crypto.createHash("sha256").update(raw).digest("hex").slice(0, 8);
|
|
11
|
+
return `${slug}_${hash}`;
|
|
12
|
+
}
|