@productbrain/mcp 0.0.1-beta.19 → 0.0.1-beta.1900
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/.env.mcp.example +4 -0
- package/dist/chunk-6JNK5DIZ.js +1052 -0
- package/dist/chunk-6JNK5DIZ.js.map +1 -0
- package/dist/chunk-ZKW7JZUF.js +14776 -0
- package/dist/chunk-ZKW7JZUF.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +1226 -103
- package/dist/http.js.map +1 -1
- package/dist/index.js +58 -37
- package/dist/index.js.map +1 -1
- package/dist/{setup-GZ5OZ5OP.js → setup-XWKTPL7V.js} +49 -109
- package/dist/setup-XWKTPL7V.js.map +1 -0
- package/dist/views/src/entry-cards/index.html +227 -0
- package/dist/views/src/graph-constellation/index.html +254 -0
- package/package.json +7 -3
- package/dist/chunk-AVSAR3AS.js +0 -1423
- package/dist/chunk-AVSAR3AS.js.map +0 -1
- package/dist/chunk-PPGB6SBT.js +0 -4569
- package/dist/chunk-PPGB6SBT.js.map +0 -1
- package/dist/chunk-XBMI6QHR.js +0 -100
- package/dist/chunk-XBMI6QHR.js.map +0 -1
- package/dist/setup-GZ5OZ5OP.js.map +0 -1
- package/dist/smart-capture-BR2OO2XS.js +0 -14
- package/dist/smart-capture-BR2OO2XS.js.map +0 -1
|
@@ -1,90 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
DEFAULT_CLOUD_URL,
|
|
4
|
+
MCP_NPX_PACKAGE,
|
|
3
5
|
initAnalytics,
|
|
6
|
+
resolveClient,
|
|
4
7
|
shutdownAnalytics,
|
|
5
8
|
trackSetupCompleted,
|
|
6
|
-
trackSetupStarted
|
|
7
|
-
|
|
9
|
+
trackSetupStarted,
|
|
10
|
+
warnOnProdFallthrough,
|
|
11
|
+
writeClientConfig
|
|
12
|
+
} from "./chunk-6JNK5DIZ.js";
|
|
8
13
|
|
|
9
14
|
// src/cli/setup.ts
|
|
10
15
|
import { execSync } from "child_process";
|
|
11
16
|
import { createInterface } from "readline";
|
|
12
|
-
import { existsSync
|
|
13
|
-
import { join
|
|
14
|
-
|
|
15
|
-
// src/cli/config-writer.ts
|
|
16
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
17
|
-
import { join, dirname } from "path";
|
|
18
|
-
import { homedir, platform } from "os";
|
|
19
|
-
var SERVER_ENTRY_KEY = "Product Brain";
|
|
20
|
-
var LEGACY_ENTRY_KEY = "productbrain";
|
|
21
|
-
function buildServerEntry(apiKey) {
|
|
22
|
-
return {
|
|
23
|
-
command: "npx",
|
|
24
|
-
args: ["-y", "@productbrain/mcp@beta"],
|
|
25
|
-
env: { PRODUCTBRAIN_API_KEY: apiKey }
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
function getCursorConfigPath() {
|
|
29
|
-
return join(process.cwd(), ".cursor", "mcp.json");
|
|
30
|
-
}
|
|
31
|
-
function getClaudeDesktopConfigPath() {
|
|
32
|
-
const os = platform();
|
|
33
|
-
if (os === "darwin") {
|
|
34
|
-
return join(
|
|
35
|
-
homedir(),
|
|
36
|
-
"Library",
|
|
37
|
-
"Application Support",
|
|
38
|
-
"Claude",
|
|
39
|
-
"claude_desktop_config.json"
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
if (os === "win32") {
|
|
43
|
-
const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
|
|
44
|
-
return join(appData, "Claude", "claude_desktop_config.json");
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
function resolveClient(name) {
|
|
49
|
-
if (name === "Cursor") {
|
|
50
|
-
return { name, configPath: getCursorConfigPath() };
|
|
51
|
-
}
|
|
52
|
-
const configPath = getClaudeDesktopConfigPath();
|
|
53
|
-
return configPath ? { name, configPath } : null;
|
|
54
|
-
}
|
|
55
|
-
function readJsonSafe(path) {
|
|
56
|
-
if (!existsSync(path)) return {};
|
|
57
|
-
try {
|
|
58
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
59
|
-
} catch {
|
|
60
|
-
return {};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
async function writeClientConfig(client, apiKey) {
|
|
64
|
-
const config = readJsonSafe(client.configPath);
|
|
65
|
-
const serversKey = "mcpServers";
|
|
66
|
-
if (!config[serversKey]) config[serversKey] = {};
|
|
67
|
-
if (config[serversKey][LEGACY_ENTRY_KEY]) {
|
|
68
|
-
const legacy = config[serversKey][LEGACY_ENTRY_KEY];
|
|
69
|
-
config[serversKey][SERVER_ENTRY_KEY] = {
|
|
70
|
-
...buildServerEntry(apiKey),
|
|
71
|
-
env: { ...legacy.env, PRODUCTBRAIN_API_KEY: legacy.env?.PRODUCTBRAIN_API_KEY ?? apiKey }
|
|
72
|
-
};
|
|
73
|
-
delete config[serversKey][LEGACY_ENTRY_KEY];
|
|
74
|
-
} else {
|
|
75
|
-
const existing = config[serversKey][SERVER_ENTRY_KEY];
|
|
76
|
-
config[serversKey][SERVER_ENTRY_KEY] = existing ? { ...existing, env: { ...existing.env, PRODUCTBRAIN_API_KEY: apiKey } } : buildServerEntry(apiKey);
|
|
77
|
-
}
|
|
78
|
-
const dir = dirname(client.configPath);
|
|
79
|
-
if (!existsSync(dir)) {
|
|
80
|
-
mkdirSync(dir, { recursive: true });
|
|
81
|
-
}
|
|
82
|
-
writeFileSync(client.configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// src/cli/setup.ts
|
|
87
|
-
var APP_URL = process.env.PRODUCTBRAIN_APP_URL ?? "https://productbrain.io";
|
|
17
|
+
import { existsSync, writeFileSync, mkdirSync } from "fs";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
var APP_URL = process.env.PRODUCTBRAIN_APP_URL ?? "https://work.productbrain.io";
|
|
88
20
|
function bold(s) {
|
|
89
21
|
return `\x1B[1m${s}\x1B[0m`;
|
|
90
22
|
}
|
|
@@ -102,10 +34,10 @@ function log(msg) {
|
|
|
102
34
|
`);
|
|
103
35
|
}
|
|
104
36
|
function openBrowser(url) {
|
|
105
|
-
const
|
|
37
|
+
const platform = process.platform;
|
|
106
38
|
try {
|
|
107
|
-
if (
|
|
108
|
-
else if (
|
|
39
|
+
if (platform === "darwin") execSync(`open "${url}"`);
|
|
40
|
+
else if (platform === "win32") execSync(`start "" "${url}"`);
|
|
109
41
|
else execSync(`xdg-open "${url}"`);
|
|
110
42
|
} catch {
|
|
111
43
|
log(dim(` Could not open browser automatically.`));
|
|
@@ -139,11 +71,16 @@ function promptChoice(question, choices) {
|
|
|
139
71
|
});
|
|
140
72
|
});
|
|
141
73
|
}
|
|
142
|
-
|
|
74
|
+
function resolveSetupSiteUrl() {
|
|
75
|
+
const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;
|
|
76
|
+
const url = explicit ?? DEFAULT_CLOUD_URL;
|
|
77
|
+
warnOnProdFallthrough(url, { explicit: explicit != null });
|
|
78
|
+
return url;
|
|
79
|
+
}
|
|
143
80
|
async function verifyWorkspace(apiKey) {
|
|
144
|
-
const siteUrl =
|
|
81
|
+
const siteUrl = resolveSetupSiteUrl();
|
|
145
82
|
try {
|
|
146
|
-
const res = await fetch(`${siteUrl.replace(/\/$/, "")}/api/
|
|
83
|
+
const res = await fetch(`${siteUrl.replace(/\/$/, "")}/api/aki`, {
|
|
147
84
|
method: "POST",
|
|
148
85
|
headers: {
|
|
149
86
|
"Content-Type": "application/json",
|
|
@@ -249,7 +186,7 @@ function printConfigSnippet(apiKey) {
|
|
|
249
186
|
mcpServers: {
|
|
250
187
|
"Product Brain": {
|
|
251
188
|
command: "npx",
|
|
252
|
-
args: ["-y",
|
|
189
|
+
args: ["-y", MCP_NPX_PACKAGE],
|
|
253
190
|
env: { PRODUCTBRAIN_API_KEY: apiKey }
|
|
254
191
|
}
|
|
255
192
|
}
|
|
@@ -273,8 +210,8 @@ alwaysApply: true
|
|
|
273
210
|
|
|
274
211
|
Product Brain is your product knowledge base. The Chain is the single source of truth.
|
|
275
212
|
|
|
276
|
-
Every entry is either a **draft** (captured but not
|
|
277
|
-
|
|
213
|
+
Every entry is either a **draft** (captured but not accepted) or **accepted** (on the Chain, SSOT).
|
|
214
|
+
Accepting onto the Chain is the compounding act.
|
|
278
215
|
|
|
279
216
|
## Quick Start
|
|
280
217
|
|
|
@@ -285,34 +222,35 @@ Say **"Start PB"** or **"Start Product Brain"** to begin. This single call:
|
|
|
285
222
|
|
|
286
223
|
## Tool Workflow
|
|
287
224
|
|
|
288
|
-
1. **Start here**: \`
|
|
289
|
-
2. **Search**: \`search\` \u2014 find entries across all collections
|
|
290
|
-
3. **Drill in**: \`get
|
|
291
|
-
4. **Context**: \`gather
|
|
292
|
-
5. **Capture**: \`capture\` \u2014 create
|
|
293
|
-
6. **
|
|
294
|
-
7. **Connect**: \`suggest
|
|
225
|
+
1. **Start here**: \`start\` \u2014 workspace context + next action
|
|
226
|
+
2. **Search**: \`entries action=search\` \u2014 find entries across all collections
|
|
227
|
+
3. **Drill in**: \`entries action=get\` \u2014 full record with data, labels, relations
|
|
228
|
+
4. **Context**: \`context action=gather\` \u2014 related knowledge around an entry or task
|
|
229
|
+
5. **Capture**: \`capture\` \u2014 create knowledge with auto-linking + quality score
|
|
230
|
+
6. **Accept**: \`commit-entry\` \u2014 promote drafts to SSOT when confirmation is still required
|
|
231
|
+
7. **Connect**: \`graph action=suggest\` then \`relations action=create\` to build the graph
|
|
295
232
|
|
|
296
233
|
## Bulk Knowledge Input
|
|
297
234
|
|
|
298
235
|
When given a document or batch of knowledge to capture:
|
|
299
236
|
1. Scan the input \u2014 identify all collections needed
|
|
300
|
-
2. Call \`list
|
|
237
|
+
2. Call \`collections action=list\` \u2014 compare against what exists
|
|
301
238
|
3. Propose missing collections to the user for confirmation
|
|
302
|
-
4. Call \`create
|
|
239
|
+
4. Call \`collections action=create\` for each confirmed collection
|
|
303
240
|
5. Then capture entries into the correct collections
|
|
304
241
|
|
|
305
242
|
Never stuff entries into the wrong collection. Never silently skip knowledge.
|
|
306
243
|
|
|
307
244
|
## Rules
|
|
308
245
|
|
|
309
|
-
-
|
|
310
|
-
-
|
|
311
|
-
-
|
|
246
|
+
- In Open mode, user-authored captures can be accepted immediately unless the user asks to keep them as drafts.
|
|
247
|
+
- In consensus/role modes, only call \`commit-entry\` when the user confirms.
|
|
248
|
+
- Use \`graph action=suggest\` after capturing to discover and create relations.
|
|
249
|
+
- Collections are dynamic \u2014 use \`collections action=create\` when the workspace needs new ones.
|
|
312
250
|
- When lost, fetch \`productbrain://orientation\` for the full system map.
|
|
313
251
|
`;
|
|
314
252
|
function isCursorProject() {
|
|
315
|
-
return
|
|
253
|
+
return existsSync(join(process.cwd(), ".cursor")) || existsSync(join(process.cwd(), ".cursorignore"));
|
|
316
254
|
}
|
|
317
255
|
async function offerCursorRulesInstall() {
|
|
318
256
|
if (!isCursorProject()) return;
|
|
@@ -322,22 +260,22 @@ async function offerCursorRulesInstall() {
|
|
|
322
260
|
log(dim(" Skipped rule install."));
|
|
323
261
|
return;
|
|
324
262
|
}
|
|
325
|
-
const rulesDir =
|
|
326
|
-
const rulePath =
|
|
327
|
-
if (
|
|
263
|
+
const rulesDir = join(process.cwd(), ".cursor", "rules");
|
|
264
|
+
const rulePath = join(rulesDir, CURSOR_RULE_FILENAME);
|
|
265
|
+
if (existsSync(rulePath)) {
|
|
328
266
|
log(` ${dim("\u2139")} Rule already exists at ${dim(rulePath)} \u2014 skipped`);
|
|
329
267
|
return;
|
|
330
268
|
}
|
|
331
|
-
if (!
|
|
332
|
-
|
|
269
|
+
if (!existsSync(rulesDir)) {
|
|
270
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
333
271
|
}
|
|
334
|
-
|
|
272
|
+
writeFileSync(rulePath, CURSOR_RULE_CONTENT, "utf-8");
|
|
335
273
|
log(` ${green("\u2713")} Installed rule at ${dim(rulePath)}`);
|
|
336
274
|
}
|
|
337
275
|
function buildDeeplink(apiKey) {
|
|
338
276
|
const config = JSON.stringify({
|
|
339
277
|
command: "npx",
|
|
340
|
-
args: ["-y",
|
|
278
|
+
args: ["-y", MCP_NPX_PACKAGE],
|
|
341
279
|
env: { PRODUCTBRAIN_API_KEY: apiKey }
|
|
342
280
|
});
|
|
343
281
|
const encoded = Buffer.from(config).toString("base64url");
|
|
@@ -356,10 +294,12 @@ function printClaudeSnippet() {
|
|
|
356
294
|
log("");
|
|
357
295
|
log(` When Product Brain MCP is available, say "Start PB" at the beginning`);
|
|
358
296
|
log(` of each session to orient to the workspace and unlock write tools.`);
|
|
359
|
-
log(`
|
|
297
|
+
log(` In Open mode, user-authored captures can be accepted immediately unless the user asks to keep drafts.`);
|
|
298
|
+
log(` In consensus/role modes, only accept when the user confirms.`);
|
|
360
299
|
log("");
|
|
361
300
|
}
|
|
362
301
|
export {
|
|
302
|
+
resolveSetupSiteUrl,
|
|
363
303
|
runSetup
|
|
364
304
|
};
|
|
365
|
-
//# sourceMappingURL=setup-
|
|
305
|
+
//# sourceMappingURL=setup-XWKTPL7V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/setup.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * `npx @productbrain/mcp@beta setup`\n *\n * Guided onboarding: get API key from the app, paste it, write MCP config,\n * and optionally install Cursor rules/skills (additive-only).\n */\n\nimport { execSync } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { existsSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolveClient, writeClientConfig, MCP_NPX_PACKAGE, type McpClientInfo } from \"./config-writer.js\";\nimport { initAnalytics, trackSetupStarted, trackSetupCompleted, shutdownAnalytics } from \"../analytics.js\";\nimport { DEFAULT_CLOUD_URL } from \"../client.js\";\nimport { warnOnProdFallthrough } from \"../prod-fallthrough.js\";\n\nconst APP_URL =\n process.env.PRODUCTBRAIN_APP_URL ?? \"https://work.productbrain.io\";\n\n// ── Helpers ─────────────────────────────────────────────────────────────\n\nfunction bold(s: string) {\n return `\\x1b[1m${s}\\x1b[0m`;\n}\nfunction green(s: string) {\n return `\\x1b[32m${s}\\x1b[0m`;\n}\nfunction dim(s: string) {\n return `\\x1b[2m${s}\\x1b[0m`;\n}\nfunction orange(s: string) {\n return `\\x1b[33m${s}\\x1b[0m`;\n}\n\nfunction log(msg: string) {\n process.stdout.write(`${msg}\\n`);\n}\n\nfunction openBrowser(url: string) {\n const platform = process.platform;\n try {\n if (platform === \"darwin\") execSync(`open \"${url}\"`);\n else if (platform === \"win32\") execSync(`start \"\" \"${url}\"`);\n else execSync(`xdg-open \"${url}\"`);\n } catch {\n log(dim(` Could not open browser automatically.`));\n log(` Open this URL manually: ${url}`);\n }\n}\n\nfunction prompt(question: string): Promise<string> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nfunction promptChoice(question: string, choices: string[]): Promise<number> {\n return new Promise((resolve) => {\n log(\"\");\n log(bold(question));\n choices.forEach((c, i) => log(` ${i + 1}) ${c}`));\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(`\\n ${dim(\"Choice [1]:\")} `, (line) => {\n rl.close();\n const n = parseInt(line.trim(), 10);\n if (isNaN(n) || n < 1 || n > choices.length) {\n resolve(0);\n } else {\n resolve(n - 1);\n }\n });\n });\n}\n\n// ── Workspace Verification ───────────────────────────────────────────────\n\n/**\n * Resolve the gateway URL the setup flow probes. TEN-2382: setup is an\n * independent entry point (no bootstrap), so it warns on silent prod\n * fall-through here. DEFAULT_CLOUD_URL is imported from client.ts (the\n * in-package SSOT) — no local re-declare.\n */\nexport function resolveSetupSiteUrl(): string {\n const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;\n const url = explicit ?? DEFAULT_CLOUD_URL;\n warnOnProdFallthrough(url, { explicit: explicit != null });\n return url;\n}\n\nasync function verifyWorkspace(\n apiKey: string,\n): Promise<{ name: string; slug: string } | null> {\n const siteUrl = resolveSetupSiteUrl();\n\n try {\n const res = await fetch(`${siteUrl.replace(/\\/$/, \"\")}/api/aki`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ fn: \"resolveWorkspace\", args: {} }),\n });\n\n if (!res.ok) return null;\n const json = (await res.json()) as {\n data?: { name: string; slug: string } | null;\n error?: string;\n };\n return json.data ?? null;\n } catch {\n return null;\n }\n}\n\n// ── Main ────────────────────────────────────────────────────────────────\n\nexport async function runSetup() {\n initAnalytics();\n trackSetupStarted();\n\n log(\"\");\n log(bold(` Product${orange(\"Brain\")} Setup`));\n log(dim(\" Connect your AI assistant to your chain\\n\"));\n\n const apiKeysUrl = `${APP_URL}/settings/api-keys`;\n\n log(` ${dim(\"1. Get your API key from Settings → API Keys\")}`);\n log(` ${dim(apiKeysUrl)}\\n`);\n\n const openNow = await prompt(` Open this URL in your browser? [Y/n]: `);\n if (openNow.toLowerCase() !== \"n\" && openNow.toLowerCase() !== \"no\") {\n openBrowser(apiKeysUrl);\n }\n\n log(\"\");\n log(` ${dim(\"2. Generate a key (if you don't have one), then copy it.\\n\")}`);\n\n const apiKey = await prompt(` Paste your API key (pb_sk_...): `);\n\n if (!apiKey || !apiKey.startsWith(\"pb_sk_\")) {\n log(` ${orange(\"!\")} Invalid key format. Keys start with pb_sk_.`);\n log(` Get one at ${apiKeysUrl}\\n`);\n await shutdownAnalytics();\n process.exit(1);\n }\n\n log(` ${green(\"✓\")} Key received`);\n\n const workspace = await verifyWorkspace(apiKey);\n if (workspace) {\n log(` ${green(\"✓\")} Connected to workspace: ${bold(workspace.name)} ${dim(`(${workspace.slug})`)}`);\n } else {\n log(` ${orange(\"!\")} Could not verify workspace. Check your key at ${apiKeysUrl}`);\n }\n log(\"\");\n\n const CLIENT_NAMES = [\"Cursor\", \"Claude Desktop\"] as const;\n const options = [...CLIENT_NAMES, \"Other\"];\n\n const choice = await promptChoice(\"Where do you want to set up Product Brain?\", options);\n\n if (choice === 2) {\n printConfigSnippet(apiKey);\n trackSetupCompleted(\"Other\", \"snippet_shown\");\n } else {\n const client = resolveClient(CLIENT_NAMES[choice]);\n if (client) {\n const outcome = await writeConfig(client, apiKey);\n trackSetupCompleted(CLIENT_NAMES[choice], outcome);\n } else {\n log(` ${orange(\"!\")} ${CLIENT_NAMES[choice]} config path not available on this platform.`);\n printConfigSnippet(apiKey);\n trackSetupCompleted(CLIENT_NAMES[choice], \"write_error\");\n }\n }\n\n // Cursor-specific: offer to install rule (additive-only)\n if (choice === 0) {\n await offerCursorRulesInstall();\n printDeeplink(apiKey);\n }\n\n // Claude-specific: print snippet (never write to CLAUDE.md)\n if (choice === 1) {\n printClaudeSnippet();\n }\n\n log(\"\");\n log(\n ` ${green(\"✓\")} Done! Restart your AI assistant and try: ${bold('\"Start PB\"')}`,\n );\n printHelpLink();\n await shutdownAnalytics();\n}\n\nasync function writeConfig(\n client: McpClientInfo,\n apiKey: string,\n): Promise<\"config_written\" | \"config_existed\" | \"write_error\"> {\n try {\n const wrote = await writeClientConfig(client, apiKey);\n if (wrote) {\n log(` ${green(\"✓\")} Wrote config to ${dim(client.configPath)}`);\n return \"config_written\";\n } else {\n log(` ${dim(\"ℹ\")} ${client.name} already configured — skipped`);\n return \"config_existed\";\n }\n } catch (err: any) {\n log(` ${orange(\"!\")} Could not write ${client.name} config: ${err.message}`);\n printConfigSnippet(apiKey);\n return \"write_error\";\n }\n}\n\nfunction printHelpLink() {\n log(` ${dim(`Need help? See ${APP_URL}/settings/api-keys`)}`);\n log(\"\");\n}\n\nfunction printConfigSnippet(apiKey: string) {\n log(\"\");\n log(bold(\" Add this to your MCP client config:\\n\"));\n const snippet = JSON.stringify(\n {\n mcpServers: {\n \"Product Brain\": {\n command: \"npx\",\n args: [\"-y\", MCP_NPX_PACKAGE],\n env: { PRODUCTBRAIN_API_KEY: apiKey },\n },\n },\n },\n null,\n 2,\n );\n for (const line of snippet.split(\"\\n\")) {\n log(` ${line}`);\n }\n log(\"\");\n}\n\n// ── Cursor Rules/Skills Install (additive-only) ─────────────────────────\n\nconst CURSOR_RULE_FILENAME = \"product-brain.mdc\";\n\nconst CURSOR_RULE_CONTENT = `---\ndescription: Product Brain MCP — single source of truth for product knowledge\nglobs:\nalwaysApply: true\n---\n\n# Product Brain MCP\n\nProduct Brain is your product knowledge base. The Chain is the single source of truth.\n\nEvery entry is either a **draft** (captured but not accepted) or **accepted** (on the Chain, SSOT).\nAccepting onto the Chain is the compounding act.\n\n## Quick Start\n\nSay **\"Start PB\"** or **\"Start Product Brain\"** to begin. This single call:\n- Orients you to the workspace (readiness, gaps, planned work)\n- Unlocks write tools for the session\n- Surfaces your next recommended action\n\n## Tool Workflow\n\n1. **Start here**: \\`start\\` — workspace context + next action\n2. **Search**: \\`entries action=search\\` — find entries across all collections\n3. **Drill in**: \\`entries action=get\\` — full record with data, labels, relations\n4. **Context**: \\`context action=gather\\` — related knowledge around an entry or task\n5. **Capture**: \\`capture\\` — create knowledge with auto-linking + quality score\n6. **Accept**: \\`commit-entry\\` — promote drafts to SSOT when confirmation is still required\n7. **Connect**: \\`graph action=suggest\\` then \\`relations action=create\\` to build the graph\n\n## Bulk Knowledge Input\n\nWhen given a document or batch of knowledge to capture:\n1. Scan the input — identify all collections needed\n2. Call \\`collections action=list\\` — compare against what exists\n3. Propose missing collections to the user for confirmation\n4. Call \\`collections action=create\\` for each confirmed collection\n5. Then capture entries into the correct collections\n\nNever stuff entries into the wrong collection. Never silently skip knowledge.\n\n## Rules\n\n- In Open mode, user-authored captures can be accepted immediately unless the user asks to keep them as drafts.\n- In consensus/role modes, only call \\`commit-entry\\` when the user confirms.\n- Use \\`graph action=suggest\\` after capturing to discover and create relations.\n- Collections are dynamic — use \\`collections action=create\\` when the workspace needs new ones.\n- When lost, fetch \\`productbrain://orientation\\` for the full system map.\n`;\n\nfunction isCursorProject(): boolean {\n return existsSync(join(process.cwd(), \".cursor\")) || existsSync(join(process.cwd(), \".cursorignore\"));\n}\n\nasync function offerCursorRulesInstall(): Promise<void> {\n if (!isCursorProject()) return;\n\n const answer = await prompt(`\\n Install Product Brain rule for Cursor? [Y/n]: `);\n if (answer.toLowerCase() === \"n\" || answer.toLowerCase() === \"no\") {\n log(dim(\" Skipped rule install.\"));\n return;\n }\n\n const rulesDir = join(process.cwd(), \".cursor\", \"rules\");\n const rulePath = join(rulesDir, CURSOR_RULE_FILENAME);\n\n if (existsSync(rulePath)) {\n log(` ${dim(\"ℹ\")} Rule already exists at ${dim(rulePath)} — skipped`);\n return;\n }\n\n if (!existsSync(rulesDir)) {\n mkdirSync(rulesDir, { recursive: true });\n }\n\n writeFileSync(rulePath, CURSOR_RULE_CONTENT, \"utf-8\");\n log(` ${green(\"✓\")} Installed rule at ${dim(rulePath)}`);\n}\n\nfunction buildDeeplink(apiKey: string): string {\n const config = JSON.stringify({\n command: \"npx\",\n args: [\"-y\", MCP_NPX_PACKAGE],\n env: { PRODUCTBRAIN_API_KEY: apiKey },\n });\n const encoded = Buffer.from(config).toString(\"base64url\");\n return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(\"Product Brain\")}&config=${encoded}`;\n}\n\nfunction printDeeplink(apiKey: string): void {\n const link = buildDeeplink(apiKey);\n log(\"\");\n log(` ${dim(\"One-click install for Cursor (paste in browser):\")}`);\n log(` ${link}`);\n}\n\nfunction printClaudeSnippet(): void {\n log(\"\");\n log(bold(\" For Claude Code / CLAUDE.md:\"));\n log(dim(\" Add this line to your ~/.claude/CLAUDE.md:\"));\n log(\"\");\n log(` When Product Brain MCP is available, say \"Start PB\" at the beginning`);\n log(` of each session to orient to the workspace and unlock write tools.`);\n log(` In Open mode, user-authored captures can be accepted immediately unless the user asks to keep drafts.`);\n log(` In consensus/role modes, only accept when the user confirms.`);\n log(\"\");\n}\n"],"mappings":"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","names":[]}
|