agentsbestfriend 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2983,7 +2983,7 @@ var require_compile = __commonJS({
2983
2983
  const schOrFunc = root.refs[ref];
2984
2984
  if (schOrFunc)
2985
2985
  return schOrFunc;
2986
- let _sch = resolve6.call(this, root, ref);
2986
+ let _sch = resolve7.call(this, root, ref);
2987
2987
  if (_sch === void 0) {
2988
2988
  const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
2989
2989
  const { schemaId } = this.opts;
@@ -3010,7 +3010,7 @@ var require_compile = __commonJS({
3010
3010
  function sameSchemaEnv(s1, s2) {
3011
3011
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3012
3012
  }
3013
- function resolve6(root, ref) {
3013
+ function resolve7(root, ref) {
3014
3014
  let sch;
3015
3015
  while (typeof (sch = this.refs[ref]) == "string")
3016
3016
  ref = sch;
@@ -3585,55 +3585,55 @@ var require_fast_uri = __commonJS({
3585
3585
  }
3586
3586
  return uri;
3587
3587
  }
3588
- function resolve6(baseURI, relativeURI, options) {
3588
+ function resolve7(baseURI, relativeURI, options) {
3589
3589
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3590
3590
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3591
3591
  schemelessOptions.skipEscape = true;
3592
3592
  return serialize(resolved, schemelessOptions);
3593
3593
  }
3594
- function resolveComponent(base, relative5, options, skipNormalization) {
3594
+ function resolveComponent(base, relative6, options, skipNormalization) {
3595
3595
  const target = {};
3596
3596
  if (!skipNormalization) {
3597
3597
  base = parse3(serialize(base, options), options);
3598
- relative5 = parse3(serialize(relative5, options), options);
3598
+ relative6 = parse3(serialize(relative6, options), options);
3599
3599
  }
3600
3600
  options = options || {};
3601
- if (!options.tolerant && relative5.scheme) {
3602
- target.scheme = relative5.scheme;
3603
- target.userinfo = relative5.userinfo;
3604
- target.host = relative5.host;
3605
- target.port = relative5.port;
3606
- target.path = removeDotSegments(relative5.path || "");
3607
- target.query = relative5.query;
3601
+ if (!options.tolerant && relative6.scheme) {
3602
+ target.scheme = relative6.scheme;
3603
+ target.userinfo = relative6.userinfo;
3604
+ target.host = relative6.host;
3605
+ target.port = relative6.port;
3606
+ target.path = removeDotSegments(relative6.path || "");
3607
+ target.query = relative6.query;
3608
3608
  } else {
3609
- if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
3610
- target.userinfo = relative5.userinfo;
3611
- target.host = relative5.host;
3612
- target.port = relative5.port;
3613
- target.path = removeDotSegments(relative5.path || "");
3614
- target.query = relative5.query;
3609
+ if (relative6.userinfo !== void 0 || relative6.host !== void 0 || relative6.port !== void 0) {
3610
+ target.userinfo = relative6.userinfo;
3611
+ target.host = relative6.host;
3612
+ target.port = relative6.port;
3613
+ target.path = removeDotSegments(relative6.path || "");
3614
+ target.query = relative6.query;
3615
3615
  } else {
3616
- if (!relative5.path) {
3616
+ if (!relative6.path) {
3617
3617
  target.path = base.path;
3618
- if (relative5.query !== void 0) {
3619
- target.query = relative5.query;
3618
+ if (relative6.query !== void 0) {
3619
+ target.query = relative6.query;
3620
3620
  } else {
3621
3621
  target.query = base.query;
3622
3622
  }
3623
3623
  } else {
3624
- if (relative5.path[0] === "/") {
3625
- target.path = removeDotSegments(relative5.path);
3624
+ if (relative6.path[0] === "/") {
3625
+ target.path = removeDotSegments(relative6.path);
3626
3626
  } else {
3627
3627
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3628
- target.path = "/" + relative5.path;
3628
+ target.path = "/" + relative6.path;
3629
3629
  } else if (!base.path) {
3630
- target.path = relative5.path;
3630
+ target.path = relative6.path;
3631
3631
  } else {
3632
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
3632
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative6.path;
3633
3633
  }
3634
3634
  target.path = removeDotSegments(target.path);
3635
3635
  }
3636
- target.query = relative5.query;
3636
+ target.query = relative6.query;
3637
3637
  }
3638
3638
  target.userinfo = base.userinfo;
3639
3639
  target.host = base.host;
@@ -3641,7 +3641,7 @@ var require_fast_uri = __commonJS({
3641
3641
  }
3642
3642
  target.scheme = base.scheme;
3643
3643
  }
3644
- target.fragment = relative5.fragment;
3644
+ target.fragment = relative6.fragment;
3645
3645
  return target;
3646
3646
  }
3647
3647
  function equal(uriA, uriB, options) {
@@ -3812,7 +3812,7 @@ var require_fast_uri = __commonJS({
3812
3812
  var fastUri = {
3813
3813
  SCHEMES,
3814
3814
  normalize,
3815
- resolve: resolve6,
3815
+ resolve: resolve7,
3816
3816
  resolveComponent,
3817
3817
  equal,
3818
3818
  serialize,
@@ -12405,7 +12405,7 @@ var init_sql = __esm({
12405
12405
  return new SQL([new StringChunk(str)]);
12406
12406
  }
12407
12407
  sql2.raw = raw;
12408
- function join12(chunks, separator) {
12408
+ function join13(chunks, separator) {
12409
12409
  const result = [];
12410
12410
  for (const [i, chunk] of chunks.entries()) {
12411
12411
  if (i > 0 && separator !== void 0) {
@@ -12415,7 +12415,7 @@ var init_sql = __esm({
12415
12415
  }
12416
12416
  return new SQL(result);
12417
12417
  }
12418
- sql2.join = join12;
12418
+ sql2.join = join13;
12419
12419
  function identifier(value) {
12420
12420
  return new Name(value);
12421
12421
  }
@@ -15400,7 +15400,7 @@ var init_select2 = __esm({
15400
15400
  const baseTableName = this.tableName;
15401
15401
  const tableName = getTableLikeName(table);
15402
15402
  for (const item of extractUsedTable(table)) this.usedTables.add(item);
15403
- if (typeof tableName === "string" && this.config.joins?.some((join12) => join12.alias === tableName)) {
15403
+ if (typeof tableName === "string" && this.config.joins?.some((join13) => join13.alias === tableName)) {
15404
15404
  throw new Error(`Alias "${tableName}" is already used in this query`);
15405
15405
  }
15406
15406
  if (!this.isPartialSelect) {
@@ -16290,7 +16290,7 @@ var init_update = __esm({
16290
16290
  createJoin(joinType) {
16291
16291
  return (table, on) => {
16292
16292
  const tableName = getTableLikeName(table);
16293
- if (typeof tableName === "string" && this.config.joins.some((join12) => join12.alias === tableName)) {
16293
+ if (typeof tableName === "string" && this.config.joins.some((join13) => join13.alias === tableName)) {
16294
16294
  throw new Error(`Alias "${tableName}" is already used in this query`);
16295
16295
  }
16296
16296
  if (typeof on === "function") {
@@ -18342,10 +18342,10 @@ async function getIndexStatus(projectRoot) {
18342
18342
  }
18343
18343
  const lastUpdated = allFiles.length > 0 ? new Date(Math.max(...allFiles.map((f) => f.lastIndexedAt.getTime()))) : null;
18344
18344
  const { statSync: statSync3 } = await import("fs");
18345
- const { join: join12 } = await import("path");
18345
+ const { join: join13 } = await import("path");
18346
18346
  let indexSizeBytes = 0;
18347
18347
  try {
18348
- const dbPath = join12(projectRoot, ".abf", "index.db");
18348
+ const dbPath = join13(projectRoot, ".abf", "index.db");
18349
18349
  indexSizeBytes = statSync3(dbPath).size;
18350
18350
  } catch {
18351
18351
  }
@@ -19265,28 +19265,28 @@ __export(init_exports, {
19265
19265
  initCommand: () => initCommand
19266
19266
  });
19267
19267
  import * as clack from "@clack/prompts";
19268
- import { resolve as resolve2 } from "path";
19269
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync9, appendFileSync } from "fs";
19268
+ import { resolve as resolve3 } from "path";
19269
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync10, appendFileSync } from "fs";
19270
19270
  import { execFile as execFile5 } from "child_process";
19271
19271
  import { promisify as promisify5 } from "util";
19272
19272
  async function initCommand(projectPath) {
19273
- const root = resolve2(projectPath);
19273
+ const root = resolve3(projectPath);
19274
19274
  clack.intro("ABF Init");
19275
- if (!existsSync5(root)) {
19275
+ if (!existsSync6(root)) {
19276
19276
  clack.log.error(`Path does not exist: ${root}`);
19277
19277
  process.exit(1);
19278
19278
  }
19279
- const abfDir = resolve2(root, ".abf");
19280
- const isNew = !existsSync5(abfDir);
19279
+ const abfDir = resolve3(root, ".abf");
19280
+ const isNew = !existsSync6(abfDir);
19281
19281
  if (isNew) {
19282
19282
  mkdirSync3(abfDir, { recursive: true });
19283
19283
  clack.log.info(`Created ${abfDir}`);
19284
19284
  } else {
19285
19285
  clack.log.info(`ABF directory already exists at ${abfDir}`);
19286
19286
  }
19287
- const gitignorePath = resolve2(root, ".gitignore");
19288
- const gitignoreExists = existsSync5(gitignorePath);
19289
- const alreadyIgnored = gitignoreExists && readFileSync9(gitignorePath, "utf-8").split("\n").some((line) => line.trim() === ".abf" || line.trim() === ".abf/");
19287
+ const gitignorePath = resolve3(root, ".gitignore");
19288
+ const gitignoreExists = existsSync6(gitignorePath);
19289
+ const alreadyIgnored = gitignoreExists && readFileSync10(gitignorePath, "utf-8").split("\n").some((line) => line.trim() === ".abf" || line.trim() === ".abf/");
19290
19290
  if (!alreadyIgnored) {
19291
19291
  const addToGitignore = await clack.confirm({
19292
19292
  message: "Add .abf/ to .gitignore?",
@@ -19396,7 +19396,27 @@ async function initCommand(projectPath) {
19396
19396
  clack.outro(isNew ? "Project initialized!" : "Index rebuilt!");
19397
19397
  return;
19398
19398
  }
19399
- const addMcpArgs = ["add-mcp", "abf start", "--name", "abf", "-y"];
19399
+ const mcpSource = await clack.select({
19400
+ message: "How should agents run ABF?",
19401
+ options: [
19402
+ {
19403
+ value: "npx",
19404
+ label: "npx (recommended)",
19405
+ hint: "Always uses the latest version via npx agentsbestfriend start"
19406
+ },
19407
+ {
19408
+ value: "local",
19409
+ label: "Local install",
19410
+ hint: "Uses your locally installed abf binary"
19411
+ }
19412
+ ]
19413
+ });
19414
+ if (clack.isCancel(mcpSource)) {
19415
+ clack.outro(isNew ? "Project initialized!" : "Index rebuilt!");
19416
+ return;
19417
+ }
19418
+ const mcpCommand = mcpSource === "npx" ? "npx agentsbestfriend start" : "abf start";
19419
+ const addMcpArgs = ["add-mcp", mcpCommand, "--name", "abf", "-y"];
19400
19420
  for (const agent of selectedAgents) {
19401
19421
  addMcpArgs.push("-a", agent);
19402
19422
  }
@@ -19413,16 +19433,22 @@ async function initCommand(projectPath) {
19413
19433
  timeout: 6e4
19414
19434
  });
19415
19435
  mcpSpinner.stop("MCP server installed successfully");
19416
- clack.log.info(
19417
- `Agents can now use ABF via the "abf start" command.
19436
+ if (mcpSource === "npx") {
19437
+ clack.log.info(
19438
+ `Agents will use ABF via "npx agentsbestfriend start" (always latest version).`
19439
+ );
19440
+ } else {
19441
+ clack.log.info(
19442
+ `Agents will use ABF via "abf start".
19418
19443
  Make sure abf is installed globally: npm install -g agentsbestfriend`
19419
- );
19444
+ );
19445
+ }
19420
19446
  } catch (err) {
19421
19447
  mcpSpinner.stop("MCP installation failed");
19422
19448
  const msg = err instanceof Error ? err.message : String(err);
19423
19449
  clack.log.warn(
19424
19450
  `Could not install MCP server: ${msg}
19425
- You can install manually: npx add-mcp "abf start" --name abf -y`
19451
+ You can install manually: npx add-mcp "${mcpCommand}" --name abf -y`
19426
19452
  );
19427
19453
  }
19428
19454
  clack.outro(isNew ? "Project initialized!" : "Index rebuilt!");
@@ -19455,9 +19481,9 @@ __export(index_cmd_exports, {
19455
19481
  indexCommand: () => indexCommand
19456
19482
  });
19457
19483
  import * as clack2 from "@clack/prompts";
19458
- import { resolve as resolve3 } from "path";
19484
+ import { resolve as resolve4 } from "path";
19459
19485
  async function indexCommand(projectPath) {
19460
- const root = resolve3(projectPath);
19486
+ const root = resolve4(projectPath);
19461
19487
  clack2.intro("ABF Index");
19462
19488
  const s = clack2.spinner();
19463
19489
  s.start("Indexing project...");
@@ -19489,7 +19515,7 @@ __export(portal_exports, {
19489
19515
  portalCommand: () => portalCommand
19490
19516
  });
19491
19517
  import * as clack3 from "@clack/prompts";
19492
- import { resolve as resolve4 } from "path";
19518
+ import { resolve as resolve5 } from "path";
19493
19519
  import { execFile as execFile6 } from "child_process";
19494
19520
  import { promisify as promisify6 } from "util";
19495
19521
  async function portalCommand() {
@@ -19595,7 +19621,7 @@ async function showStatus() {
19595
19621
  initialValue: process.cwd()
19596
19622
  });
19597
19623
  if (clack3.isCancel(projectPath)) return;
19598
- const root = resolve4(projectPath);
19624
+ const root = resolve5(projectPath);
19599
19625
  const s = clack3.spinner();
19600
19626
  s.start("Loading index status...");
19601
19627
  try {
@@ -19623,7 +19649,7 @@ async function reindex() {
19623
19649
  initialValue: process.cwd()
19624
19650
  });
19625
19651
  if (clack3.isCancel(projectPath)) return;
19626
- const root = resolve4(projectPath);
19652
+ const root = resolve5(projectPath);
19627
19653
  const s = clack3.spinner();
19628
19654
  s.start("Indexing...");
19629
19655
  try {
@@ -19957,9 +19983,9 @@ __export(status_exports, {
19957
19983
  statusCommand: () => statusCommand
19958
19984
  });
19959
19985
  import * as clack5 from "@clack/prompts";
19960
- import { resolve as resolve5 } from "path";
19986
+ import { resolve as resolve6 } from "path";
19961
19987
  async function statusCommand(projectPath) {
19962
- const root = resolve5(projectPath);
19988
+ const root = resolve6(projectPath);
19963
19989
  const config2 = loadConfig();
19964
19990
  clack5.intro("ABF Status");
19965
19991
  try {
@@ -41312,7 +41338,7 @@ var Protocol = class {
41312
41338
  return;
41313
41339
  }
41314
41340
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
41315
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
41341
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
41316
41342
  options?.signal?.throwIfAborted();
41317
41343
  }
41318
41344
  } catch (error48) {
@@ -41329,7 +41355,7 @@ var Protocol = class {
41329
41355
  */
41330
41356
  request(request, resultSchema, options) {
41331
41357
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
41332
- return new Promise((resolve6, reject) => {
41358
+ return new Promise((resolve7, reject) => {
41333
41359
  const earlyReject = (error48) => {
41334
41360
  reject(error48);
41335
41361
  };
@@ -41407,7 +41433,7 @@ var Protocol = class {
41407
41433
  if (!parseResult.success) {
41408
41434
  reject(parseResult.error);
41409
41435
  } else {
41410
- resolve6(parseResult.data);
41436
+ resolve7(parseResult.data);
41411
41437
  }
41412
41438
  } catch (error48) {
41413
41439
  reject(error48);
@@ -41668,12 +41694,12 @@ var Protocol = class {
41668
41694
  }
41669
41695
  } catch {
41670
41696
  }
41671
- return new Promise((resolve6, reject) => {
41697
+ return new Promise((resolve7, reject) => {
41672
41698
  if (signal.aborted) {
41673
41699
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
41674
41700
  return;
41675
41701
  }
41676
- const timeoutId = setTimeout(resolve6, interval);
41702
+ const timeoutId = setTimeout(resolve7, interval);
41677
41703
  signal.addEventListener("abort", () => {
41678
41704
  clearTimeout(timeoutId);
41679
41705
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -42773,7 +42799,7 @@ var McpServer = class {
42773
42799
  let task = createTaskResult.task;
42774
42800
  const pollInterval = task.pollInterval ?? 5e3;
42775
42801
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
42776
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
42802
+ await new Promise((resolve7) => setTimeout(resolve7, pollInterval));
42777
42803
  const updatedTask = await extra.taskStore.getTask(taskId);
42778
42804
  if (!updatedTask) {
42779
42805
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -43422,12 +43448,12 @@ var StdioServerTransport = class {
43422
43448
  this.onclose?.();
43423
43449
  }
43424
43450
  send(message) {
43425
- return new Promise((resolve6) => {
43451
+ return new Promise((resolve7) => {
43426
43452
  const json2 = serializeMessage(message);
43427
43453
  if (this._stdout.write(json2)) {
43428
- resolve6();
43454
+ resolve7();
43429
43455
  } else {
43430
- this._stdout.once("drain", resolve6);
43456
+ this._stdout.once("drain", resolve7);
43431
43457
  }
43432
43458
  });
43433
43459
  }
@@ -43445,7 +43471,7 @@ function registerPingTool(server) {
43445
43471
  const status = {
43446
43472
  status: "ok",
43447
43473
  server: "agents-best-friend",
43448
- version: "0.8.0",
43474
+ version: "0.10.0",
43449
43475
  projectRoot,
43450
43476
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
43451
43477
  };
@@ -43477,9 +43503,9 @@ function registerSearchTool(server) {
43477
43503
  - "keyword": Exploratory search \u2014 splits the query into keywords, scores every file by keyword density, returns the top matching files ranked by relevance. Best when you're not sure of exact names.
43478
43504
  - "semantic": (Requires Ollama + embeddings index) Embedding-based similarity search. Falls back to keyword mode if unavailable.
43479
43505
 
43480
- Use "exact" when you know what to search for. Use "keyword" when exploring or looking for files related to a concept.`, {
43506
+ Use "exact" when you EXACTLY know what to search for. Use "keyword" when exploring or looking for files related to a concept.`, {
43481
43507
  query: external_exports3.string().describe("Search query \u2014 exact text, regex, or space-separated keywords depending on mode"),
43482
- mode: external_exports3.enum(["exact", "keyword", "semantic"]).default("exact").describe('Search mode: "exact" (ripgrep), "keyword" (file scoring), or "semantic" (embedding similarity)'),
43508
+ mode: external_exports3.enum(["exact", "keyword", "semantic"]).default("keyword").describe('Search mode: "exact" (ripgrep), "keyword" (file scoring), or "semantic" (embedding similarity)'),
43483
43509
  path_filter: external_exports3.string().optional().describe('Glob pattern to filter files, e.g. "src/**/*.ts" or "*.py"'),
43484
43510
  max_results: external_exports3.number().int().positive().optional().default(20).describe("Maximum number of results to return (default: 20)"),
43485
43511
  case_sensitive: external_exports3.boolean().optional().default(false).describe("Case-sensitive search (exact mode only)"),
@@ -44610,6 +44636,366 @@ function parseBiomePresence(_content) {
44610
44636
  ];
44611
44637
  }
44612
44638
 
44639
+ // ../core/dist/analysis/context-bundle.js
44640
+ init_parse();
44641
+ import { readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
44642
+ import { join as join9, dirname, resolve, relative as relative5 } from "path";
44643
+ import { execFileSync } from "child_process";
44644
+ function buildContextBundle(opts) {
44645
+ const { projectRoot, depth = 1, include = "smart", focusSymbol, reverse = false } = opts;
44646
+ const entryPath = opts.entry.startsWith("/") ? opts.entry : join9(projectRoot, opts.entry);
44647
+ const entryRel = relative5(projectRoot, entryPath);
44648
+ const visited = /* @__PURE__ */ new Set();
44649
+ const bundledFiles = [];
44650
+ traverseImports(entryPath, projectRoot, 0, depth, include, focusSymbol, visited, bundledFiles);
44651
+ if (reverse) {
44652
+ const reverseFiles = findReverseDeps(projectRoot, entryRel);
44653
+ for (const revFile of reverseFiles) {
44654
+ const absPath = join9(projectRoot, revFile.file);
44655
+ if (visited.has(absPath))
44656
+ continue;
44657
+ visited.add(absPath);
44658
+ try {
44659
+ const content = readFileSync6(absPath, "utf-8");
44660
+ const parsed = parseFile(absPath, content);
44661
+ bundledFiles.push({
44662
+ path: revFile.file,
44663
+ depth: -1,
44664
+ // reverse
44665
+ symbols: parsed.symbols,
44666
+ imports: parsed.imports,
44667
+ isReverse: true
44668
+ });
44669
+ } catch {
44670
+ }
44671
+ }
44672
+ }
44673
+ let totalSymbols = 0;
44674
+ for (const f of bundledFiles) {
44675
+ totalSymbols += countSymbols(f.symbols);
44676
+ }
44677
+ return {
44678
+ entry: entryRel,
44679
+ focusSymbol,
44680
+ depth,
44681
+ include,
44682
+ files: bundledFiles,
44683
+ totalSymbols
44684
+ };
44685
+ }
44686
+ function traverseImports(filePath, projectRoot, currentDepth, maxDepth, include, focusSymbol, visited, out) {
44687
+ if (visited.has(filePath))
44688
+ return;
44689
+ visited.add(filePath);
44690
+ let content;
44691
+ try {
44692
+ content = readFileSync6(filePath, "utf-8");
44693
+ } catch {
44694
+ return;
44695
+ }
44696
+ const parsed = parseFile(filePath, content);
44697
+ const relPath = relative5(projectRoot, filePath);
44698
+ const isEntry = currentDepth === 0;
44699
+ const includeContent = include === "full" || include === "smart" && isEntry;
44700
+ const bundled = {
44701
+ path: relPath,
44702
+ depth: currentDepth,
44703
+ symbols: parsed.symbols,
44704
+ imports: parsed.imports,
44705
+ content: includeContent ? content : void 0
44706
+ };
44707
+ out.push(bundled);
44708
+ if (currentDepth >= maxDepth)
44709
+ return;
44710
+ for (const imp of parsed.imports) {
44711
+ const resolved = resolveImportPath(imp.targetPath, filePath, projectRoot);
44712
+ if (!resolved)
44713
+ continue;
44714
+ if (focusSymbol && isEntry) {
44715
+ if (!isImportRelevantToSymbol(content, focusSymbol, imp))
44716
+ continue;
44717
+ }
44718
+ traverseImports(
44719
+ resolved,
44720
+ projectRoot,
44721
+ currentDepth + 1,
44722
+ maxDepth,
44723
+ include,
44724
+ void 0,
44725
+ // no focus filtering beyond entry
44726
+ visited,
44727
+ out
44728
+ );
44729
+ }
44730
+ }
44731
+ function resolveImportPath(importPath, fromFile, projectRoot) {
44732
+ if (importPath.startsWith(".")) {
44733
+ const fromDir = dirname(fromFile);
44734
+ return tryResolveFile(resolve(fromDir, importPath));
44735
+ }
44736
+ if (importPath.startsWith("@")) {
44737
+ return resolveMonorepoImport(importPath, projectRoot);
44738
+ }
44739
+ return null;
44740
+ }
44741
+ function tryResolveFile(base) {
44742
+ const extensions = [
44743
+ "",
44744
+ ".ts",
44745
+ ".tsx",
44746
+ ".js",
44747
+ ".jsx",
44748
+ ".mjs",
44749
+ "/index.ts",
44750
+ "/index.tsx",
44751
+ "/index.js"
44752
+ ];
44753
+ const stripped = base.replace(/\.js$/, "");
44754
+ for (const ext of extensions) {
44755
+ const candidate = stripped + ext;
44756
+ if (existsSync4(candidate))
44757
+ return candidate;
44758
+ }
44759
+ for (const ext of extensions) {
44760
+ const candidate = base + ext;
44761
+ if (existsSync4(candidate))
44762
+ return candidate;
44763
+ }
44764
+ return null;
44765
+ }
44766
+ function resolveMonorepoImport(importPath, projectRoot) {
44767
+ const parts = importPath.split("/");
44768
+ if (parts.length < 2)
44769
+ return null;
44770
+ const scope = parts[0];
44771
+ const pkgName = parts[1];
44772
+ const subpath = parts.slice(2).join("/");
44773
+ const pkgDir = join9(projectRoot, "packages", pkgName);
44774
+ if (!existsSync4(pkgDir))
44775
+ return null;
44776
+ const pkgJsonPath = join9(pkgDir, "package.json");
44777
+ if (!existsSync4(pkgJsonPath))
44778
+ return null;
44779
+ try {
44780
+ const pkgJson = JSON.parse(readFileSync6(pkgJsonPath, "utf-8"));
44781
+ const exportKey = subpath ? `./${subpath}` : ".";
44782
+ const exportEntry = pkgJson.exports?.[exportKey];
44783
+ if (exportEntry) {
44784
+ const distPath = typeof exportEntry === "string" ? exportEntry : exportEntry.import ?? exportEntry.types;
44785
+ if (distPath) {
44786
+ const srcPath = distPath.replace(/^\.\/dist\//, "./src/").replace(/\.js$/, ".ts").replace(/\.d\.ts$/, ".ts");
44787
+ const resolved = join9(pkgDir, srcPath);
44788
+ if (existsSync4(resolved))
44789
+ return resolved;
44790
+ const distResolved = join9(pkgDir, distPath);
44791
+ if (existsSync4(distResolved))
44792
+ return distResolved;
44793
+ }
44794
+ }
44795
+ } catch {
44796
+ }
44797
+ const fallbacks = [
44798
+ join9(pkgDir, "src", subpath || "index", "index.ts"),
44799
+ join9(pkgDir, "src", (subpath || "index") + ".ts")
44800
+ ];
44801
+ for (const fb of fallbacks) {
44802
+ if (existsSync4(fb))
44803
+ return fb;
44804
+ }
44805
+ return null;
44806
+ }
44807
+ function isImportRelevantToSymbol(fileContent, focusSymbol, imp) {
44808
+ const lines = fileContent.split("\n");
44809
+ const symbolRegex = new RegExp(`(?:function|const|class|interface|type)\\s+${escapeRegex2(focusSymbol)}`);
44810
+ let inSymbol = false;
44811
+ let braceDepth = 0;
44812
+ let symbolFound = false;
44813
+ for (const line of lines) {
44814
+ if (!inSymbol && symbolRegex.test(line)) {
44815
+ inSymbol = true;
44816
+ symbolFound = true;
44817
+ }
44818
+ if (inSymbol) {
44819
+ for (const ch of line) {
44820
+ if (ch === "{")
44821
+ braceDepth++;
44822
+ if (ch === "}")
44823
+ braceDepth--;
44824
+ }
44825
+ for (const sym of imp.importedSymbols) {
44826
+ if (sym === "*")
44827
+ return true;
44828
+ if (line.includes(sym))
44829
+ return true;
44830
+ }
44831
+ if (braceDepth <= 0 && symbolFound) {
44832
+ return false;
44833
+ }
44834
+ }
44835
+ }
44836
+ return !symbolFound;
44837
+ }
44838
+ function findReverseDeps(projectRoot, targetRelPath) {
44839
+ let filePaths;
44840
+ try {
44841
+ const stdout = execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd: projectRoot, maxBuffer: 10 * 1024 * 1024 }).toString();
44842
+ filePaths = stdout.trim().split("\n").filter(Boolean);
44843
+ } catch {
44844
+ return [];
44845
+ }
44846
+ const codeExts = /* @__PURE__ */ new Set([
44847
+ ".ts",
44848
+ ".tsx",
44849
+ ".js",
44850
+ ".jsx",
44851
+ ".mjs",
44852
+ ".cjs",
44853
+ ".py"
44854
+ ]);
44855
+ const results = [];
44856
+ const targetName = targetRelPath.replace(/\.[^.]+$/, "").replace(/\/index$/, "");
44857
+ for (const fp of filePaths) {
44858
+ if (fp === targetRelPath)
44859
+ continue;
44860
+ const ext = fp.slice(fp.lastIndexOf("."));
44861
+ if (!codeExts.has(ext))
44862
+ continue;
44863
+ try {
44864
+ const content = readFileSync6(join9(projectRoot, fp), "utf-8");
44865
+ const parsed = parseFile(join9(projectRoot, fp), content);
44866
+ for (const imp of parsed.imports) {
44867
+ if (importMatchesTarget(imp.targetPath, fp, targetRelPath, targetName)) {
44868
+ results.push({ file: fp, symbols: imp.importedSymbols });
44869
+ break;
44870
+ }
44871
+ }
44872
+ } catch {
44873
+ }
44874
+ }
44875
+ return results;
44876
+ }
44877
+ function importMatchesTarget(importPath, sourceFile, targetRelPath, targetName) {
44878
+ if (!importPath.startsWith("."))
44879
+ return false;
44880
+ const sourceDir = dirname(sourceFile);
44881
+ const resolved = join9(sourceDir, importPath).replace(/\.[^.]+$/, "").replace(/\/index$/, "");
44882
+ return resolved === targetName || resolved === targetRelPath.replace(/\.[^.]+$/, "");
44883
+ }
44884
+ function formatContextBundle(result) {
44885
+ const lines = [];
44886
+ lines.push(`Bundle for ${result.entry}${result.focusSymbol ? ` \u2192 ${result.focusSymbol}` : ""}`);
44887
+ lines.push(` Depth: ${result.depth} | Files: ${result.files.length} | Symbols: ${result.totalSymbols} | Mode: ${result.include}`);
44888
+ lines.push("");
44889
+ const forwardFiles = result.files.filter((f) => !f.isReverse);
44890
+ const reverseFiles = result.files.filter((f) => f.isReverse);
44891
+ for (const file2 of forwardFiles) {
44892
+ const depthLabel = file2.depth === 0 ? "ENTRY" : `DEPTH ${file2.depth}`;
44893
+ lines.push(`\u2550\u2550\u2550 ${depthLabel}: ${file2.path} \u2550\u2550\u2550`);
44894
+ if (file2.content && result.focusSymbol && file2.depth === 0) {
44895
+ const focused = findSymbolInList(file2.symbols, result.focusSymbol);
44896
+ if (focused) {
44897
+ const contentLines = file2.content.split("\n");
44898
+ const code = contentLines.slice(focused.startLine - 1, focused.endLine).join("\n");
44899
+ lines.push(` [${focused.kind} ${focused.name} L${focused.startLine}-${focused.endLine}]`);
44900
+ lines.push(code);
44901
+ lines.push("");
44902
+ const others = file2.symbols.filter((s) => s.name !== result.focusSymbol);
44903
+ if (others.length > 0) {
44904
+ lines.push(" Other symbols:");
44905
+ for (const sym of others) {
44906
+ lines.push(formatSymbolSignature(sym, " "));
44907
+ }
44908
+ lines.push("");
44909
+ }
44910
+ } else {
44911
+ lines.push(file2.content);
44912
+ lines.push("");
44913
+ }
44914
+ } else if (file2.content) {
44915
+ lines.push(file2.content);
44916
+ lines.push("");
44917
+ } else {
44918
+ if (file2.symbols.length > 0) {
44919
+ for (const sym of file2.symbols) {
44920
+ lines.push(formatSymbolSignature(sym, " "));
44921
+ }
44922
+ } else if (file2.imports.length > 0) {
44923
+ lines.push(" Re-exports:");
44924
+ for (const imp of file2.imports) {
44925
+ const syms = imp.importedSymbols.join(", ");
44926
+ lines.push(` ${imp.targetPath} \u2192 {${syms}}`);
44927
+ }
44928
+ } else {
44929
+ lines.push(" (no symbols parsed)");
44930
+ }
44931
+ lines.push("");
44932
+ }
44933
+ if (file2.imports.length > 0) {
44934
+ lines.push(" Imports:");
44935
+ for (const imp of file2.imports) {
44936
+ const syms = imp.importedSymbols.join(", ");
44937
+ lines.push(` ${imp.targetPath} \u2192 {${syms}}`);
44938
+ }
44939
+ lines.push("");
44940
+ }
44941
+ }
44942
+ if (reverseFiles.length > 0) {
44943
+ lines.push(`\u2550\u2550\u2550 REVERSE (imports ${result.entry}) \u2550\u2550\u2550`);
44944
+ const entryBaseName = result.entry.replace(/\.[^.]+$/, "").replace(/\/index$/, "");
44945
+ for (const file2 of reverseFiles) {
44946
+ const relevantImps = file2.imports.filter((imp) => {
44947
+ const resolved = imp.targetPath.replace(/\.[^.]+$/, "").replace(/\/index$/, "");
44948
+ return imp.targetPath.includes(entryBaseName.split("/").pop()) || resolved.endsWith(entryBaseName.split("/").pop());
44949
+ });
44950
+ if (relevantImps.length > 0) {
44951
+ for (const imp of relevantImps) {
44952
+ const syms = imp.importedSymbols.join(", ");
44953
+ lines.push(` ${file2.path} \u2192 {${syms}} from ${imp.targetPath}`);
44954
+ }
44955
+ } else {
44956
+ lines.push(` ${file2.path}`);
44957
+ }
44958
+ }
44959
+ lines.push("");
44960
+ }
44961
+ return lines.join("\n");
44962
+ }
44963
+ function formatSymbolSignature(sym, indent) {
44964
+ const exported = sym.exported ? "\u2605 " : "";
44965
+ const sig = sym.signature ?? sym.name;
44966
+ const range = `L${sym.startLine}-${sym.endLine}`;
44967
+ let line = `${indent}${exported}${sym.kind} ${sig} (${range})`;
44968
+ if (sym.children.length > 0) {
44969
+ for (const child of sym.children) {
44970
+ line += "\n" + formatSymbolSignature(child, indent + " ");
44971
+ }
44972
+ }
44973
+ return line;
44974
+ }
44975
+ function findSymbolInList(symbols2, name) {
44976
+ const lower = name.toLowerCase();
44977
+ for (const sym of symbols2) {
44978
+ if (sym.name.toLowerCase() === lower)
44979
+ return sym;
44980
+ for (const child of sym.children) {
44981
+ if (child.name.toLowerCase() === lower)
44982
+ return child;
44983
+ }
44984
+ }
44985
+ return void 0;
44986
+ }
44987
+ function countSymbols(symbols2) {
44988
+ let count = 0;
44989
+ for (const sym of symbols2) {
44990
+ count++;
44991
+ count += countSymbols(sym.children);
44992
+ }
44993
+ return count;
44994
+ }
44995
+ function escapeRegex2(str) {
44996
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44997
+ }
44998
+
44613
44999
  // ../server/dist/tools/project-overview.js
44614
45000
  function registerProjectOverviewTool(server) {
44615
45001
  server.tool("abf_project_overview", `Get a comprehensive overview of the current project. Returns detected tech stack, frameworks, entry points, directory structure with purposes, language distribution, config files, and architectural patterns. Very token-efficient way to orient yourself in a new codebase. No index required.`, {
@@ -44992,17 +45378,17 @@ function registerIndexTool(server) {
44992
45378
  }
44993
45379
 
44994
45380
  // ../server/dist/tools/symbols.js
44995
- import { readFileSync as readFileSync6 } from "fs";
44996
- import { join as join9 } from "path";
45381
+ import { readFileSync as readFileSync7 } from "fs";
45382
+ import { join as join10 } from "path";
44997
45383
  function registerSymbolsTool(server) {
44998
45384
  server.tool("abf_symbols", "Get the symbol outline (functions, classes, methods, types) of a file.", {
44999
45385
  file_path: external_exports3.string().describe("Path to the file (relative or absolute)"),
45000
45386
  depth: external_exports3.number().int().min(1).max(5).default(2).describe("How deep to show nested symbols")
45001
45387
  }, async ({ file_path, depth }) => {
45002
45388
  const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
45003
- const absPath = file_path.startsWith("/") ? file_path : join9(cwd, file_path);
45389
+ const absPath = file_path.startsWith("/") ? file_path : join10(cwd, file_path);
45004
45390
  try {
45005
- const content = readFileSync6(absPath, "utf-8");
45391
+ const content = readFileSync7(absPath, "utf-8");
45006
45392
  const { symbols: symbols2 } = parseFile(absPath, content);
45007
45393
  if (symbols2.length === 0) {
45008
45394
  return {
@@ -45038,8 +45424,8 @@ function formatSymbolTree(symbols2, maxDepth, currentDepth) {
45038
45424
  }
45039
45425
 
45040
45426
  // ../server/dist/tools/chunk.js
45041
- import { readFileSync as readFileSync7 } from "fs";
45042
- import { join as join10 } from "path";
45427
+ import { readFileSync as readFileSync8 } from "fs";
45428
+ import { join as join11 } from "path";
45043
45429
  function registerChunkTool(server) {
45044
45430
  server.tool("abf_chunk", `Smart file chunking by symbol boundaries. Returns actual source code.
45045
45431
  Use EXACTLY ONE of these modes:
@@ -45051,9 +45437,9 @@ Use EXACTLY ONE of these modes:
45051
45437
  symbol: external_exports3.string().optional().describe("Name of a symbol (function, class, etc.) to retrieve its full source code directly.")
45052
45438
  }, async ({ file_path, chunk_index, symbol: symbol2 }) => {
45053
45439
  const projectRoot = process.env.ABF_PROJECT_ROOT || process.cwd();
45054
- const absPath = file_path.startsWith("/") ? file_path : join10(projectRoot, file_path);
45440
+ const absPath = file_path.startsWith("/") ? file_path : join11(projectRoot, file_path);
45055
45441
  try {
45056
- const content = readFileSync7(absPath, "utf-8");
45442
+ const content = readFileSync8(absPath, "utf-8");
45057
45443
  const lines = content.split("\n");
45058
45444
  const { symbols: parsedSymbols } = parseFile(absPath, content);
45059
45445
  if (symbol2) {
@@ -45194,10 +45580,10 @@ function buildChunks(symbols2, totalLines) {
45194
45580
  }
45195
45581
 
45196
45582
  // ../server/dist/tools/dependencies.js
45197
- import { readFileSync as readFileSync8 } from "fs";
45198
- import { join as join11, dirname, resolve } from "path";
45199
- import { existsSync as existsSync4 } from "fs";
45200
- import { execFileSync } from "child_process";
45583
+ import { readFileSync as readFileSync9 } from "fs";
45584
+ import { join as join12, dirname as dirname2, resolve as resolve2 } from "path";
45585
+ import { existsSync as existsSync5 } from "fs";
45586
+ import { execFileSync as execFileSync2 } from "child_process";
45201
45587
  function registerDependenciesTool(server) {
45202
45588
  server.tool("abf_dependencies", "Show imports and reverse dependencies (imported_by) for a file.", {
45203
45589
  file_path: external_exports3.string().describe("Path to the file"),
@@ -45205,19 +45591,19 @@ function registerDependenciesTool(server) {
45205
45591
  depth: external_exports3.number().int().min(1).max(3).default(1).describe("Depth of transitive dependencies")
45206
45592
  }, async ({ file_path, direction, depth }) => {
45207
45593
  const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
45208
- const absPath = file_path.startsWith("/") ? file_path : join11(cwd, file_path);
45594
+ const absPath = file_path.startsWith("/") ? file_path : join12(cwd, file_path);
45209
45595
  const relPath = file_path.startsWith("/") ? file_path.slice(cwd.length + 1) : file_path;
45210
45596
  try {
45211
45597
  const result = [];
45212
45598
  if (direction === "imports" || direction === "both") {
45213
- const content = readFileSync8(absPath, "utf-8");
45599
+ const content = readFileSync9(absPath, "utf-8");
45214
45600
  const parsed = parseFile(absPath, content);
45215
45601
  result.push(`Imports from ${relPath}:`);
45216
45602
  if (parsed.imports.length === 0) {
45217
45603
  result.push(" (none)");
45218
45604
  } else {
45219
45605
  for (const imp of parsed.imports) {
45220
- const resolved = tryResolve(imp.targetPath, dirname(absPath));
45606
+ const resolved = tryResolve(imp.targetPath, dirname2(absPath));
45221
45607
  const syms = imp.importedSymbols.join(", ");
45222
45608
  const resolvedNote = resolved ? ` \u2192 ${resolved.slice(cwd.length + 1)}` : "";
45223
45609
  result.push(` ${imp.targetPath} {${syms}}${resolvedNote}`);
@@ -45261,10 +45647,10 @@ function tryResolve(importPath, fromDir) {
45261
45647
  "/index.ts",
45262
45648
  "/index.js"
45263
45649
  ];
45264
- const base = resolve(fromDir, importPath);
45650
+ const base = resolve2(fromDir, importPath);
45265
45651
  for (const ext of extensions) {
45266
45652
  const candidate = base + ext;
45267
- if (existsSync4(candidate))
45653
+ if (existsSync5(candidate))
45268
45654
  return candidate;
45269
45655
  }
45270
45656
  return null;
@@ -45272,7 +45658,7 @@ function tryResolve(importPath, fromDir) {
45272
45658
  function findImportedBy(cwd, targetRelPath) {
45273
45659
  let filePaths;
45274
45660
  try {
45275
- const stdout = execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd, maxBuffer: 10 * 1024 * 1024 }).toString();
45661
+ const stdout = execFileSync2("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd, maxBuffer: 10 * 1024 * 1024 }).toString();
45276
45662
  filePaths = stdout.trim().split("\n").filter(Boolean);
45277
45663
  } catch {
45278
45664
  return [];
@@ -45299,10 +45685,10 @@ function findImportedBy(cwd, targetRelPath) {
45299
45685
  if (!codeExts.has(ext))
45300
45686
  continue;
45301
45687
  try {
45302
- const content = readFileSync8(join11(cwd, fp), "utf-8");
45303
- const parsed = parseFile(join11(cwd, fp), content);
45688
+ const content = readFileSync9(join12(cwd, fp), "utf-8");
45689
+ const parsed = parseFile(join12(cwd, fp), content);
45304
45690
  for (const imp of parsed.imports) {
45305
- if (importMatchesTarget(imp.targetPath, fp, targetRelPath, targetName)) {
45691
+ if (importMatchesTarget2(imp.targetPath, fp, targetRelPath, targetName)) {
45306
45692
  results.push({
45307
45693
  file: fp,
45308
45694
  symbols: imp.importedSymbols
@@ -45315,11 +45701,11 @@ function findImportedBy(cwd, targetRelPath) {
45315
45701
  }
45316
45702
  return results;
45317
45703
  }
45318
- function importMatchesTarget(importPath, sourceFile, targetRelPath, targetName) {
45704
+ function importMatchesTarget2(importPath, sourceFile, targetRelPath, targetName) {
45319
45705
  if (!importPath.startsWith("."))
45320
45706
  return false;
45321
- const sourceDir = dirname(sourceFile);
45322
- const resolved = join11(sourceDir, importPath).replace(/\.[^.]+$/, "").replace(/\/index$/, "");
45707
+ const sourceDir = dirname2(sourceFile);
45708
+ const resolved = join12(sourceDir, importPath).replace(/\.[^.]+$/, "").replace(/\/index$/, "");
45323
45709
  return resolved === targetName || resolved === targetRelPath.replace(/\.[^.]+$/, "");
45324
45710
  }
45325
45711
 
@@ -45333,7 +45719,7 @@ function registerImpactTool(server) {
45333
45719
  const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
45334
45720
  try {
45335
45721
  const results = await ripgrepSearch({
45336
- query: `\\b${escapeRegex2(symbol2)}\\b`,
45722
+ query: `\\b${escapeRegex3(symbol2)}\\b`,
45337
45723
  cwd,
45338
45724
  maxResults: 50,
45339
45725
  regex: true,
@@ -45388,33 +45774,33 @@ function classifyUsage(lineText, symbolName) {
45388
45774
  if (/^import\b/.test(trimmed) || /^from\b/.test(trimmed) || /require\(/.test(trimmed)) {
45389
45775
  return "import";
45390
45776
  }
45391
- if (new RegExp(`^(export\\s+)?(class|interface|type|enum)\\s+${escapeRegex2(symbolName)}`).test(trimmed)) {
45777
+ if (new RegExp(`^(export\\s+)?(class|interface|type|enum)\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
45392
45778
  return "definition";
45393
45779
  }
45394
- if (new RegExp(`^(export\\s+)?(function|const|let|var|async\\s+function)\\s+${escapeRegex2(symbolName)}`).test(trimmed)) {
45780
+ if (new RegExp(`^(export\\s+)?(function|const|let|var|async\\s+function)\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
45395
45781
  return "definition";
45396
45782
  }
45397
- if (new RegExp(`^(pub\\s+)?fn\\s+${escapeRegex2(symbolName)}`).test(trimmed)) {
45783
+ if (new RegExp(`^(pub\\s+)?fn\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
45398
45784
  return "definition";
45399
45785
  }
45400
- if (new RegExp(`^def\\s+${escapeRegex2(symbolName)}`).test(trimmed)) {
45786
+ if (new RegExp(`^def\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
45401
45787
  return "definition";
45402
45788
  }
45403
- if (new RegExp(`extends\\s+${escapeRegex2(symbolName)}`).test(trimmed)) {
45789
+ if (new RegExp(`extends\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
45404
45790
  return "extends";
45405
45791
  }
45406
- if (new RegExp(`implements\\s+.*${escapeRegex2(symbolName)}`).test(trimmed)) {
45792
+ if (new RegExp(`implements\\s+.*${escapeRegex3(symbolName)}`).test(trimmed)) {
45407
45793
  return "implements";
45408
45794
  }
45409
- if (new RegExp(`:\\s*${escapeRegex2(symbolName)}[<\\[|\\s,;>)]`).test(trimmed)) {
45795
+ if (new RegExp(`:\\s*${escapeRegex3(symbolName)}[<\\[|\\s,;>)]`).test(trimmed)) {
45410
45796
  return "type_ref";
45411
45797
  }
45412
- if (new RegExp(`${escapeRegex2(symbolName)}\\s*\\(`).test(trimmed)) {
45798
+ if (new RegExp(`${escapeRegex3(symbolName)}\\s*\\(`).test(trimmed)) {
45413
45799
  return "call";
45414
45800
  }
45415
45801
  return "reference";
45416
45802
  }
45417
- function escapeRegex2(str) {
45803
+ function escapeRegex3(str) {
45418
45804
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
45419
45805
  }
45420
45806
 
@@ -45572,9 +45958,51 @@ Purely heuristic \u2014 no LLM required. Useful to understand a project's style
45572
45958
  });
45573
45959
  }
45574
45960
 
45961
+ // ../server/dist/tools/context-bundle.js
45962
+ function registerContextBundleTool(server) {
45963
+ server.tool("abf_context_bundle", `Bundle multi-file context around an entry point in a single call.
45964
+ Traverses the import graph from the entry file, collecting symbols and optionally source code from all connected files.
45965
+ Replaces multiple read_file + abf_symbols + abf_dependencies calls with one compact result.
45966
+
45967
+ Modes:
45968
+ - "signatures": compact \u2014 shows exported symbols with type signatures for all files (lowest tokens)
45969
+ - "full": returns full source code for all files up to the specified depth
45970
+ - "smart" (default): full source code for the entry file, signatures for dependencies
45971
+
45972
+ Use focus_symbol to narrow the bundle to only imports relevant to a specific function/class.
45973
+ Use reverse=true to also find files that import the entry file.`, {
45974
+ entry: external_exports3.string().describe("Entry file path (relative to project root or absolute)"),
45975
+ depth: external_exports3.number().int().min(0).max(4).default(1).describe("How deep to follow the import graph (0 = entry only, default: 1)"),
45976
+ include: external_exports3.enum(["signatures", "full", "smart"]).default("smart").describe('What to include: "signatures" (compact types), "full" (source code), "smart" (full entry + signature deps)'),
45977
+ focus_symbol: external_exports3.string().optional().describe("Focus on a specific symbol \u2014 only follows imports relevant to this function/class"),
45978
+ reverse: external_exports3.boolean().default(false).describe("Also include files that import the entry file (reverse dependencies)")
45979
+ }, async ({ entry, depth, include, focus_symbol, reverse }) => {
45980
+ const projectRoot = process.env.ABF_PROJECT_ROOT || process.cwd();
45981
+ try {
45982
+ const result = buildContextBundle({
45983
+ entry,
45984
+ projectRoot,
45985
+ depth,
45986
+ include,
45987
+ focusSymbol: focus_symbol,
45988
+ reverse
45989
+ });
45990
+ const formatted = formatContextBundle(result);
45991
+ return {
45992
+ content: [{ type: "text", text: formatted }]
45993
+ };
45994
+ } catch (err) {
45995
+ const msg = err instanceof Error ? err.message : String(err);
45996
+ return {
45997
+ content: [{ type: "text", text: `Error: ${msg}` }]
45998
+ };
45999
+ }
46000
+ });
46001
+ }
46002
+
45575
46003
  // ../server/dist/server.js
45576
46004
  var SERVER_NAME = "agents-best-friend";
45577
- var SERVER_VERSION = "0.8.0";
46005
+ var SERVER_VERSION = "0.10.0";
45578
46006
  function createAbfServer() {
45579
46007
  const server = new McpServer({
45580
46008
  name: SERVER_NAME,
@@ -45598,6 +46026,7 @@ function createAbfServer() {
45598
46026
  registerImpactTool(server);
45599
46027
  registerFileSummaryTool(server);
45600
46028
  registerConventionsTool(server);
46029
+ registerContextBundleTool(server);
45601
46030
  return server;
45602
46031
  }
45603
46032
  async function startStdioServer() {
@@ -45623,7 +46052,7 @@ async function startCommand() {
45623
46052
 
45624
46053
  // src/index.ts
45625
46054
  var program = new Command();
45626
- program.name("abf").description("AgentsBestFriend \u2014 AI-first code navigation and analysis tools").version("0.8.0");
46055
+ program.name("abf").description("AgentsBestFriend \u2014 AI-first code navigation and analysis tools").version("0.10.0");
45627
46056
  program.command("start").description("Start the MCP server in stdio mode (for AI agent connections)").action(async () => {
45628
46057
  await startCommand();
45629
46058
  });