brainjar 0.3.702 → 0.3.703

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 +116 -51
  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.702";
110
+ CLI_VERSION = "0.3.703";
111
111
  CACHE = path.join(os.homedir(), ".brainjar", "version-check.json");
112
112
  DAY = 864e5;
113
113
  }
@@ -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 promisify5(Promise2, callback) {
4590
+ function promisify6(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 = promisify5(this.Promise, cb);
4728
+ const response = promisify6(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 = promisify5(this.Promise, text);
4913
+ const response2 = promisify6(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 = promisify5(this.Promise, cb);
4923
+ const response = promisify6(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 = promisify5(this.Promise, cb);
4968
+ const promised = promisify6(this.Promise, cb);
4969
4969
  this._endCallback = promised.callback;
4970
4970
  this._pulseQueue();
4971
4971
  return promised.result;
@@ -15143,13 +15143,20 @@ __export(brief_exports, {
15143
15143
  BRIEF_FILE: () => BRIEF_FILE,
15144
15144
  BRIEF_START: () => BRIEF_START,
15145
15145
  CLAUDE_IMPORT: () => CLAUDE_IMPORT,
15146
+ CURSOR_RULE_FILE: () => CURSOR_RULE_FILE,
15147
+ POINTER: () => POINTER,
15146
15148
  briefMain: () => briefMain,
15147
15149
  claudeMdWithImport: () => claudeMdWithImport,
15148
- refreshBrief: () => refreshBrief,
15149
- upsertBriefBlock: () => upsertBriefBlock
15150
+ cursorRule: () => cursorRule,
15151
+ installPointer: () => installPointer,
15152
+ refreshCursorRules: () => refreshCursorRules,
15153
+ upsertBriefBlock: () => upsertBriefBlock,
15154
+ withExcluded: () => withExcluded
15150
15155
  });
15151
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync3, existsSync as existsSync11 } from "node:fs";
15156
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync3 } from "node:fs";
15152
15157
  import path15 from "node:path";
15158
+ import { execFile as execFile3 } from "node:child_process";
15159
+ import { promisify as promisify3 } from "node:util";
15153
15160
  function claudeMdWithImport(existing) {
15154
15161
  let text = existing;
15155
15162
  const start = text.indexOf(BRIEF_START);
@@ -15180,6 +15187,7 @@ function upsertBriefBlock(existing, brief) {
15180
15187
  const block = `${BRIEF_START}
15181
15188
 
15182
15189
  ${defuseMarkers(brief).trimEnd()}
15190
+
15183
15191
  ${BRIEF_END}
15184
15192
  `;
15185
15193
  const start = existing.indexOf(BRIEF_START);
@@ -15192,18 +15200,11 @@ ${BRIEF_END}
15192
15200
  if (!existing.trim()) return block;
15193
15201
  return existing.trimEnd() + "\n\n" + block;
15194
15202
  }
15195
- async function refreshBrief(cwd) {
15203
+ function installPointer(root) {
15196
15204
  if (loadEnv().BRAINJAR_NO_BRIEF === "1") return "skipped";
15197
- const root = await repoRoot(cwd);
15198
- const link = await captureProject(root);
15199
- if (!link) return "skipped";
15200
- const { text } = await captureApi(
15201
- `/capture/brief?brain=${link.brainId}`
15202
- );
15203
- if (!text) return "skipped";
15204
15205
  const file = path15.join(root, BRIEF_FILE);
15205
15206
  const existing = existsSync11(file) ? readFileSync11(file, "utf8") : "";
15206
- const next = upsertBriefBlock(existing, text);
15207
+ const next = upsertBriefBlock(existing, POINTER);
15207
15208
  if (next !== null) writeFileSync3(file, next);
15208
15209
  const claudeFile = path15.join(root, "CLAUDE.md");
15209
15210
  const claude = existsSync11(claudeFile) ? readFileSync11(claudeFile, "utf8") : "";
@@ -15211,28 +15212,95 @@ async function refreshBrief(cwd) {
15211
15212
  if (claudeNext !== null) writeFileSync3(claudeFile, claudeNext);
15212
15213
  return next !== null || claudeNext !== null ? "updated" : "unchanged";
15213
15214
  }
15215
+ function cursorRule(brief) {
15216
+ return [
15217
+ "---",
15218
+ "description: BrainJar session context (generated by brainjar, kept out of git)",
15219
+ "alwaysApply: true",
15220
+ "---",
15221
+ "",
15222
+ brief.trimEnd(),
15223
+ ""
15224
+ ].join("\n");
15225
+ }
15226
+ function withExcluded(existing, line) {
15227
+ if (existing.split(/\r?\n/).some((l) => l.trim() === line)) return null;
15228
+ const sep = existing.length && !existing.endsWith("\n") ? "\n" : "";
15229
+ return `${existing}${sep}${line}
15230
+ `;
15231
+ }
15232
+ async function refreshCursorRules(root) {
15233
+ if (loadEnv().BRAINJAR_NO_BRIEF === "1") return "skipped";
15234
+ const cursor3 = CAPTURE_TOOLS.find((t) => t.id === "cursor");
15235
+ if (!cursor3?.detect(root)) return "skipped";
15236
+ const link = await captureProject(root);
15237
+ if (!link) return "skipped";
15238
+ const { text } = await captureApi(
15239
+ `/capture/brief?brain=${link.brainId}`
15240
+ );
15241
+ if (!text) return "skipped";
15242
+ const file = path15.join(root, CURSOR_RULE_FILE);
15243
+ const next = cursorRule(text);
15244
+ const changed = !existsSync11(file) || readFileSync11(file, "utf8") !== next;
15245
+ if (changed) {
15246
+ mkdirSync3(path15.dirname(file), { recursive: true });
15247
+ writeFileSync3(file, next);
15248
+ }
15249
+ try {
15250
+ const { stdout } = await run3(
15251
+ "git",
15252
+ ["rev-parse", "--path-format=absolute", "--git-path", "info/exclude"],
15253
+ { cwd: root, windowsHide: true }
15254
+ );
15255
+ const exclude = stdout.trim();
15256
+ const current = existsSync11(exclude) ? readFileSync11(exclude, "utf8") : "";
15257
+ const withLine = withExcluded(current, CURSOR_RULE_FILE);
15258
+ if (withLine !== null) {
15259
+ mkdirSync3(path15.dirname(exclude), { recursive: true });
15260
+ writeFileSync3(exclude, withLine);
15261
+ }
15262
+ } catch {
15263
+ }
15264
+ return changed ? "updated" : "unchanged";
15265
+ }
15214
15266
  async function briefMain() {
15215
15267
  try {
15216
- const result = await refreshBrief(process.cwd());
15268
+ const root = await repoRoot(process.cwd());
15269
+ const link = await captureProject(root);
15270
+ if (!link) {
15271
+ console.error(notConnected(root));
15272
+ process.exitCode = 1;
15273
+ return;
15274
+ }
15275
+ const { text } = await captureApi(
15276
+ `/capture/brief?brain=${link.brainId}`
15277
+ );
15217
15278
  console.log(
15218
- result === "updated" ? "AGENTS.md brief block updated (CLAUDE.md imports it)." : result === "unchanged" ? "AGENTS.md brief block already current." : "skipped (no connected Brain, empty brief, or BRAINJAR_NO_BRIEF=1)"
15279
+ text || "The Brain has no features yet; the brief fills in as captures land."
15219
15280
  );
15220
15281
  } catch (e) {
15221
15282
  console.error("brainjar brief:", e instanceof Error ? e.message : e);
15222
15283
  process.exitCode = 1;
15223
15284
  }
15224
15285
  }
15225
- var BRIEF_START, BRIEF_END, BRIEF_FILE, CLAUDE_IMPORT;
15286
+ var run3, BRIEF_START, BRIEF_END, BRIEF_FILE, CLAUDE_IMPORT, CURSOR_RULE_FILE, POINTER;
15226
15287
  var init_brief = __esm({
15227
15288
  "packages/cli/src/brief.ts"() {
15228
15289
  "use strict";
15229
15290
  init_src3();
15230
15291
  init_src();
15231
15292
  init_api_client();
15293
+ run3 = promisify3(execFile3);
15232
15294
  BRIEF_START = "<!-- brainjar:brief:start -->";
15233
15295
  BRIEF_END = "<!-- brainjar:brief:end -->";
15234
15296
  BRIEF_FILE = "AGENTS.md";
15235
15297
  CLAUDE_IMPORT = "@AGENTS.md";
15298
+ CURSOR_RULE_FILE = ".cursor/rules/brainjar.mdc";
15299
+ POINTER = [
15300
+ "## Memory",
15301
+ "",
15302
+ "This project uses BrainJar as its memory. Before non-trivial work, search it (MCP tool `search_memory`; `get_negative_constraints` before trying an approach that may have failed before). The session context (working discipline, reuse index, project rules, known failures) arrives at session start from the server, never from this file."
15303
+ ].join("\n");
15236
15304
  }
15237
15305
  });
15238
15306
 
@@ -15242,12 +15310,12 @@ __export(debt_exports, {
15242
15310
  debtMain: () => debtMain,
15243
15311
  scanDebtInRepo: () => scanDebtInRepo
15244
15312
  });
15245
- import { execFile as execFile3 } from "node:child_process";
15313
+ import { execFile as execFile4 } from "node:child_process";
15246
15314
  import { readFile as readFile11 } from "node:fs/promises";
15247
- import { promisify as promisify3 } from "node:util";
15315
+ import { promisify as promisify4 } from "node:util";
15248
15316
  import path16 from "node:path";
15249
15317
  async function scanDebtInRepo(root) {
15250
- const { stdout } = await run3("git", ["-C", root, "ls-files"], {
15318
+ const { stdout } = await run4("git", ["-C", root, "ls-files"], {
15251
15319
  maxBuffer: 16 * 1048576
15252
15320
  });
15253
15321
  const files = [];
@@ -15286,13 +15354,13 @@ async function debtMain() {
15286
15354
  `);
15287
15355
  }
15288
15356
  }
15289
- var run3;
15357
+ var run4;
15290
15358
  var init_debt = __esm({
15291
15359
  "packages/cli/src/debt.ts"() {
15292
15360
  "use strict";
15293
15361
  init_src3();
15294
15362
  init_api_client();
15295
- run3 = promisify3(execFile3);
15363
+ run4 = promisify4(execFile4);
15296
15364
  }
15297
15365
  });
15298
15366
 
@@ -16409,7 +16477,7 @@ __export(hook_exports, {
16409
16477
  import {
16410
16478
  appendFileSync,
16411
16479
  existsSync as existsSync12,
16412
- mkdirSync as mkdirSync3,
16480
+ mkdirSync as mkdirSync4,
16413
16481
  readdirSync,
16414
16482
  readFileSync as readFileSync12,
16415
16483
  rmSync,
@@ -16504,7 +16572,7 @@ async function hookMain(argv) {
16504
16572
  if (out.reply) process.stdout.write(out.reply);
16505
16573
  const file = spoolPath(adapter.id, out.sessionId);
16506
16574
  if (out.events.length) {
16507
- mkdirSync3(path17.dirname(file), { recursive: true });
16575
+ mkdirSync4(path17.dirname(file), { recursive: true });
16508
16576
  appendFileSync(
16509
16577
  file,
16510
16578
  out.events.map((e) => JSON.stringify(clampCaptureEvent(e))).join("\n") + "\n"
@@ -16615,10 +16683,9 @@ async function syncRepo(root) {
16615
16683
  }
16616
16684
  } finally {
16617
16685
  try {
16618
- const { refreshBrief: refreshBrief2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
16619
- const r = await refreshBrief2(root);
16620
- if (r === "updated")
16621
- console.error("brainjar: AGENTS.md brief block updated");
16686
+ const { refreshCursorRules: refreshCursorRules2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
16687
+ if (await refreshCursorRules2(root) === "updated")
16688
+ console.error("brainjar: Cursor rule file refreshed");
16622
16689
  } catch {
16623
16690
  }
16624
16691
  }
@@ -16783,15 +16850,20 @@ async function init(argv) {
16783
16850
  ok(
16784
16851
  gitHook ? "git post-commit hook installed" : "git post-commit hook already present"
16785
16852
  );
16786
- step(7, "Informed laziness \u2014 the brief block in AGENTS.md\u2026");
16853
+ step(7, "Informed laziness \u2014 the memory pointer in AGENTS.md\u2026");
16787
16854
  try {
16788
- const { refreshBrief: refreshBrief2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
16789
- const r = await refreshBrief2(root);
16855
+ const { installPointer: installPointer2, refreshCursorRules: refreshCursorRules2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
16856
+ const pointer = installPointer2(root);
16790
16857
  ok(
16791
- r === "updated" ? "brief block written (discipline core + reuse index, kept current by sync)" : r === "unchanged" ? "brief block already current" : "skipped (empty Brain or BRAINJAR_NO_BRIEF=1) \u2014 sync adds it once features exist"
16858
+ pointer === "updated" ? "AGENTS.md points to the Brain (static, never changes again); the brief itself arrives at every session start" : pointer === "unchanged" ? "AGENTS.md pointer already in place" : "skipped (BRAINJAR_NO_BRIEF=1)"
16792
16859
  );
16860
+ const cursor3 = await refreshCursorRules2(root);
16861
+ if (cursor3 !== "skipped")
16862
+ ok(
16863
+ `Cursor rule file ${cursor3} (.cursor/rules/brainjar.mdc, kept out of git)`
16864
+ );
16793
16865
  } catch {
16794
- ok("skipped (server unreachable) \u2014 the sync hook will add it");
16866
+ ok("skipped (could not write the pointer)");
16795
16867
  }
16796
16868
  try {
16797
16869
  await captureApi("/capture/connect", {
@@ -16892,13 +16964,6 @@ async function constraintsMain(argv) {
16892
16964
  else if (hook && argv.includes("--cline")) console.log(CLINE_REPLY);
16893
16965
  else if (!hook)
16894
16966
  console.log("No recorded failed approaches or rules for this Brain.");
16895
- if (hook) {
16896
- try {
16897
- const { refreshBrief: refreshBrief2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
16898
- await refreshBrief2(root);
16899
- } catch {
16900
- }
16901
- }
16902
16967
  } catch (e) {
16903
16968
  if (hook && e instanceof TokenError) console.log(envelope(e.message));
16904
16969
  else
@@ -16948,8 +17013,8 @@ __export(doctor_exports, {
16948
17013
  renderDoctorReport: () => renderDoctorReport,
16949
17014
  runDoctor: () => runDoctor
16950
17015
  });
16951
- import { execFile as execFile4 } from "node:child_process";
16952
- import { promisify as promisify4 } from "node:util";
17016
+ import { execFile as execFile5 } from "node:child_process";
17017
+ import { promisify as promisify5 } from "node:util";
16953
17018
  async function runDoctor(root, opts = {}) {
16954
17019
  const checks = [];
16955
17020
  const add = (name, ok2, detail) => checks.push({ name, ok: ok2, detail });
@@ -17019,7 +17084,7 @@ async function runDoctor(root, opts = {}) {
17019
17084
  }
17020
17085
  if (opts.runtime === false) return { checks, ok: checks.every((c) => c.ok) };
17021
17086
  try {
17022
- const { stdout } = await run4("npx", ["--yes", "tsx", "--version"], {
17087
+ const { stdout } = await run5("npx", ["--yes", "tsx", "--version"], {
17023
17088
  cwd: root,
17024
17089
  shell: true,
17025
17090
  // Windows resolves npx.cmd
@@ -17055,14 +17120,14 @@ brainjar doctor. repo: ${root}
17055
17120
  process.exitCode = 1;
17056
17121
  }
17057
17122
  }
17058
- var run4;
17123
+ var run5;
17059
17124
  var init_doctor = __esm({
17060
17125
  "packages/cli/src/doctor.ts"() {
17061
17126
  "use strict";
17062
17127
  init_src3();
17063
17128
  init_api_client();
17064
17129
  init_version_check();
17065
- run4 = promisify4(execFile4);
17130
+ run5 = promisify5(execFile5);
17066
17131
  }
17067
17132
  });
17068
17133
 
@@ -17352,7 +17417,7 @@ async function main() {
17352
17417
  return;
17353
17418
  }
17354
17419
  try {
17355
- await run5(cmd, argv);
17420
+ await run6(cmd, argv);
17356
17421
  } finally {
17357
17422
  if (cmd !== "hook" && !argv.includes("--hook")) {
17358
17423
  const { noticeNewerVersion: noticeNewerVersion2 } = await Promise.resolve().then(() => (init_version_check(), version_check_exports));
@@ -17360,7 +17425,7 @@ async function main() {
17360
17425
  }
17361
17426
  }
17362
17427
  }
17363
- async function run5(cmd, argv) {
17428
+ async function run6(cmd, argv) {
17364
17429
  switch (cmd) {
17365
17430
  case "brief": {
17366
17431
  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.702",
3
+ "version": "0.3.703",
4
4
  "description": "BrainJar CLI: turn a repository into a live, self-documenting Brain.",
5
5
  "type": "module",
6
6
  "bin": {