brainjar 0.3.679 → 0.3.684

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 +311 -87
  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.679";
110
+ CLI_VERSION = "0.3.684";
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(path14, params) {
242
- let url = this.buildUrl(path14, params);
241
+ async *paginate(path15, params) {
242
+ let url = this.buildUrl(path15, 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(path14, params) {
253
+ buildUrl(path15, params) {
254
254
  const url = new URL(
255
- `${this.baseUrl}/repos/${this.opts.owner}/${this.opts.repo}${path14}`
255
+ `${this.baseUrl}/repos/${this.opts.owner}/${this.opts.repo}${path15}`
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 path14 = __require("path");
3738
+ var path15 = __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 ? path14.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path14.join(env.HOME || "./", ".pgpass"));
3777
+ var file = env.PGPASSFILE || (isWin ? path15.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path15.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 path14 = __require("path");
3909
+ var path15 = __require("path");
3910
3910
  var fs = __require("fs");
3911
3911
  var helper = require_helper();
3912
3912
  module.exports = function(connInfo, cb) {
@@ -8850,12 +8850,12 @@ var init_path = __esm({
8850
8850
  "node_modules/@anthropic-ai/sdk/internal/utils/path.mjs"() {
8851
8851
  init_error();
8852
8852
  EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
8853
- createPathTagFunction = (pathEncoder = encodeURIPath) => function path14(statics, ...params) {
8853
+ createPathTagFunction = (pathEncoder = encodeURIPath) => function path15(statics, ...params) {
8854
8854
  if (statics.length === 1)
8855
8855
  return statics[0];
8856
8856
  let postPath = false;
8857
8857
  const invalidSegments = [];
8858
- const path15 = statics.reduce((previousValue, currentValue, index) => {
8858
+ const path16 = statics.reduce((previousValue, currentValue, index) => {
8859
8859
  if (/[?#]/.test(currentValue)) {
8860
8860
  postPath = true;
8861
8861
  }
@@ -8872,7 +8872,7 @@ var init_path = __esm({
8872
8872
  }
8873
8873
  return previousValue + currentValue + (index === params.length ? "" : encoded);
8874
8874
  }, "");
8875
- const pathOnly = path15.split(/[?#]/, 1)[0];
8875
+ const pathOnly = path16.split(/[?#]/, 1)[0];
8876
8876
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
8877
8877
  let match;
8878
8878
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -8893,10 +8893,10 @@ var init_path = __esm({
8893
8893
  }, "");
8894
8894
  throw new AnthropicError(`Path parameters result in path with invalid segments:
8895
8895
  ${invalidSegments.map((e) => e.error).join("\n")}
8896
- ${path15}
8896
+ ${path16}
8897
8897
  ${underline}`);
8898
8898
  }
8899
- return path15;
8899
+ return path16;
8900
8900
  };
8901
8901
  path3 = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
8902
8902
  }
@@ -11882,9 +11882,9 @@ var init_client = __esm({
11882
11882
  makeStatusError(status, error, message, headers) {
11883
11883
  return APIError.generate(status, error, message, headers);
11884
11884
  }
11885
- buildURL(path14, query, defaultBaseURL) {
11885
+ buildURL(path15, query, defaultBaseURL) {
11886
11886
  const baseURL = !__classPrivateFieldGet(this, _BaseAnthropic_instances, "m", _BaseAnthropic_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
11887
- const url = isAbsoluteURL(path14) ? new URL(path14) : new URL(baseURL + (baseURL.endsWith("/") && path14.startsWith("/") ? path14.slice(1) : path14));
11887
+ const url = isAbsoluteURL(path15) ? new URL(path15) : new URL(baseURL + (baseURL.endsWith("/") && path15.startsWith("/") ? path15.slice(1) : path15));
11888
11888
  const defaultQuery = this.defaultQuery();
11889
11889
  if (!isEmptyObj(defaultQuery)) {
11890
11890
  query = { ...defaultQuery, ...query };
@@ -11915,24 +11915,24 @@ var init_client = __esm({
11915
11915
  */
11916
11916
  async prepareRequest(request, { url, options }) {
11917
11917
  }
11918
- get(path14, opts) {
11919
- return this.methodRequest("get", path14, opts);
11918
+ get(path15, opts) {
11919
+ return this.methodRequest("get", path15, opts);
11920
11920
  }
11921
- post(path14, opts) {
11922
- return this.methodRequest("post", path14, opts);
11921
+ post(path15, opts) {
11922
+ return this.methodRequest("post", path15, opts);
11923
11923
  }
11924
- patch(path14, opts) {
11925
- return this.methodRequest("patch", path14, opts);
11924
+ patch(path15, opts) {
11925
+ return this.methodRequest("patch", path15, opts);
11926
11926
  }
11927
- put(path14, opts) {
11928
- return this.methodRequest("put", path14, opts);
11927
+ put(path15, opts) {
11928
+ return this.methodRequest("put", path15, opts);
11929
11929
  }
11930
- delete(path14, opts) {
11931
- return this.methodRequest("delete", path14, opts);
11930
+ delete(path15, opts) {
11931
+ return this.methodRequest("delete", path15, opts);
11932
11932
  }
11933
- methodRequest(method, path14, opts) {
11933
+ methodRequest(method, path15, opts) {
11934
11934
  return this.request(Promise.resolve(opts).then((opts2) => {
11935
- return { method, path: path14, ...opts2 };
11935
+ return { method, path: path15, ...opts2 };
11936
11936
  }));
11937
11937
  }
11938
11938
  request(options, remainingRetries = null) {
@@ -12036,8 +12036,8 @@ var init_client = __esm({
12036
12036
  }));
12037
12037
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
12038
12038
  }
12039
- getAPIList(path14, Page2, opts) {
12040
- return this.requestAPIList(Page2, { method: "get", path: path14, ...opts });
12039
+ getAPIList(path15, Page2, opts) {
12040
+ return this.requestAPIList(Page2, { method: "get", path: path15, ...opts });
12041
12041
  }
12042
12042
  requestAPIList(Page2, options) {
12043
12043
  const request = this.makeRequest(options, null, void 0);
@@ -12124,8 +12124,8 @@ var init_client = __esm({
12124
12124
  }
12125
12125
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
12126
12126
  const options = { ...inputOptions };
12127
- const { method, path: path14, query, defaultBaseURL } = options;
12128
- const url = this.buildURL(path14, query, defaultBaseURL);
12127
+ const { method, path: path15, query, defaultBaseURL } = options;
12128
+ const url = this.buildURL(path15, query, defaultBaseURL);
12129
12129
  if ("timeout" in options)
12130
12130
  validatePositiveInteger("timeout", options.timeout);
12131
12131
  options.timeout = options.timeout ?? this.timeout;
@@ -13050,24 +13050,90 @@ var init_codex_hooks = __esm({
13050
13050
  }
13051
13051
  });
13052
13052
 
13053
+ // packages/ingest/src/copilot-hooks.ts
13054
+ import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, rm as rm2 } from "node:fs/promises";
13055
+ import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
13056
+ import path8 from "node:path";
13057
+ function render() {
13058
+ const hooks = {
13059
+ sessionStart: [
13060
+ {
13061
+ type: "command",
13062
+ command: `${captureHookCommand("constraints")} --copilot`,
13063
+ timeoutSec: 15
13064
+ }
13065
+ ]
13066
+ };
13067
+ for (const { event, timeoutSec } of EVENTS3) {
13068
+ hooks[event] = [
13069
+ {
13070
+ type: "command",
13071
+ command: `${captureHookCommand("hook copilot")} --event ${event}`,
13072
+ timeoutSec
13073
+ }
13074
+ ];
13075
+ }
13076
+ return JSON.stringify({ version: 1, hooks }, null, 2) + "\n";
13077
+ }
13078
+ async function installCopilotHooks(root) {
13079
+ const file = hooksFile3(root);
13080
+ const next = render();
13081
+ if (existsSync5(file) && await readFile5(file, "utf8") === next) return false;
13082
+ await mkdir4(path8.dirname(file), { recursive: true });
13083
+ await writeFile4(file, next, "utf8");
13084
+ return true;
13085
+ }
13086
+ function copilotHooksInstalled(root) {
13087
+ const file = hooksFile3(root);
13088
+ if (!existsSync5(file)) return false;
13089
+ try {
13090
+ return readFileSync6(file, "utf8").includes(PROBE2);
13091
+ } catch {
13092
+ return false;
13093
+ }
13094
+ }
13095
+ async function removeCopilotHooks(root) {
13096
+ const file = hooksFile3(root);
13097
+ if (!copilotHooksInstalled(root)) return false;
13098
+ await rm2(file, { force: true });
13099
+ return true;
13100
+ }
13101
+ var PROBE2, EVENTS3, hooksFile3;
13102
+ var init_copilot_hooks = __esm({
13103
+ "packages/ingest/src/copilot-hooks.ts"() {
13104
+ "use strict";
13105
+ init_bootstrap();
13106
+ PROBE2 = "hook copilot";
13107
+ EVENTS3 = [
13108
+ { event: "userPromptSubmitted", timeoutSec: 30 },
13109
+ { event: "postToolUse", timeoutSec: 30 },
13110
+ { event: "postToolUseFailure", timeoutSec: 30 },
13111
+ { event: "agentStop", timeoutSec: 120 },
13112
+ { event: "sessionEnd", timeoutSec: 120 }
13113
+ ];
13114
+ hooksFile3 = (root) => path8.join(root, ".github", "hooks", "brainjar.json");
13115
+ }
13116
+ });
13117
+
13053
13118
  // packages/ingest/src/tools.ts
13054
- import { existsSync as existsSync5 } from "node:fs";
13119
+ import { existsSync as existsSync6 } from "node:fs";
13055
13120
  import os4 from "node:os";
13056
- import path8 from "node:path";
13121
+ import path9 from "node:path";
13057
13122
  function detectedTools(root) {
13058
13123
  return CAPTURE_TOOLS.filter((t) => t.detect(root));
13059
13124
  }
13060
- var claudeCode, cursor, codex, CAPTURE_TOOLS;
13125
+ var claudeCode, cursor, codex, copilot, CAPTURE_TOOLS;
13061
13126
  var init_tools = __esm({
13062
13127
  "packages/ingest/src/tools.ts"() {
13063
13128
  "use strict";
13064
13129
  init_bootstrap();
13065
13130
  init_cursor_hooks();
13066
13131
  init_codex_hooks();
13132
+ init_copilot_hooks();
13067
13133
  claudeCode = {
13068
13134
  id: "claude-code",
13069
13135
  name: "Claude Code",
13070
- detect: (root) => existsSync5(path8.join(os4.homedir(), ".claude")) || existsSync5(path8.join(root, ".claude")),
13136
+ detect: (root) => existsSync6(path9.join(os4.homedir(), ".claude")) || existsSync6(path9.join(root, ".claude")),
13071
13137
  install: installCaptureHook,
13072
13138
  installed: captureHookInstalled,
13073
13139
  remove: removeCaptureHook
@@ -13075,7 +13141,7 @@ var init_tools = __esm({
13075
13141
  cursor = {
13076
13142
  id: "cursor",
13077
13143
  name: "Cursor",
13078
- detect: (root) => existsSync5(path8.join(os4.homedir(), ".cursor")) || existsSync5(path8.join(root, ".cursor")),
13144
+ detect: (root) => existsSync6(path9.join(os4.homedir(), ".cursor")) || existsSync6(path9.join(root, ".cursor")),
13079
13145
  install: installCursorHooks,
13080
13146
  installed: cursorHooksInstalled,
13081
13147
  remove: removeCursorHooks
@@ -13083,15 +13149,24 @@ var init_tools = __esm({
13083
13149
  codex = {
13084
13150
  id: "codex",
13085
13151
  name: "Codex",
13086
- detect: (root) => existsSync5(path8.join(os4.homedir(), ".codex")) || existsSync5(path8.join(root, ".codex")),
13152
+ detect: (root) => existsSync6(path9.join(os4.homedir(), ".codex")) || existsSync6(path9.join(root, ".codex")),
13087
13153
  install: installCodexHooks,
13088
13154
  installed: codexHooksInstalled,
13089
13155
  remove: removeCodexHooks
13090
13156
  };
13157
+ copilot = {
13158
+ id: "copilot",
13159
+ name: "GitHub Copilot",
13160
+ detect: (root) => existsSync6(path9.join(os4.homedir(), ".copilot")) || existsSync6(path9.join(root, ".github", "hooks")),
13161
+ install: installCopilotHooks,
13162
+ installed: copilotHooksInstalled,
13163
+ remove: removeCopilotHooks
13164
+ };
13091
13165
  CAPTURE_TOOLS = [
13092
13166
  claudeCode,
13093
13167
  cursor,
13094
- codex
13168
+ codex,
13169
+ copilot
13095
13170
  ];
13096
13171
  }
13097
13172
  });
@@ -13568,8 +13643,8 @@ var init_doku_model = __esm({
13568
13643
 
13569
13644
  // packages/ingest/src/doku.ts
13570
13645
  import { createHash as createHash2 } from "node:crypto";
13571
- import { mkdir as mkdir4, readFile as readFile5, rm as rm2, stat, writeFile as writeFile4 } from "node:fs/promises";
13572
- import path9 from "node:path";
13646
+ import { mkdir as mkdir5, readFile as readFile6, rm as rm3, stat, writeFile as writeFile5 } from "node:fs/promises";
13647
+ import path10 from "node:path";
13573
13648
  function bodyLocations(o) {
13574
13649
  return Array.isArray(o.body.location) ? o.body.location.map(String) : [];
13575
13650
  }
@@ -13635,7 +13710,7 @@ async function loadDokuData(brainId, root) {
13635
13710
  if (root) {
13636
13711
  try {
13637
13712
  const pkg = JSON.parse(
13638
- await readFile5(path9.join(root, "package.json"), "utf8")
13713
+ await readFile6(path10.join(root, "package.json"), "utf8")
13639
13714
  );
13640
13715
  description = pkg.description ?? null;
13641
13716
  scripts = pkg.scripts ?? null;
@@ -14218,11 +14293,11 @@ function dokuFilePath(kind, slug) {
14218
14293
  return `${DOKU_DIR}/${slug}.md`;
14219
14294
  }
14220
14295
  async function writeDokuTree(root, files) {
14221
- const dir = path9.join(root, DOKU_DIR);
14222
- const manifestPath = path9.join(dir, MANIFEST);
14296
+ const dir = path10.join(root, DOKU_DIR);
14297
+ const manifestPath = path10.join(dir, MANIFEST);
14223
14298
  let old = { files: {} };
14224
14299
  try {
14225
- old = JSON.parse(await readFile5(manifestPath, "utf8"));
14300
+ old = JSON.parse(await readFile6(manifestPath, "utf8"));
14226
14301
  if (typeof old.files !== "object" || old.files === null)
14227
14302
  old = { files: {} };
14228
14303
  } catch {
@@ -14232,21 +14307,21 @@ async function writeDokuTree(root, files) {
14232
14307
  for (const file of files) {
14233
14308
  const hash = sha1(file.markdown);
14234
14309
  next[file.relPath] = { hash };
14235
- const abs = path9.join(root, file.relPath);
14310
+ const abs = path10.join(root, file.relPath);
14236
14311
  if (old.files[file.relPath]?.hash === hash && await exists(abs)) continue;
14237
- await mkdir4(path9.dirname(abs), { recursive: true });
14238
- await writeFile4(abs, file.markdown, "utf8");
14312
+ await mkdir5(path10.dirname(abs), { recursive: true });
14313
+ await writeFile5(abs, file.markdown, "utf8");
14239
14314
  written++;
14240
14315
  }
14241
14316
  let deleted = 0;
14242
14317
  for (const rel of Object.keys(old.files)) {
14243
14318
  if (next[rel]) continue;
14244
14319
  if (rel !== DOKU_FILENAME && !rel.startsWith(`${DOKU_DIR}/`)) continue;
14245
- await rm2(path9.join(root, rel), { force: true });
14320
+ await rm3(path10.join(root, rel), { force: true });
14246
14321
  deleted++;
14247
14322
  }
14248
- await mkdir4(dir, { recursive: true });
14249
- await writeFile4(
14323
+ await mkdir5(dir, { recursive: true });
14324
+ await writeFile5(
14250
14325
  manifestPath,
14251
14326
  JSON.stringify({ files: next }, null, 2),
14252
14327
  "utf8"
@@ -14399,8 +14474,8 @@ var init_ponytail_brief = __esm({
14399
14474
  });
14400
14475
 
14401
14476
  // packages/ingest/src/ponytail-debt.ts
14402
- function isDebtScannable(path14) {
14403
- return !/\.(md|mdx|markdown|txt|jsonl|log|lock)$/i.test(path14);
14477
+ function isDebtScannable(path15) {
14478
+ return !/\.(md|mdx|markdown|txt|jsonl|log|lock)$/i.test(path15);
14404
14479
  }
14405
14480
  function scanPonytailComments(files) {
14406
14481
  const out = [];
@@ -14566,6 +14641,7 @@ var init_src3 = __esm({
14566
14641
  init_tools();
14567
14642
  init_cursor_hooks();
14568
14643
  init_codex_hooks();
14644
+ init_copilot_hooks();
14569
14645
  init_commit_links();
14570
14646
  init_tool_failure();
14571
14647
  init_bootstrap();
@@ -14651,8 +14727,8 @@ __export(brief_exports, {
14651
14727
  refreshBrief: () => refreshBrief,
14652
14728
  upsertBriefBlock: () => upsertBriefBlock
14653
14729
  });
14654
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync6 } from "node:fs";
14655
- import path10 from "node:path";
14730
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync7 } from "node:fs";
14731
+ import path11 from "node:path";
14656
14732
  function claudeMdWithImport(existing) {
14657
14733
  let text = existing;
14658
14734
  const start = text.indexOf(BRIEF_START);
@@ -14706,12 +14782,12 @@ async function refreshBrief(cwd) {
14706
14782
  `/capture/brief?brain=${project.brainId}`
14707
14783
  );
14708
14784
  if (!text) return "skipped";
14709
- const file = path10.join(root, BRIEF_FILE);
14710
- const existing = existsSync6(file) ? readFileSync6(file, "utf8") : "";
14785
+ const file = path11.join(root, BRIEF_FILE);
14786
+ const existing = existsSync7(file) ? readFileSync7(file, "utf8") : "";
14711
14787
  const next = upsertBriefBlock(existing, text);
14712
14788
  if (next !== null) writeFileSync3(file, next);
14713
- const claudeFile = path10.join(root, "CLAUDE.md");
14714
- const claude = existsSync6(claudeFile) ? readFileSync6(claudeFile, "utf8") : "";
14789
+ const claudeFile = path11.join(root, "CLAUDE.md");
14790
+ const claude = existsSync7(claudeFile) ? readFileSync7(claudeFile, "utf8") : "";
14715
14791
  const claudeNext = claudeMdWithImport(claude);
14716
14792
  if (claudeNext !== null) writeFileSync3(claudeFile, claudeNext);
14717
14793
  return next !== null || claudeNext !== null ? "updated" : "unchanged";
@@ -14748,9 +14824,9 @@ __export(debt_exports, {
14748
14824
  scanDebtInRepo: () => scanDebtInRepo
14749
14825
  });
14750
14826
  import { execFile as execFile3 } from "node:child_process";
14751
- import { readFile as readFile6 } from "node:fs/promises";
14827
+ import { readFile as readFile7 } from "node:fs/promises";
14752
14828
  import { promisify as promisify3 } from "node:util";
14753
- import path11 from "node:path";
14829
+ import path12 from "node:path";
14754
14830
  async function scanDebtInRepo(root) {
14755
14831
  const { stdout } = await run3("git", ["-C", root, "ls-files"], {
14756
14832
  maxBuffer: 16 * 1048576
@@ -14760,7 +14836,7 @@ async function scanDebtInRepo(root) {
14760
14836
  const p = rel.trim();
14761
14837
  if (!p || !isDebtScannable(p)) continue;
14762
14838
  try {
14763
- const buf = await readFile6(path11.join(root, p));
14839
+ const buf = await readFile7(path12.join(root, p));
14764
14840
  if (buf.length > 512 * 1024 || buf.includes(0)) continue;
14765
14841
  files.push({ path: p, content: buf.toString("utf8") });
14766
14842
  } catch {
@@ -15186,11 +15262,11 @@ var init_codex = __esm({
15186
15262
  ...commits.length ? { commits } : {}
15187
15263
  });
15188
15264
  files.forEach(
15189
- (path14, i) => events.push({
15265
+ (path15, i) => events.push({
15190
15266
  ...base,
15191
15267
  event_id: `file:${id}:${i}`,
15192
15268
  kind: "file_changed",
15193
- path: path14
15269
+ path: path15
15194
15270
  })
15195
15271
  );
15196
15272
  }
@@ -15218,6 +15294,148 @@ var init_codex = __esm({
15218
15294
  }
15219
15295
  });
15220
15296
 
15297
+ // packages/cli/src/adapters/copilot.ts
15298
+ import { createHash as createHash6 } from "node:crypto";
15299
+ function targetOf3(args) {
15300
+ if (!args || typeof args !== "object") return "";
15301
+ const a = args;
15302
+ for (const k of ["command", "path", "file_path", "filePath", "file"]) {
15303
+ if (typeof a[k] === "string") return a[k];
15304
+ }
15305
+ return "";
15306
+ }
15307
+ function commitShas4(text) {
15308
+ const s = typeof text === "string" ? text : JSON.stringify(text ?? "");
15309
+ const out = /* @__PURE__ */ new Set();
15310
+ for (const m of s.matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
15311
+ out.add(m[1].toLowerCase());
15312
+ }
15313
+ return [...out];
15314
+ }
15315
+ function stamp(ts, now) {
15316
+ if (typeof ts === "number" && Number.isFinite(ts))
15317
+ return new Date(ts).toISOString();
15318
+ if (typeof ts === "string" && !Number.isNaN(Date.parse(ts)))
15319
+ return new Date(ts).toISOString();
15320
+ return now;
15321
+ }
15322
+ function copilotContextEnvelope(text) {
15323
+ return JSON.stringify({ additionalContext: text });
15324
+ }
15325
+ var EDIT_TOOLS2, short5, EVENT_ALIASES, copilot2;
15326
+ var init_copilot = __esm({
15327
+ "packages/cli/src/adapters/copilot.ts"() {
15328
+ "use strict";
15329
+ init_src();
15330
+ EDIT_TOOLS2 = /* @__PURE__ */ new Set(["edit", "create"]);
15331
+ short5 = (s) => createHash6("sha256").update(s).digest("hex").slice(0, 16);
15332
+ EVENT_ALIASES = {
15333
+ SessionStart: "sessionStart",
15334
+ SessionEnd: "sessionEnd",
15335
+ UserPromptSubmit: "userPromptSubmitted",
15336
+ PostToolUse: "postToolUse",
15337
+ PostToolUseFailure: "postToolUseFailure",
15338
+ Stop: "agentStop"
15339
+ };
15340
+ copilot2 = {
15341
+ id: "copilot",
15342
+ map(payload, now, opts) {
15343
+ const p = payload;
15344
+ const sessionId = p?.sessionId ?? p?.session_id;
15345
+ if (!p || typeof p !== "object" || typeof sessionId !== "string") {
15346
+ return null;
15347
+ }
15348
+ const raw = opts?.event ?? p.hook_event_name ?? "";
15349
+ const event = EVENT_ALIASES[raw] ?? raw;
15350
+ const at = stamp(p.timestamp, now);
15351
+ const base = {
15352
+ v: CAPTURE_EVENT_VERSION,
15353
+ tool: "copilot",
15354
+ session_id: sessionId,
15355
+ at,
15356
+ cwd: p.cwd ?? null
15357
+ };
15358
+ const events = [];
15359
+ let flush = false;
15360
+ let end = false;
15361
+ const toolName = p.toolName ?? p.tool_name ?? "tool";
15362
+ const args = p.toolArgs ?? p.tool_args;
15363
+ switch (event) {
15364
+ case "sessionStart":
15365
+ events.push({
15366
+ ...base,
15367
+ event_id: `start:${sessionId}`,
15368
+ kind: "session_started"
15369
+ });
15370
+ break;
15371
+ case "userPromptSubmitted":
15372
+ if (typeof p.prompt === "string" && p.prompt.trim()) {
15373
+ events.push({
15374
+ ...base,
15375
+ event_id: `prompt:${short5(at + p.prompt)}`,
15376
+ kind: "prompt",
15377
+ text: p.prompt
15378
+ });
15379
+ }
15380
+ break;
15381
+ case "postToolUse": {
15382
+ const target = targetOf3(args);
15383
+ const id = short5(at + toolName + target);
15384
+ const commits = commitShas4(p.toolResult?.textResultForLlm);
15385
+ events.push({
15386
+ ...base,
15387
+ event_id: `use:${id}`,
15388
+ kind: "tool_used",
15389
+ tool_name: toolName,
15390
+ ...target ? { target } : {},
15391
+ ...commits.length ? { commits } : {}
15392
+ });
15393
+ if (EDIT_TOOLS2.has(toolName) && target) {
15394
+ events.push({
15395
+ ...base,
15396
+ event_id: `file:${id}`,
15397
+ kind: "file_changed",
15398
+ path: target
15399
+ });
15400
+ }
15401
+ break;
15402
+ }
15403
+ case "postToolUseFailure": {
15404
+ const error = typeof p.error === "string" ? p.error : p.error && typeof p.error === "object" ? String(p.error.message ?? "") : "";
15405
+ if (!error.trim()) break;
15406
+ const target = targetOf3(args);
15407
+ events.push({
15408
+ ...base,
15409
+ event_id: `fail:${short5(at + toolName + target + error)}`,
15410
+ kind: "tool_failed",
15411
+ tool_name: toolName,
15412
+ ...target ? { target } : {},
15413
+ error
15414
+ });
15415
+ break;
15416
+ }
15417
+ case "agentStop":
15418
+ flush = true;
15419
+ break;
15420
+ case "sessionEnd":
15421
+ events.push({
15422
+ ...base,
15423
+ event_id: `end:${sessionId}`,
15424
+ kind: "session_ended",
15425
+ ...p.reason ? { reason: p.reason } : {}
15426
+ });
15427
+ flush = true;
15428
+ end = true;
15429
+ break;
15430
+ default:
15431
+ return null;
15432
+ }
15433
+ return { sessionId, cwd: p.cwd ?? null, events, flush, end };
15434
+ }
15435
+ };
15436
+ }
15437
+ });
15438
+
15221
15439
  // packages/cli/src/adapters/index.ts
15222
15440
  var ADAPTERS;
15223
15441
  var init_adapters = __esm({
@@ -15226,10 +15444,12 @@ var init_adapters = __esm({
15226
15444
  init_claude_code();
15227
15445
  init_cursor();
15228
15446
  init_codex();
15447
+ init_copilot();
15229
15448
  ADAPTERS = {
15230
15449
  [claudeCode2.id]: claudeCode2,
15231
15450
  [cursor2.id]: cursor2,
15232
- [codex2.id]: codex2
15451
+ [codex2.id]: codex2,
15452
+ [copilot2.id]: copilot2
15233
15453
  };
15234
15454
  }
15235
15455
  });
@@ -15243,17 +15463,17 @@ __export(hook_exports, {
15243
15463
  });
15244
15464
  import {
15245
15465
  appendFileSync,
15246
- existsSync as existsSync7,
15466
+ existsSync as existsSync8,
15247
15467
  mkdirSync as mkdirSync3,
15248
15468
  readdirSync,
15249
- readFileSync as readFileSync7,
15469
+ readFileSync as readFileSync8,
15250
15470
  rmSync,
15251
15471
  statSync as statSync2
15252
15472
  } from "node:fs";
15253
15473
  import os5 from "node:os";
15254
- import path12 from "node:path";
15474
+ import path13 from "node:path";
15255
15475
  function spoolPath(tool, sessionId) {
15256
- return path12.join(
15476
+ return path13.join(
15257
15477
  os5.homedir(),
15258
15478
  ".brainjar",
15259
15479
  "spool",
@@ -15263,7 +15483,7 @@ function spoolPath(tool, sessionId) {
15263
15483
  }
15264
15484
  function readSpool(file) {
15265
15485
  const out = [];
15266
- for (const line of readFileSync7(file, "utf8").split("\n")) {
15486
+ for (const line of readFileSync8(file, "utf8").split("\n")) {
15267
15487
  if (!line.trim()) continue;
15268
15488
  try {
15269
15489
  out.push(clampCaptureEvent(JSON.parse(line)));
@@ -15275,7 +15495,7 @@ function readSpool(file) {
15275
15495
  function pruneSpool(dir, keep, maxAgeMs = 7 * 864e5) {
15276
15496
  try {
15277
15497
  for (const f of readdirSync(dir)) {
15278
- const file = path12.join(dir, f);
15498
+ const file = path13.join(dir, f);
15279
15499
  if (file === keep) continue;
15280
15500
  if (Date.now() - statSync2(file).mtimeMs > maxAgeMs)
15281
15501
  rmSync(file, { force: true });
@@ -15284,7 +15504,9 @@ function pruneSpool(dir, keep, maxAgeMs = 7 * 864e5) {
15284
15504
  }
15285
15505
  }
15286
15506
  async function hookMain(argv) {
15287
- const tool = argv.find((a) => !a.startsWith("--"));
15507
+ const eventFlag = argv.indexOf("--event");
15508
+ const event = eventFlag >= 0 ? argv[eventFlag + 1] : void 0;
15509
+ const tool = argv.find((a, i) => !a.startsWith("--") && i !== eventFlag + 1);
15288
15510
  const adapter = tool ? ADAPTERS[tool] : void 0;
15289
15511
  try {
15290
15512
  if (!adapter) {
@@ -15295,16 +15517,16 @@ async function hookMain(argv) {
15295
15517
  }
15296
15518
  let payload = null;
15297
15519
  try {
15298
- payload = JSON.parse(readFileSync7(0, "utf8"));
15520
+ payload = JSON.parse(readFileSync8(0, "utf8"));
15299
15521
  } catch {
15300
15522
  return;
15301
15523
  }
15302
- const out = adapter.map(payload, (/* @__PURE__ */ new Date()).toISOString());
15524
+ const out = adapter.map(payload, (/* @__PURE__ */ new Date()).toISOString(), { event });
15303
15525
  if (!out) return;
15304
15526
  if (out.reply) process.stdout.write(out.reply);
15305
15527
  const file = spoolPath(adapter.id, out.sessionId);
15306
15528
  if (out.events.length) {
15307
- mkdirSync3(path12.dirname(file), { recursive: true });
15529
+ mkdirSync3(path13.dirname(file), { recursive: true });
15308
15530
  appendFileSync(
15309
15531
  file,
15310
15532
  out.events.map((e) => JSON.stringify(clampCaptureEvent(e))).join("\n") + "\n"
@@ -15312,7 +15534,7 @@ async function hookMain(argv) {
15312
15534
  }
15313
15535
  if (!out.flush) return;
15314
15536
  const root = await repoRoot(out.cwd ?? process.cwd());
15315
- const events = existsSync7(file) ? readSpool(file) : [];
15537
+ const events = existsSync8(file) ? readSpool(file) : [];
15316
15538
  if (events.length) {
15317
15539
  const r = await captureApi("/capture/events", {
15318
15540
  method: "POST",
@@ -15333,7 +15555,7 @@ async function hookMain(argv) {
15333
15555
  }
15334
15556
  }
15335
15557
  if (out.end) rmSync(file, { force: true });
15336
- pruneSpool(path12.dirname(file), file);
15558
+ pruneSpool(path13.dirname(file), file);
15337
15559
  await syncRepo(root);
15338
15560
  } catch (e) {
15339
15561
  console.error("brainjar hook:", e instanceof Error ? e.message : e);
@@ -15456,7 +15678,7 @@ __export(init_exports, {
15456
15678
  init: () => init,
15457
15679
  initMain: () => initMain
15458
15680
  });
15459
- import { readFile as readFile7 } from "node:fs/promises";
15681
+ import { readFile as readFile8 } from "node:fs/promises";
15460
15682
  async function init(argv) {
15461
15683
  const skipTranscripts = argv.includes("--no-transcripts");
15462
15684
  const flag = (f) => argv.includes(f) ? argv[argv.indexOf(f) + 1] ?? null : null;
@@ -15541,7 +15763,7 @@ async function init(argv) {
15541
15763
  let skipped = 0;
15542
15764
  for (const f of files) {
15543
15765
  try {
15544
- const { text, truncated } = capTranscript(await readFile7(f, "utf8"));
15766
+ const { text, truncated } = capTranscript(await readFile8(f, "utf8"));
15545
15767
  if (truncated) truncatedCount++;
15546
15768
  const r = await captureApi("/capture/transcript", {
15547
15769
  method: "POST",
@@ -15564,7 +15786,7 @@ async function init(argv) {
15564
15786
  const tools = detectedTools(root);
15565
15787
  if (tools.length === 0) {
15566
15788
  ok(
15567
- "no coding agent detected (looked for Claude Code, Cursor, Codex); rerun init after installing one"
15789
+ "no coding agent detected (looked for Claude Code, Cursor, Codex, GitHub Copilot); rerun init after installing one"
15568
15790
  );
15569
15791
  }
15570
15792
  for (const tool of tools) {
@@ -15631,7 +15853,7 @@ __export(constraints_exports, {
15631
15853
  constraintsMain: () => constraintsMain,
15632
15854
  sessionStartEnvelope: () => sessionStartEnvelope
15633
15855
  });
15634
- import { readFileSync as readFileSync8 } from "node:fs";
15856
+ import { readFileSync as readFileSync9 } from "node:fs";
15635
15857
  function sessionStartEnvelope(text) {
15636
15858
  return JSON.stringify({
15637
15859
  hookSpecificOutput: {
@@ -15649,7 +15871,7 @@ async function constraintsMain(argv) {
15649
15871
  if (hook) {
15650
15872
  let cwd;
15651
15873
  try {
15652
- const payload = JSON.parse(readFileSync8(0, "utf8"));
15874
+ const payload = JSON.parse(readFileSync9(0, "utf8"));
15653
15875
  cwd = payload.cwd;
15654
15876
  } catch {
15655
15877
  }
@@ -15671,7 +15893,8 @@ async function constraintsMain(argv) {
15671
15893
  }
15672
15894
  return;
15673
15895
  }
15674
- if (r.text) console.log(hook ? sessionStartEnvelope(r.text) : r.text);
15896
+ const envelope = argv.includes("--copilot") ? copilotContextEnvelope : sessionStartEnvelope;
15897
+ if (r.text) console.log(hook ? envelope(r.text) : r.text);
15675
15898
  else if (!hook)
15676
15899
  console.log("No recorded failed approaches or rules for this Brain.");
15677
15900
  if (hook) {
@@ -15691,6 +15914,7 @@ var init_constraints = __esm({
15691
15914
  "use strict";
15692
15915
  init_src3();
15693
15916
  init_api_client();
15917
+ init_copilot();
15694
15918
  }
15695
15919
  });
15696
15920
 
@@ -15842,8 +16066,8 @@ __export(deepscan_exports, {
15842
16066
  deepscanMain: () => deepscanMain,
15843
16067
  selectDeepScanFiles: () => selectDeepScanFiles
15844
16068
  });
15845
- import { readdir as readdir3, readFile as readFile8, stat as stat2 } from "node:fs/promises";
15846
- import path13 from "node:path";
16069
+ import { readdir as readdir3, readFile as readFile9, stat as stat2 } from "node:fs/promises";
16070
+ import path14 from "node:path";
15847
16071
  function selectDeepScanFiles(candidates, graph, maxFiles = MAX_FILES) {
15848
16072
  const picked = [];
15849
16073
  const has = /* @__PURE__ */ new Set();
@@ -15880,7 +16104,7 @@ async function walkCandidates(root) {
15880
16104
  for (const e of entries) {
15881
16105
  if (!e.isFile()) continue;
15882
16106
  const parent = e.parentPath ?? e.path ?? root;
15883
- const rel = path13.relative(root, path13.join(parent, e.name)).split(path13.sep).join("/");
16107
+ const rel = path14.relative(root, path14.join(parent, e.name)).split(path14.sep).join("/");
15884
16108
  if (rel.split("/").some((seg) => SKIP_DIRS.has(seg))) continue;
15885
16109
  out.push(rel);
15886
16110
  }
@@ -15905,10 +16129,10 @@ async function deepscanMain() {
15905
16129
  const artifacts = [];
15906
16130
  for (const rel of files) {
15907
16131
  try {
15908
- const abs = path13.join(root, rel);
16132
+ const abs = path14.join(root, rel);
15909
16133
  const info = await stat2(abs);
15910
16134
  if (info.size > RAW_SKIP_BYTES) continue;
15911
- const content = await readFile8(abs, "utf8");
16135
+ const content = await readFile9(abs, "utf8");
15912
16136
  artifacts.push({
15913
16137
  source: "deepscan",
15914
16138
  kind: "source_file",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainjar",
3
- "version": "0.3.679",
3
+ "version": "0.3.684",
4
4
  "description": "BrainJar CLI: turn a repository into a live, self-documenting Brain.",
5
5
  "type": "module",
6
6
  "bin": {