brainjar 0.3.711 → 0.3.713

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/brainjar.js +411 -118
  2. package/package.json +1 -1
package/brainjar.js CHANGED
@@ -107,7 +107,7 @@ var CLI_VERSION, CACHE, DAY;
107
107
  var init_version_check = __esm({
108
108
  "packages/cli/src/version-check.ts"() {
109
109
  "use strict";
110
- CLI_VERSION = "0.3.711";
110
+ CLI_VERSION = "0.3.713";
111
111
  CACHE = path.join(os.homedir(), ".brainjar", "version-check.json");
112
112
  DAY = 864e5;
113
113
  }
@@ -238,8 +238,8 @@ var init_github = __esm({
238
238
  if (this.opts.token) h.Authorization = `Bearer ${this.opts.token}`;
239
239
  return h;
240
240
  }
241
- async *paginate(path19, params) {
242
- let url = this.buildUrl(path19, params);
241
+ async *paginate(path20, params) {
242
+ let url = this.buildUrl(path20, params);
243
243
  while (url) {
244
244
  const res = await this.fetchImpl(url, { headers: this.headers() });
245
245
  if (!res.ok) {
@@ -250,9 +250,9 @@ var init_github = __esm({
250
250
  url = parseNextLink(res.headers.get("link"));
251
251
  }
252
252
  }
253
- buildUrl(path19, params) {
253
+ buildUrl(path20, params) {
254
254
  const url = new URL(
255
- `${this.baseUrl}/repos/${this.opts.owner}/${this.opts.repo}${path19}`
255
+ `${this.baseUrl}/repos/${this.opts.owner}/${this.opts.repo}${path20}`
256
256
  );
257
257
  url.searchParams.set("per_page", String(this.perPage));
258
258
  for (const [k, v] of Object.entries(params)) {
@@ -3735,7 +3735,7 @@ var require_split2 = __commonJS({
3735
3735
  var require_helper = __commonJS({
3736
3736
  "node_modules/pgpass/lib/helper.js"(exports, module) {
3737
3737
  "use strict";
3738
- var path19 = __require("path");
3738
+ var path20 = __require("path");
3739
3739
  var Stream2 = __require("stream").Stream;
3740
3740
  var split = require_split2();
3741
3741
  var util = __require("util");
@@ -3774,7 +3774,7 @@ var require_helper = __commonJS({
3774
3774
  };
3775
3775
  module.exports.getFileName = function(rawEnv) {
3776
3776
  var env = rawEnv || process.env;
3777
- var file = env.PGPASSFILE || (isWin ? path19.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path19.join(env.HOME || "./", ".pgpass"));
3777
+ var file = env.PGPASSFILE || (isWin ? path20.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path20.join(env.HOME || "./", ".pgpass"));
3778
3778
  return file;
3779
3779
  };
3780
3780
  module.exports.usePgPass = function(stats, fname) {
@@ -3906,7 +3906,7 @@ var require_helper = __commonJS({
3906
3906
  var require_lib = __commonJS({
3907
3907
  "node_modules/pgpass/lib/index.js"(exports, module) {
3908
3908
  "use strict";
3909
- var path19 = __require("path");
3909
+ var path20 = __require("path");
3910
3910
  var fs = __require("fs");
3911
3911
  var helper = require_helper();
3912
3912
  module.exports = function(connInfo, cb) {
@@ -4587,7 +4587,7 @@ var require_pg_pool = __commonJS({
4587
4587
  function throwOnDoubleRelease() {
4588
4588
  throw new Error("Release called on client which has already been released to the pool.");
4589
4589
  }
4590
- function promisify6(Promise2, callback) {
4590
+ function promisify7(Promise2, callback) {
4591
4591
  if (callback) {
4592
4592
  return { callback, result: void 0 };
4593
4593
  }
@@ -4725,7 +4725,7 @@ var require_pg_pool = __commonJS({
4725
4725
  const err = new Error("Cannot use a pool after calling end on the pool");
4726
4726
  return cb ? cb(err) : this.Promise.reject(err);
4727
4727
  }
4728
- const response = promisify6(this.Promise, cb);
4728
+ const response = promisify7(this.Promise, cb);
4729
4729
  const result = response.result;
4730
4730
  if (this._isFull() || this._idle.length) {
4731
4731
  if (this._idle.length) {
@@ -4910,7 +4910,7 @@ var require_pg_pool = __commonJS({
4910
4910
  }
4911
4911
  query(text, values, cb) {
4912
4912
  if (typeof text === "function") {
4913
- const response2 = promisify6(this.Promise, text);
4913
+ const response2 = promisify7(this.Promise, text);
4914
4914
  setImmediate(function() {
4915
4915
  return response2.callback(new Error("Passing a function as the first parameter to pool.query is not supported"));
4916
4916
  });
@@ -4920,7 +4920,7 @@ var require_pg_pool = __commonJS({
4920
4920
  cb = values;
4921
4921
  values = void 0;
4922
4922
  }
4923
- const response = promisify6(this.Promise, cb);
4923
+ const response = promisify7(this.Promise, cb);
4924
4924
  cb = response.callback;
4925
4925
  this.connect((err, client) => {
4926
4926
  if (err) {
@@ -4965,7 +4965,7 @@ var require_pg_pool = __commonJS({
4965
4965
  return cb ? cb(err) : this.Promise.reject(err);
4966
4966
  }
4967
4967
  this.ending = true;
4968
- const promised = promisify6(this.Promise, cb);
4968
+ const promised = promisify7(this.Promise, cb);
4969
4969
  this._endCallback = promised.callback;
4970
4970
  this._pulseQueue();
4971
4971
  return promised.result;
@@ -8852,12 +8852,12 @@ var init_path = __esm({
8852
8852
  "node_modules/@anthropic-ai/sdk/internal/utils/path.mjs"() {
8853
8853
  init_error();
8854
8854
  EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
8855
- createPathTagFunction = (pathEncoder = encodeURIPath) => function path19(statics, ...params) {
8855
+ createPathTagFunction = (pathEncoder = encodeURIPath) => function path20(statics, ...params) {
8856
8856
  if (statics.length === 1)
8857
8857
  return statics[0];
8858
8858
  let postPath = false;
8859
8859
  const invalidSegments = [];
8860
- const path20 = statics.reduce((previousValue, currentValue, index) => {
8860
+ const path21 = statics.reduce((previousValue, currentValue, index) => {
8861
8861
  if (/[?#]/.test(currentValue)) {
8862
8862
  postPath = true;
8863
8863
  }
@@ -8874,7 +8874,7 @@ var init_path = __esm({
8874
8874
  }
8875
8875
  return previousValue + currentValue + (index === params.length ? "" : encoded);
8876
8876
  }, "");
8877
- const pathOnly = path20.split(/[?#]/, 1)[0];
8877
+ const pathOnly = path21.split(/[?#]/, 1)[0];
8878
8878
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
8879
8879
  let match;
8880
8880
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -8895,10 +8895,10 @@ var init_path = __esm({
8895
8895
  }, "");
8896
8896
  throw new AnthropicError(`Path parameters result in path with invalid segments:
8897
8897
  ${invalidSegments.map((e) => e.error).join("\n")}
8898
- ${path20}
8898
+ ${path21}
8899
8899
  ${underline}`);
8900
8900
  }
8901
- return path20;
8901
+ return path21;
8902
8902
  };
8903
8903
  path3 = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
8904
8904
  }
@@ -11884,9 +11884,9 @@ var init_client = __esm({
11884
11884
  makeStatusError(status, error, message, headers) {
11885
11885
  return APIError.generate(status, error, message, headers);
11886
11886
  }
11887
- buildURL(path19, query, defaultBaseURL) {
11887
+ buildURL(path20, query, defaultBaseURL) {
11888
11888
  const baseURL = !__classPrivateFieldGet(this, _BaseAnthropic_instances, "m", _BaseAnthropic_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
11889
- const url = isAbsoluteURL(path19) ? new URL(path19) : new URL(baseURL + (baseURL.endsWith("/") && path19.startsWith("/") ? path19.slice(1) : path19));
11889
+ const url = isAbsoluteURL(path20) ? new URL(path20) : new URL(baseURL + (baseURL.endsWith("/") && path20.startsWith("/") ? path20.slice(1) : path20));
11890
11890
  const defaultQuery = this.defaultQuery();
11891
11891
  if (!isEmptyObj(defaultQuery)) {
11892
11892
  query = { ...defaultQuery, ...query };
@@ -11917,24 +11917,24 @@ var init_client = __esm({
11917
11917
  */
11918
11918
  async prepareRequest(request, { url, options }) {
11919
11919
  }
11920
- get(path19, opts) {
11921
- return this.methodRequest("get", path19, opts);
11920
+ get(path20, opts) {
11921
+ return this.methodRequest("get", path20, opts);
11922
11922
  }
11923
- post(path19, opts) {
11924
- return this.methodRequest("post", path19, opts);
11923
+ post(path20, opts) {
11924
+ return this.methodRequest("post", path20, opts);
11925
11925
  }
11926
- patch(path19, opts) {
11927
- return this.methodRequest("patch", path19, opts);
11926
+ patch(path20, opts) {
11927
+ return this.methodRequest("patch", path20, opts);
11928
11928
  }
11929
- put(path19, opts) {
11930
- return this.methodRequest("put", path19, opts);
11929
+ put(path20, opts) {
11930
+ return this.methodRequest("put", path20, opts);
11931
11931
  }
11932
- delete(path19, opts) {
11933
- return this.methodRequest("delete", path19, opts);
11932
+ delete(path20, opts) {
11933
+ return this.methodRequest("delete", path20, opts);
11934
11934
  }
11935
- methodRequest(method, path19, opts) {
11935
+ methodRequest(method, path20, opts) {
11936
11936
  return this.request(Promise.resolve(opts).then((opts2) => {
11937
- return { method, path: path19, ...opts2 };
11937
+ return { method, path: path20, ...opts2 };
11938
11938
  }));
11939
11939
  }
11940
11940
  request(options, remainingRetries = null) {
@@ -12038,8 +12038,8 @@ var init_client = __esm({
12038
12038
  }));
12039
12039
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
12040
12040
  }
12041
- getAPIList(path19, Page2, opts) {
12042
- return this.requestAPIList(Page2, { method: "get", path: path19, ...opts });
12041
+ getAPIList(path20, Page2, opts) {
12042
+ return this.requestAPIList(Page2, { method: "get", path: path20, ...opts });
12043
12043
  }
12044
12044
  requestAPIList(Page2, options) {
12045
12045
  const request = this.makeRequest(options, null, void 0);
@@ -12126,8 +12126,8 @@ var init_client = __esm({
12126
12126
  }
12127
12127
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
12128
12128
  const options = { ...inputOptions };
12129
- const { method, path: path19, query, defaultBaseURL } = options;
12130
- const url = this.buildURL(path19, query, defaultBaseURL);
12129
+ const { method, path: path20, query, defaultBaseURL } = options;
12130
+ const url = this.buildURL(path20, query, defaultBaseURL);
12131
12131
  if ("timeout" in options)
12132
12132
  validatePositiveInteger("timeout", options.timeout);
12133
12133
  options.timeout = options.timeout ?? this.timeout;
@@ -13511,10 +13511,242 @@ exec ${captureHookCommand(subcommand)}${flag}
13511
13511
  }
13512
13512
  });
13513
13513
 
13514
- // packages/ingest/src/tools.ts
13515
- import { existsSync as existsSync10 } from "node:fs";
13514
+ // packages/ingest/src/mcp-config.ts
13515
+ import { execFile as execFile3 } from "node:child_process";
13516
+ import { existsSync as existsSync10, readFileSync as readFileSync11, realpathSync } from "node:fs";
13517
+ import { mkdir as mkdir9, writeFile as writeFile9 } from "node:fs/promises";
13516
13518
  import os4 from "node:os";
13517
13519
  import path13 from "node:path";
13520
+ import { promisify as promisify3 } from "node:util";
13521
+ function brainOf(url) {
13522
+ try {
13523
+ return new URL(url).searchParams.get("brain");
13524
+ } catch {
13525
+ return null;
13526
+ }
13527
+ }
13528
+ function readJson(file) {
13529
+ if (!existsSync10(file)) return null;
13530
+ try {
13531
+ return JSON.parse(readFileSync11(file, "utf8"));
13532
+ } catch {
13533
+ throw new Error(`${file} is not valid JSON`);
13534
+ }
13535
+ }
13536
+ function urlIn(entry) {
13537
+ const e = entry;
13538
+ if (typeof e?.url === "string") return e.url;
13539
+ if (typeof e?.httpUrl === "string") return e.httpUrl;
13540
+ return null;
13541
+ }
13542
+ function jsonMcp(file, top, signIn, entry) {
13543
+ const servers = (cfg) => cfg[top];
13544
+ return {
13545
+ where: file,
13546
+ signIn,
13547
+ url: (root) => {
13548
+ try {
13549
+ return urlIn(
13550
+ servers(readJson(path13.join(root, file)) ?? {})?.[MCP_NAME]
13551
+ );
13552
+ } catch {
13553
+ return null;
13554
+ }
13555
+ },
13556
+ install: async (root, url) => {
13557
+ const f = path13.join(root, file);
13558
+ const cfg = readJson(f) ?? {};
13559
+ const all = cfg[top] ??= {};
13560
+ const before = JSON.stringify(all[MCP_NAME] ?? null);
13561
+ all[MCP_NAME] = { ...all[MCP_NAME], ...entry(url) };
13562
+ if (JSON.stringify(all[MCP_NAME]) === before) return false;
13563
+ await mkdir9(path13.dirname(f), { recursive: true });
13564
+ await writeFile9(f, JSON.stringify(cfg, null, 2) + "\n", "utf8");
13565
+ return true;
13566
+ },
13567
+ remove: async (root) => {
13568
+ const f = path13.join(root, file);
13569
+ const cfg = readJson(f);
13570
+ const all = cfg && servers(cfg);
13571
+ if (!cfg || !all || !(MCP_NAME in all)) return false;
13572
+ delete all[MCP_NAME];
13573
+ if (Object.keys(all).length === 0) delete cfg[top];
13574
+ await writeFile9(f, JSON.stringify(cfg, null, 2) + "\n", "utf8");
13575
+ return true;
13576
+ }
13577
+ };
13578
+ }
13579
+ function codexSection(lines) {
13580
+ const start = lines.findIndex((l) => l.trim() === CODEX_HEADER);
13581
+ if (start < 0) return null;
13582
+ let end = start + 1;
13583
+ while (end < lines.length && !lines[end].trim().startsWith("[")) end++;
13584
+ return [start + 1, end];
13585
+ }
13586
+ function claudeConfigFile() {
13587
+ return path13.join(
13588
+ process.env.CLAUDE_CONFIG_DIR?.trim() || os4.homedir(),
13589
+ ".claude.json"
13590
+ );
13591
+ }
13592
+ function claudeLocalUrl(root) {
13593
+ try {
13594
+ const cfg = readJson(claudeConfigFile());
13595
+ const projects = cfg?.projects;
13596
+ const p = projects?.[root] ?? projects?.[path13.resolve(root)] ?? projects?.[realpathSync(root)];
13597
+ return urlIn(p?.mcpServers?.[MCP_NAME]);
13598
+ } catch {
13599
+ return null;
13600
+ }
13601
+ }
13602
+ function claudeAddCommand(url) {
13603
+ return `claude mcp add --transport http ${MCP_NAME} '${url}'`;
13604
+ }
13605
+ var run3, MCP_NAME, cursorMcp, vscodeMcp, geminiMcp, kiroMcp, opencodeMcp, CODEX_FILE, CODEX_HEADER, codexMcp, claudeCli, claudeMcp;
13606
+ var init_mcp_config = __esm({
13607
+ "packages/ingest/src/mcp-config.ts"() {
13608
+ "use strict";
13609
+ run3 = promisify3(execFile3);
13610
+ MCP_NAME = "brainjar";
13611
+ cursorMcp = jsonMcp(
13612
+ path13.join(".cursor", "mcp.json"),
13613
+ "mcpServers",
13614
+ "Cursor Settings > MCP, sign in on brainjar",
13615
+ (url) => ({ url })
13616
+ );
13617
+ vscodeMcp = jsonMcp(
13618
+ path13.join(".vscode", "mcp.json"),
13619
+ "servers",
13620
+ "VS Code asks you to sign in when the server starts",
13621
+ (url) => ({ type: "http", url })
13622
+ );
13623
+ geminiMcp = jsonMcp(
13624
+ path13.join(".gemini", "settings.json"),
13625
+ "mcpServers",
13626
+ "Gemini opens the browser on first use, or /mcp auth brainjar",
13627
+ (url) => ({ httpUrl: url })
13628
+ );
13629
+ kiroMcp = jsonMcp(
13630
+ path13.join(".kiro", "settings", "mcp.json"),
13631
+ "mcpServers",
13632
+ "Kiro opens the browser on first use",
13633
+ (url) => ({ url })
13634
+ );
13635
+ opencodeMcp = jsonMcp(
13636
+ "opencode.json",
13637
+ "mcp",
13638
+ "run: opencode mcp auth brainjar",
13639
+ (url) => ({ type: "remote", url, enabled: true })
13640
+ );
13641
+ CODEX_FILE = path13.join(".codex", "config.toml");
13642
+ CODEX_HEADER = `[mcp_servers.${MCP_NAME}]`;
13643
+ codexMcp = {
13644
+ where: CODEX_FILE,
13645
+ signIn: "run: codex mcp login brainjar",
13646
+ url: (root) => {
13647
+ const f = path13.join(root, CODEX_FILE);
13648
+ if (!existsSync10(f)) return null;
13649
+ const lines = readFileSync11(f, "utf8").split("\n");
13650
+ const range = codexSection(lines);
13651
+ if (!range) return null;
13652
+ for (const l of lines.slice(...range)) {
13653
+ const m = /^\s*url\s*=\s*"([^"]*)"/.exec(l);
13654
+ if (m) return m[1];
13655
+ }
13656
+ return null;
13657
+ },
13658
+ install: async (root, url) => {
13659
+ const f = path13.join(root, CODEX_FILE);
13660
+ const text = existsSync10(f) ? readFileSync11(f, "utf8") : "";
13661
+ const lines = text.split("\n");
13662
+ const line = `url = "${url}"`;
13663
+ const range = codexSection(lines);
13664
+ let next;
13665
+ if (!range) {
13666
+ const body = text.length === 0 || text.endsWith("\n") ? text : text + "\n";
13667
+ next = `${body}${body ? "\n" : ""}${CODEX_HEADER}
13668
+ ${line}
13669
+ `.split("\n");
13670
+ } else {
13671
+ const i = lines.findIndex(
13672
+ (l, n) => n >= range[0] && n < range[1] && /^\s*url\s*=/.test(l)
13673
+ );
13674
+ next = [...lines];
13675
+ if (i >= 0) next[i] = line;
13676
+ else next.splice(range[0], 0, line);
13677
+ }
13678
+ const out = next.join("\n");
13679
+ if (out === text) return false;
13680
+ await mkdir9(path13.dirname(f), { recursive: true });
13681
+ await writeFile9(f, out, "utf8");
13682
+ return true;
13683
+ },
13684
+ remove: async (root) => {
13685
+ const f = path13.join(root, CODEX_FILE);
13686
+ if (!existsSync10(f)) return false;
13687
+ const lines = readFileSync11(f, "utf8").split("\n");
13688
+ const ours3 = (l) => l.trim() === CODEX_HEADER || l.trim().startsWith(`[mcp_servers.${MCP_NAME}.`);
13689
+ const kept = [];
13690
+ let skipping = false;
13691
+ for (const l of lines) {
13692
+ if (l.trim().startsWith("[")) skipping = ours3(l);
13693
+ if (!skipping) kept.push(l);
13694
+ }
13695
+ if (kept.length === lines.length) return false;
13696
+ await writeFile9(f, kept.join("\n").replace(/\n{3,}/g, "\n\n"), "utf8");
13697
+ return true;
13698
+ }
13699
+ };
13700
+ claudeCli = (args, cwd) => run3("claude", args, {
13701
+ cwd,
13702
+ shell: process.platform === "win32",
13703
+ // claude.cmd
13704
+ windowsHide: true,
13705
+ timeout: 3e4
13706
+ });
13707
+ claudeMcp = {
13708
+ where: "Claude Code, this folder",
13709
+ signIn: "type /mcp in Claude Code and sign in",
13710
+ url: (root) => {
13711
+ const local = claudeLocalUrl(root);
13712
+ if (local) return local;
13713
+ try {
13714
+ const project = readJson(path13.join(root, ".mcp.json"));
13715
+ const fromProject = urlIn(
13716
+ project?.mcpServers?.[MCP_NAME]
13717
+ );
13718
+ if (fromProject) return fromProject;
13719
+ const user = readJson(claudeConfigFile());
13720
+ return urlIn(user?.mcpServers?.[MCP_NAME]);
13721
+ } catch {
13722
+ return null;
13723
+ }
13724
+ },
13725
+ install: async (root, url) => {
13726
+ if (claudeLocalUrl(root) === url) return false;
13727
+ await claudeCli(["mcp", "remove", "-s", "local", MCP_NAME], root).catch(
13728
+ () => {
13729
+ }
13730
+ );
13731
+ await claudeCli(
13732
+ ["mcp", "add", "-s", "local", "--transport", "http", MCP_NAME, url],
13733
+ root
13734
+ );
13735
+ return true;
13736
+ },
13737
+ remove: async (root) => {
13738
+ if (!claudeLocalUrl(root)) return false;
13739
+ await claudeCli(["mcp", "remove", "-s", "local", MCP_NAME], root);
13740
+ return true;
13741
+ }
13742
+ };
13743
+ }
13744
+ });
13745
+
13746
+ // packages/ingest/src/tools.ts
13747
+ import { existsSync as existsSync11 } from "node:fs";
13748
+ import os5 from "node:os";
13749
+ import path14 from "node:path";
13518
13750
  function detectedTools(root) {
13519
13751
  return CAPTURE_TOOLS.filter((t) => t.detect(root));
13520
13752
  }
@@ -13530,66 +13762,74 @@ var init_tools = __esm({
13530
13762
  init_kiro_hooks();
13531
13763
  init_opencode_plugin();
13532
13764
  init_cline_hooks();
13765
+ init_mcp_config();
13533
13766
  claudeCode = {
13534
13767
  id: "claude-code",
13535
13768
  name: "Claude Code",
13536
- detect: (root) => existsSync10(path13.join(os4.homedir(), ".claude")) || existsSync10(path13.join(root, ".claude")),
13769
+ detect: (root) => existsSync11(path14.join(os5.homedir(), ".claude")) || existsSync11(path14.join(root, ".claude")),
13537
13770
  install: installCaptureHook,
13538
13771
  installed: captureHookInstalled,
13539
- remove: removeCaptureHook
13772
+ remove: removeCaptureHook,
13773
+ mcp: claudeMcp
13540
13774
  };
13541
13775
  cursor = {
13542
13776
  id: "cursor",
13543
13777
  name: "Cursor",
13544
- detect: (root) => existsSync10(path13.join(os4.homedir(), ".cursor")) || existsSync10(path13.join(root, ".cursor")),
13778
+ detect: (root) => existsSync11(path14.join(os5.homedir(), ".cursor")) || existsSync11(path14.join(root, ".cursor")),
13545
13779
  install: installCursorHooks,
13546
13780
  installed: cursorHooksInstalled,
13547
- remove: removeCursorHooks
13781
+ remove: removeCursorHooks,
13782
+ mcp: cursorMcp
13548
13783
  };
13549
13784
  codex = {
13550
13785
  id: "codex",
13551
13786
  name: "Codex",
13552
- detect: (root) => existsSync10(path13.join(os4.homedir(), ".codex")) || existsSync10(path13.join(root, ".codex")),
13787
+ detect: (root) => existsSync11(path14.join(os5.homedir(), ".codex")) || existsSync11(path14.join(root, ".codex")),
13553
13788
  install: installCodexHooks,
13554
13789
  installed: codexHooksInstalled,
13555
- remove: removeCodexHooks
13790
+ remove: removeCodexHooks,
13791
+ mcp: codexMcp
13556
13792
  };
13557
13793
  copilot = {
13558
13794
  id: "copilot",
13559
13795
  name: "GitHub Copilot",
13560
- detect: (root) => existsSync10(path13.join(os4.homedir(), ".copilot")) || existsSync10(path13.join(root, ".github", "hooks")),
13796
+ detect: (root) => existsSync11(path14.join(os5.homedir(), ".copilot")) || existsSync11(path14.join(root, ".github", "hooks")),
13561
13797
  install: installCopilotHooks,
13562
13798
  installed: copilotHooksInstalled,
13563
- remove: removeCopilotHooks
13799
+ remove: removeCopilotHooks,
13800
+ mcp: vscodeMcp
13564
13801
  };
13565
13802
  gemini = {
13566
13803
  id: "gemini",
13567
13804
  name: "Gemini CLI",
13568
- detect: (root) => existsSync10(path13.join(os4.homedir(), ".gemini")) || existsSync10(path13.join(root, ".gemini")),
13805
+ detect: (root) => existsSync11(path14.join(os5.homedir(), ".gemini")) || existsSync11(path14.join(root, ".gemini")),
13569
13806
  install: installGeminiHooks,
13570
13807
  installed: geminiHooksInstalled,
13571
- remove: removeGeminiHooks
13808
+ remove: removeGeminiHooks,
13809
+ mcp: geminiMcp
13572
13810
  };
13573
13811
  kiro = {
13574
13812
  id: "kiro",
13575
13813
  name: "Kiro",
13576
- detect: (root) => existsSync10(path13.join(os4.homedir(), ".kiro")) || existsSync10(path13.join(root, ".kiro")),
13814
+ detect: (root) => existsSync11(path14.join(os5.homedir(), ".kiro")) || existsSync11(path14.join(root, ".kiro")),
13577
13815
  install: installKiroHooks,
13578
13816
  installed: kiroHooksInstalled,
13579
- remove: removeKiroHooks
13817
+ remove: removeKiroHooks,
13818
+ mcp: kiroMcp
13580
13819
  };
13581
13820
  opencode = {
13582
13821
  id: "opencode",
13583
13822
  name: "OpenCode",
13584
- detect: (root) => existsSync10(path13.join(os4.homedir(), ".config", "opencode")) || existsSync10(path13.join(root, ".opencode")),
13823
+ detect: (root) => existsSync11(path14.join(os5.homedir(), ".config", "opencode")) || existsSync11(path14.join(root, ".opencode")),
13585
13824
  install: installOpencodePlugin,
13586
13825
  installed: opencodePluginInstalled,
13587
- remove: removeOpencodePlugin
13826
+ remove: removeOpencodePlugin,
13827
+ mcp: opencodeMcp
13588
13828
  };
13589
13829
  cline = {
13590
13830
  id: "cline",
13591
13831
  name: "Cline",
13592
- detect: (root) => existsSync10(path13.join(os4.homedir(), "Documents", "Cline")) || existsSync10(path13.join(os4.homedir(), ".cline")) || existsSync10(path13.join(root, ".clinerules")),
13832
+ detect: (root) => existsSync11(path14.join(os5.homedir(), "Documents", "Cline")) || existsSync11(path14.join(os5.homedir(), ".cline")) || existsSync11(path14.join(root, ".clinerules")),
13593
13833
  install: installClineHooks,
13594
13834
  installed: clineHooksInstalled,
13595
13835
  remove: removeClineHooks
@@ -14079,8 +14319,8 @@ var init_doku_model = __esm({
14079
14319
 
14080
14320
  // packages/ingest/src/doku.ts
14081
14321
  import { createHash as createHash2 } from "node:crypto";
14082
- import { mkdir as mkdir9, readFile as readFile10, rm as rm6, stat, writeFile as writeFile9 } from "node:fs/promises";
14083
- import path14 from "node:path";
14322
+ import { mkdir as mkdir10, readFile as readFile10, rm as rm6, stat, writeFile as writeFile10 } from "node:fs/promises";
14323
+ import path15 from "node:path";
14084
14324
  function bodyLocations(o) {
14085
14325
  return Array.isArray(o.body.location) ? o.body.location.map(String) : [];
14086
14326
  }
@@ -14146,7 +14386,7 @@ async function loadDokuData(brainId, root) {
14146
14386
  if (root) {
14147
14387
  try {
14148
14388
  const pkg = JSON.parse(
14149
- await readFile10(path14.join(root, "package.json"), "utf8")
14389
+ await readFile10(path15.join(root, "package.json"), "utf8")
14150
14390
  );
14151
14391
  description = pkg.description ?? null;
14152
14392
  scripts = pkg.scripts ?? null;
@@ -14729,8 +14969,8 @@ function dokuFilePath(kind, slug) {
14729
14969
  return `${DOKU_DIR}/${slug}.md`;
14730
14970
  }
14731
14971
  async function writeDokuTree(root, files) {
14732
- const dir = path14.join(root, DOKU_DIR);
14733
- const manifestPath = path14.join(dir, MANIFEST);
14972
+ const dir = path15.join(root, DOKU_DIR);
14973
+ const manifestPath = path15.join(dir, MANIFEST);
14734
14974
  let old = { files: {} };
14735
14975
  try {
14736
14976
  old = JSON.parse(await readFile10(manifestPath, "utf8"));
@@ -14743,21 +14983,21 @@ async function writeDokuTree(root, files) {
14743
14983
  for (const file of files) {
14744
14984
  const hash = sha1(file.markdown);
14745
14985
  next[file.relPath] = { hash };
14746
- const abs = path14.join(root, file.relPath);
14986
+ const abs = path15.join(root, file.relPath);
14747
14987
  if (old.files[file.relPath]?.hash === hash && await exists(abs)) continue;
14748
- await mkdir9(path14.dirname(abs), { recursive: true });
14749
- await writeFile9(abs, file.markdown, "utf8");
14988
+ await mkdir10(path15.dirname(abs), { recursive: true });
14989
+ await writeFile10(abs, file.markdown, "utf8");
14750
14990
  written++;
14751
14991
  }
14752
14992
  let deleted = 0;
14753
14993
  for (const rel of Object.keys(old.files)) {
14754
14994
  if (next[rel]) continue;
14755
14995
  if (rel !== DOKU_FILENAME && !rel.startsWith(`${DOKU_DIR}/`)) continue;
14756
- await rm6(path14.join(root, rel), { force: true });
14996
+ await rm6(path15.join(root, rel), { force: true });
14757
14997
  deleted++;
14758
14998
  }
14759
- await mkdir9(dir, { recursive: true });
14760
- await writeFile9(
14999
+ await mkdir10(dir, { recursive: true });
15000
+ await writeFile10(
14761
15001
  manifestPath,
14762
15002
  JSON.stringify({ files: next }, null, 2),
14763
15003
  "utf8"
@@ -14910,8 +15150,8 @@ var init_ponytail_brief = __esm({
14910
15150
  });
14911
15151
 
14912
15152
  // packages/ingest/src/ponytail-debt.ts
14913
- function isDebtScannable(path19) {
14914
- return !/\.(md|mdx|markdown|txt|jsonl|log|lock)$/i.test(path19);
15153
+ function isDebtScannable(path20) {
15154
+ return !/\.(md|mdx|markdown|txt|jsonl|log|lock)$/i.test(path20);
14915
15155
  }
14916
15156
  function scanPonytailComments(files) {
14917
15157
  const out = [];
@@ -15086,6 +15326,7 @@ var init_src3 = __esm({
15086
15326
  init_session_ingest();
15087
15327
  init_capture_events2();
15088
15328
  init_tools();
15329
+ init_mcp_config();
15089
15330
  init_cursor_hooks();
15090
15331
  init_codex_hooks();
15091
15332
  init_copilot_hooks();
@@ -15192,10 +15433,10 @@ __export(brief_exports, {
15192
15433
  upsertBriefBlock: () => upsertBriefBlock,
15193
15434
  withExcluded: () => withExcluded
15194
15435
  });
15195
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync3 } from "node:fs";
15196
- import path15 from "node:path";
15197
- import { execFile as execFile3 } from "node:child_process";
15198
- import { promisify as promisify3 } from "node:util";
15436
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync3 } from "node:fs";
15437
+ import path16 from "node:path";
15438
+ import { execFile as execFile4 } from "node:child_process";
15439
+ import { promisify as promisify4 } from "node:util";
15199
15440
  function claudeMdWithImport(existing) {
15200
15441
  let text = existing;
15201
15442
  const start = text.indexOf(BRIEF_START);
@@ -15241,12 +15482,12 @@ ${BRIEF_END}
15241
15482
  }
15242
15483
  function installPointer(root) {
15243
15484
  if (loadEnv().BRAINJAR_NO_BRIEF === "1") return "skipped";
15244
- const file = path15.join(root, BRIEF_FILE);
15245
- const existing = existsSync11(file) ? readFileSync11(file, "utf8") : "";
15485
+ const file = path16.join(root, BRIEF_FILE);
15486
+ const existing = existsSync12(file) ? readFileSync12(file, "utf8") : "";
15246
15487
  const next = upsertBriefBlock(existing, POINTER);
15247
15488
  if (next !== null) writeFileSync3(file, next);
15248
- const claudeFile = path15.join(root, "CLAUDE.md");
15249
- const claude = existsSync11(claudeFile) ? readFileSync11(claudeFile, "utf8") : "";
15489
+ const claudeFile = path16.join(root, "CLAUDE.md");
15490
+ const claude = existsSync12(claudeFile) ? readFileSync12(claudeFile, "utf8") : "";
15250
15491
  const claudeNext = claudeMdWithImport(claude);
15251
15492
  if (claudeNext !== null) writeFileSync3(claudeFile, claudeNext);
15252
15493
  return next !== null || claudeNext !== null ? "updated" : "unchanged";
@@ -15278,24 +15519,24 @@ async function refreshCursorRules(root) {
15278
15519
  `/capture/brief?brain=${link.brainId}`
15279
15520
  );
15280
15521
  if (!text) return "skipped";
15281
- const file = path15.join(root, CURSOR_RULE_FILE);
15522
+ const file = path16.join(root, CURSOR_RULE_FILE);
15282
15523
  const next = cursorRule(text);
15283
- const changed = !existsSync11(file) || readFileSync11(file, "utf8") !== next;
15524
+ const changed = !existsSync12(file) || readFileSync12(file, "utf8") !== next;
15284
15525
  if (changed) {
15285
- mkdirSync3(path15.dirname(file), { recursive: true });
15526
+ mkdirSync3(path16.dirname(file), { recursive: true });
15286
15527
  writeFileSync3(file, next);
15287
15528
  }
15288
15529
  try {
15289
- const { stdout } = await run3(
15530
+ const { stdout } = await run4(
15290
15531
  "git",
15291
15532
  ["rev-parse", "--path-format=absolute", "--git-path", "info/exclude"],
15292
15533
  { cwd: root, windowsHide: true }
15293
15534
  );
15294
15535
  const exclude = stdout.trim();
15295
- const current = existsSync11(exclude) ? readFileSync11(exclude, "utf8") : "";
15536
+ const current = existsSync12(exclude) ? readFileSync12(exclude, "utf8") : "";
15296
15537
  const withLine = withExcluded(current, CURSOR_RULE_FILE);
15297
15538
  if (withLine !== null) {
15298
- mkdirSync3(path15.dirname(exclude), { recursive: true });
15539
+ mkdirSync3(path16.dirname(exclude), { recursive: true });
15299
15540
  writeFileSync3(exclude, withLine);
15300
15541
  }
15301
15542
  } catch {
@@ -15322,14 +15563,14 @@ async function briefMain() {
15322
15563
  process.exitCode = 1;
15323
15564
  }
15324
15565
  }
15325
- var run3, BRIEF_START, BRIEF_END, BRIEF_FILE, CLAUDE_IMPORT, CURSOR_RULE_FILE, POINTER;
15566
+ var run4, BRIEF_START, BRIEF_END, BRIEF_FILE, CLAUDE_IMPORT, CURSOR_RULE_FILE, POINTER;
15326
15567
  var init_brief = __esm({
15327
15568
  "packages/cli/src/brief.ts"() {
15328
15569
  "use strict";
15329
15570
  init_src3();
15330
15571
  init_src();
15331
15572
  init_api_client();
15332
- run3 = promisify3(execFile3);
15573
+ run4 = promisify4(execFile4);
15333
15574
  BRIEF_START = "<!-- brainjar:brief:start -->";
15334
15575
  BRIEF_END = "<!-- brainjar:brief:end -->";
15335
15576
  BRIEF_FILE = "AGENTS.md";
@@ -15349,12 +15590,12 @@ __export(debt_exports, {
15349
15590
  debtMain: () => debtMain,
15350
15591
  scanDebtInRepo: () => scanDebtInRepo
15351
15592
  });
15352
- import { execFile as execFile4 } from "node:child_process";
15593
+ import { execFile as execFile5 } from "node:child_process";
15353
15594
  import { readFile as readFile11 } from "node:fs/promises";
15354
- import { promisify as promisify4 } from "node:util";
15355
- import path16 from "node:path";
15595
+ import { promisify as promisify5 } from "node:util";
15596
+ import path17 from "node:path";
15356
15597
  async function scanDebtInRepo(root) {
15357
- const { stdout } = await run4("git", ["-C", root, "ls-files"], {
15598
+ const { stdout } = await run5("git", ["-C", root, "ls-files"], {
15358
15599
  maxBuffer: 16 * 1048576
15359
15600
  });
15360
15601
  const files = [];
@@ -15362,7 +15603,7 @@ async function scanDebtInRepo(root) {
15362
15603
  const p = rel.trim();
15363
15604
  if (!p || !isDebtScannable(p)) continue;
15364
15605
  try {
15365
- const buf = await readFile11(path16.join(root, p));
15606
+ const buf = await readFile11(path17.join(root, p));
15366
15607
  if (buf.length > 512 * 1024 || buf.includes(0)) continue;
15367
15608
  files.push({ path: p, content: buf.toString("utf8") });
15368
15609
  } catch {
@@ -15393,13 +15634,13 @@ async function debtMain() {
15393
15634
  `);
15394
15635
  }
15395
15636
  }
15396
- var run4;
15637
+ var run5;
15397
15638
  var init_debt = __esm({
15398
15639
  "packages/cli/src/debt.ts"() {
15399
15640
  "use strict";
15400
15641
  init_src3();
15401
15642
  init_api_client();
15402
- run4 = promisify4(execFile4);
15643
+ run5 = promisify5(execFile5);
15403
15644
  }
15404
15645
  });
15405
15646
 
@@ -15787,11 +16028,11 @@ var init_codex = __esm({
15787
16028
  ...commits.length ? { commits } : {}
15788
16029
  });
15789
16030
  files.forEach(
15790
- (path19, i) => events.push({
16031
+ (path20, i) => events.push({
15791
16032
  ...base,
15792
16033
  event_id: `file:${id}:${i}`,
15793
16034
  kind: "file_changed",
15794
- path: path19
16035
+ path: path20
15795
16036
  })
15796
16037
  );
15797
16038
  }
@@ -16515,18 +16756,18 @@ __export(hook_exports, {
16515
16756
  });
16516
16757
  import {
16517
16758
  appendFileSync,
16518
- existsSync as existsSync12,
16759
+ existsSync as existsSync13,
16519
16760
  mkdirSync as mkdirSync4,
16520
16761
  readdirSync,
16521
- readFileSync as readFileSync12,
16762
+ readFileSync as readFileSync13,
16522
16763
  rmSync,
16523
16764
  statSync as statSync2
16524
16765
  } from "node:fs";
16525
- import os5 from "node:os";
16526
- import path17 from "node:path";
16766
+ import os6 from "node:os";
16767
+ import path18 from "node:path";
16527
16768
  function spoolPath(tool, sessionId) {
16528
- return path17.join(
16529
- os5.homedir(),
16769
+ return path18.join(
16770
+ os6.homedir(),
16530
16771
  ".brainjar",
16531
16772
  "spool",
16532
16773
  tool,
@@ -16535,7 +16776,7 @@ function spoolPath(tool, sessionId) {
16535
16776
  }
16536
16777
  function readSpool(file) {
16537
16778
  const out = [];
16538
- for (const line of readFileSync12(file, "utf8").split("\n")) {
16779
+ for (const line of readFileSync13(file, "utf8").split("\n")) {
16539
16780
  if (!line.trim()) continue;
16540
16781
  try {
16541
16782
  out.push(clampCaptureEvent(JSON.parse(line)));
@@ -16547,7 +16788,7 @@ function readSpool(file) {
16547
16788
  function pruneSpool(dir, keep, maxAgeMs = 7 * 864e5) {
16548
16789
  try {
16549
16790
  for (const f of readdirSync(dir)) {
16550
- const file = path17.join(dir, f);
16791
+ const file = path18.join(dir, f);
16551
16792
  if (file === keep) continue;
16552
16793
  if (Date.now() - statSync2(file).mtimeMs > maxAgeMs)
16553
16794
  rmSync(file, { force: true });
@@ -16557,7 +16798,7 @@ function pruneSpool(dir, keep, maxAgeMs = 7 * 864e5) {
16557
16798
  }
16558
16799
  function staleSpools(dir, keep, minAgeMs = 36e5) {
16559
16800
  try {
16560
- return readdirSync(dir).map((f) => path17.join(dir, f)).filter(
16801
+ return readdirSync(dir).map((f) => path18.join(dir, f)).filter(
16561
16802
  (file) => file !== keep && Date.now() - statSync2(file).mtimeMs > minAgeMs
16562
16803
  ).sort();
16563
16804
  } catch {
@@ -16602,7 +16843,7 @@ async function hookMain(argv) {
16602
16843
  }
16603
16844
  let payload = null;
16604
16845
  try {
16605
- payload = JSON.parse(readFileSync12(0, "utf8"));
16846
+ payload = JSON.parse(readFileSync13(0, "utf8"));
16606
16847
  } catch {
16607
16848
  return;
16608
16849
  }
@@ -16611,7 +16852,7 @@ async function hookMain(argv) {
16611
16852
  if (out.reply) process.stdout.write(out.reply);
16612
16853
  const file = spoolPath(adapter.id, out.sessionId);
16613
16854
  if (out.events.length) {
16614
- mkdirSync4(path17.dirname(file), { recursive: true });
16855
+ mkdirSync4(path18.dirname(file), { recursive: true });
16615
16856
  appendFileSync(
16616
16857
  file,
16617
16858
  out.events.map((e) => JSON.stringify(clampCaptureEvent(e))).join("\n") + "\n"
@@ -16625,9 +16866,9 @@ async function hookMain(argv) {
16625
16866
  if (out.end) rmSync(file, { force: true });
16626
16867
  return;
16627
16868
  }
16628
- if (existsSync12(file)) await shipSpool(file, link);
16869
+ if (existsSync13(file)) await shipSpool(file, link);
16629
16870
  if (out.end) rmSync(file, { force: true });
16630
- const dir = path17.dirname(file);
16871
+ const dir = path18.dirname(file);
16631
16872
  for (const other of staleSpools(dir, file)) {
16632
16873
  const otherLink = await linkForSpool(other);
16633
16874
  if (!otherLink) continue;
@@ -16874,18 +17115,31 @@ async function init(argv) {
16874
17115
  }
16875
17116
  step(5, "Semantic search\u2026");
16876
17117
  ok("embedding runs on the server after every capture");
16877
- step(6, "Installing continuous capture (agent hooks)\u2026");
17118
+ step(6, "Installing continuous capture (agent hooks) and the memory\u2026");
16878
17119
  const tools = detectedTools(root);
16879
17120
  if (tools.length === 0) {
16880
17121
  ok(
16881
17122
  "no coding agent detected (looked for Claude Code, Cursor, Codex, GitHub Copilot, Gemini CLI, Kiro, OpenCode, Cline); rerun init after installing one"
16882
17123
  );
16883
17124
  }
17125
+ const memoryUrl = `${serverUrl()}/mcp?brain=${conn.brainId}`;
16884
17126
  for (const tool of tools) {
16885
17127
  const changed = await tool.install(root);
16886
17128
  ok(
16887
17129
  `${tool.name}: ${changed ? "hooks installed" : "hooks already present"}`
16888
17130
  );
17131
+ if (!tool.mcp) continue;
17132
+ try {
17133
+ const wrote = await tool.mcp.install(root, memoryUrl);
17134
+ ok(
17135
+ `${tool.name}: memory ${wrote ? "added to" : "already in"} ${tool.mcp.where}; sign in once: ${tool.mcp.signIn}`
17136
+ );
17137
+ } catch (e) {
17138
+ const msg = e instanceof Error ? e.message : String(e);
17139
+ ok(
17140
+ `${tool.name}: memory not added (${msg.slice(0, 120)}). ` + (tool.id === "claude-code" ? `Run: ${claudeAddCommand(memoryUrl)}` : `Add it yourself in ${tool.mcp.where}`)
17141
+ );
17142
+ }
16889
17143
  }
16890
17144
  const gitHook = await installGitPostCommitHook(root);
16891
17145
  ok(
@@ -16925,7 +17179,8 @@ async function init(argv) {
16925
17179
  console.log(
16926
17180
  `
16927
17181
  \u2705 Brain "${name}" is live \u2014 ${final.objects} memory objects.
16928
- Open the web app and visit Docs. Approve the hooks in your coding agent when prompted.
17182
+ Open the web app and visit Docs. Approve the hooks in your coding agent when prompted,
17183
+ and sign in to the memory once (see the lines above).
16929
17184
  Optional: \`npx brainjar deepscan\` reads the repo's key files so the docs start full.
16930
17185
  `
16931
17186
  );
@@ -16963,7 +17218,7 @@ __export(constraints_exports, {
16963
17218
  constraintsMain: () => constraintsMain,
16964
17219
  sessionStartEnvelope: () => sessionStartEnvelope
16965
17220
  });
16966
- import { readFileSync as readFileSync13 } from "node:fs";
17221
+ import { readFileSync as readFileSync14 } from "node:fs";
16967
17222
  function sessionStartEnvelope(text) {
16968
17223
  return JSON.stringify({
16969
17224
  hookSpecificOutput: {
@@ -16982,7 +17237,7 @@ async function constraintsMain(argv) {
16982
17237
  if (hook) {
16983
17238
  let cwd;
16984
17239
  try {
16985
- const payload = JSON.parse(readFileSync13(0, "utf8"));
17240
+ const payload = JSON.parse(readFileSync14(0, "utf8"));
16986
17241
  cwd = payload.cwd ?? payload.workspace_roots?.[0] ?? payload.workspaceRoots?.[0];
16987
17242
  } catch {
16988
17243
  }
@@ -17051,11 +17306,12 @@ var init_failure = __esm({
17051
17306
  var doctor_exports = {};
17052
17307
  __export(doctor_exports, {
17053
17308
  doctorMain: () => doctorMain,
17309
+ memoryCheck: () => memoryCheck,
17054
17310
  renderDoctorReport: () => renderDoctorReport,
17055
17311
  runDoctor: () => runDoctor
17056
17312
  });
17057
- import { execFile as execFile5 } from "node:child_process";
17058
- import { promisify as promisify5 } from "node:util";
17313
+ import { execFile as execFile6 } from "node:child_process";
17314
+ import { promisify as promisify6 } from "node:util";
17059
17315
  async function runDoctor(root, opts = {}) {
17060
17316
  const checks = [];
17061
17317
  const add = (name, ok2, detail) => checks.push({ name, ok: ok2, detail });
@@ -17108,6 +17364,9 @@ async function runDoctor(root, opts = {}) {
17108
17364
  installed ? "installed" : detected ? "not installed. Run npx brainjar init" : "skipped (tool not detected on this machine)"
17109
17365
  );
17110
17366
  }
17367
+ for (const tool of CAPTURE_TOOLS) {
17368
+ if (tool.mcp) checks.push(memoryCheck(tool, root, link?.brainId ?? null));
17369
+ }
17111
17370
  add(
17112
17371
  "git post-commit hook",
17113
17372
  gitHookInstalled(root),
@@ -17125,7 +17384,7 @@ async function runDoctor(root, opts = {}) {
17125
17384
  }
17126
17385
  if (opts.runtime === false) return { checks, ok: checks.every((c) => c.ok) };
17127
17386
  try {
17128
- const { stdout } = await run5("npx", ["--yes", "tsx", "--version"], {
17387
+ const { stdout } = await run6("npx", ["--yes", "tsx", "--version"], {
17129
17388
  cwd: root,
17130
17389
  shell: true,
17131
17390
  // Windows resolves npx.cmd
@@ -17138,6 +17397,37 @@ async function runDoctor(root, opts = {}) {
17138
17397
  }
17139
17398
  return { checks, ok: checks.every((c) => c.ok) };
17140
17399
  }
17400
+ function memoryCheck(tool, root, brainId) {
17401
+ const name = `${tool.name} memory`;
17402
+ const mcp = tool.mcp;
17403
+ if (!tool.detect(root))
17404
+ return {
17405
+ name,
17406
+ ok: true,
17407
+ detail: "skipped (tool not detected on this machine)"
17408
+ };
17409
+ const url = mcp.url(root);
17410
+ if (!brainId)
17411
+ return {
17412
+ name,
17413
+ ok: true,
17414
+ detail: url ? `points to Brain ${brainOf(url) ?? "?"}` : "skipped (not connected)"
17415
+ };
17416
+ if (!url)
17417
+ return {
17418
+ name,
17419
+ ok: false,
17420
+ detail: `no entry in ${mcp.where}. Run npx brainjar init`
17421
+ };
17422
+ const brain = brainOf(url);
17423
+ if (brain !== brainId)
17424
+ return {
17425
+ name,
17426
+ ok: false,
17427
+ detail: `${mcp.where} points to Brain ${brain ?? "?"}, capture uses ${brainId}. Run npx brainjar init`
17428
+ };
17429
+ return { name, ok: true, detail: `${mcp.where} points to this Brain` };
17430
+ }
17141
17431
  function renderDoctorReport(report) {
17142
17432
  const lines = report.checks.map(
17143
17433
  (c) => ` ${c.ok ? "\u2713" : "\u2717"} ${c.name.padEnd(20)} ${c.detail}`
@@ -17161,14 +17451,14 @@ brainjar doctor. repo: ${root}
17161
17451
  process.exitCode = 1;
17162
17452
  }
17163
17453
  }
17164
- var run5;
17454
+ var run6;
17165
17455
  var init_doctor = __esm({
17166
17456
  "packages/cli/src/doctor.ts"() {
17167
17457
  "use strict";
17168
17458
  init_src3();
17169
17459
  init_api_client();
17170
17460
  init_version_check();
17171
- run5 = promisify5(execFile5);
17461
+ run6 = promisify6(execFile6);
17172
17462
  }
17173
17463
  });
17174
17464
 
@@ -17179,7 +17469,7 @@ __export(deepscan_exports, {
17179
17469
  selectDeepScanFiles: () => selectDeepScanFiles
17180
17470
  });
17181
17471
  import { readdir as readdir3, readFile as readFile13, stat as stat2 } from "node:fs/promises";
17182
- import path18 from "node:path";
17472
+ import path19 from "node:path";
17183
17473
  function selectDeepScanFiles(candidates, graph, maxFiles = MAX_FILES) {
17184
17474
  const picked = [];
17185
17475
  const has = /* @__PURE__ */ new Set();
@@ -17216,7 +17506,7 @@ async function walkCandidates(root) {
17216
17506
  for (const e of entries) {
17217
17507
  if (!e.isFile()) continue;
17218
17508
  const parent = e.parentPath ?? e.path ?? root;
17219
- const rel = path18.relative(root, path18.join(parent, e.name)).split(path18.sep).join("/");
17509
+ const rel = path19.relative(root, path19.join(parent, e.name)).split(path19.sep).join("/");
17220
17510
  if (rel.split("/").some((seg) => SKIP_DIRS.has(seg))) continue;
17221
17511
  out.push(rel);
17222
17512
  }
@@ -17239,7 +17529,7 @@ async function deepscanMain() {
17239
17529
  const artifacts = [];
17240
17530
  for (const rel of files) {
17241
17531
  try {
17242
- const abs = path18.join(root, rel);
17532
+ const abs = path19.join(root, rel);
17243
17533
  const info = await stat2(abs);
17244
17534
  if (info.size > RAW_SKIP_BYTES) continue;
17245
17535
  const content = await readFile13(abs, "utf8");
@@ -17351,6 +17641,9 @@ async function unhookMain() {
17351
17641
  console.log(
17352
17642
  removed ? `${tool.name}: BrainJar hooks removed` : `${tool.name}: no BrainJar hooks`
17353
17643
  );
17644
+ if (!tool.mcp) continue;
17645
+ const memory = await tool.mcp.remove(root).catch(() => false);
17646
+ if (memory) console.log(`${tool.name}: memory entry removed`);
17354
17647
  }
17355
17648
  const git = await removeGitPostCommitHook(root);
17356
17649
  console.log(
@@ -17458,7 +17751,7 @@ async function main() {
17458
17751
  return;
17459
17752
  }
17460
17753
  try {
17461
- await run6(cmd, argv);
17754
+ await run7(cmd, argv);
17462
17755
  } finally {
17463
17756
  if (cmd !== "hook" && !argv.includes("--hook")) {
17464
17757
  const { noticeNewerVersion: noticeNewerVersion2 } = await Promise.resolve().then(() => (init_version_check(), version_check_exports));
@@ -17466,7 +17759,7 @@ async function main() {
17466
17759
  }
17467
17760
  }
17468
17761
  }
17469
- async function run6(cmd, argv) {
17762
+ async function run7(cmd, argv) {
17470
17763
  switch (cmd) {
17471
17764
  case "brief": {
17472
17765
  const { briefMain: briefMain2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainjar",
3
- "version": "0.3.711",
3
+ "version": "0.3.713",
4
4
  "description": "BrainJar CLI: turn a repository into a live, self-documenting Brain.",
5
5
  "type": "module",
6
6
  "bin": {