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 +533 -104
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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 =
|
|
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
|
|
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
|
|
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,
|
|
3594
|
+
function resolveComponent(base, relative6, options, skipNormalization) {
|
|
3595
3595
|
const target = {};
|
|
3596
3596
|
if (!skipNormalization) {
|
|
3597
3597
|
base = parse3(serialize(base, options), options);
|
|
3598
|
-
|
|
3598
|
+
relative6 = parse3(serialize(relative6, options), options);
|
|
3599
3599
|
}
|
|
3600
3600
|
options = options || {};
|
|
3601
|
-
if (!options.tolerant &&
|
|
3602
|
-
target.scheme =
|
|
3603
|
-
target.userinfo =
|
|
3604
|
-
target.host =
|
|
3605
|
-
target.port =
|
|
3606
|
-
target.path = removeDotSegments(
|
|
3607
|
-
target.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 (
|
|
3610
|
-
target.userinfo =
|
|
3611
|
-
target.host =
|
|
3612
|
-
target.port =
|
|
3613
|
-
target.path = removeDotSegments(
|
|
3614
|
-
target.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 (!
|
|
3616
|
+
if (!relative6.path) {
|
|
3617
3617
|
target.path = base.path;
|
|
3618
|
-
if (
|
|
3619
|
-
target.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 (
|
|
3625
|
-
target.path = removeDotSegments(
|
|
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 = "/" +
|
|
3628
|
+
target.path = "/" + relative6.path;
|
|
3629
3629
|
} else if (!base.path) {
|
|
3630
|
-
target.path =
|
|
3630
|
+
target.path = relative6.path;
|
|
3631
3631
|
} else {
|
|
3632
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
|
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 =
|
|
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((
|
|
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((
|
|
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:
|
|
18345
|
+
const { join: join13 } = await import("path");
|
|
18346
18346
|
let indexSizeBytes = 0;
|
|
18347
18347
|
try {
|
|
18348
|
-
const dbPath =
|
|
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
|
|
19269
|
-
import { existsSync as
|
|
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 =
|
|
19273
|
+
const root = resolve3(projectPath);
|
|
19274
19274
|
clack.intro("ABF Init");
|
|
19275
|
-
if (!
|
|
19275
|
+
if (!existsSync6(root)) {
|
|
19276
19276
|
clack.log.error(`Path does not exist: ${root}`);
|
|
19277
19277
|
process.exit(1);
|
|
19278
19278
|
}
|
|
19279
|
-
const abfDir =
|
|
19280
|
-
const isNew = !
|
|
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 =
|
|
19288
|
-
const gitignoreExists =
|
|
19289
|
-
const alreadyIgnored = gitignoreExists &&
|
|
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
|
|
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
|
-
|
|
19417
|
-
|
|
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 "
|
|
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
|
|
19484
|
+
import { resolve as resolve4 } from "path";
|
|
19459
19485
|
async function indexCommand(projectPath) {
|
|
19460
|
-
const root =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
19986
|
+
import { resolve as resolve6 } from "path";
|
|
19961
19987
|
async function statusCommand(projectPath) {
|
|
19962
|
-
const root =
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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(
|
|
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((
|
|
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((
|
|
43451
|
+
return new Promise((resolve7) => {
|
|
43426
43452
|
const json2 = serializeMessage(message);
|
|
43427
43453
|
if (this._stdout.write(json2)) {
|
|
43428
|
-
|
|
43454
|
+
resolve7();
|
|
43429
43455
|
} else {
|
|
43430
|
-
this._stdout.once("drain",
|
|
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.
|
|
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("
|
|
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
|
|
44996
|
-
import { join as
|
|
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 :
|
|
45389
|
+
const absPath = file_path.startsWith("/") ? file_path : join10(cwd, file_path);
|
|
45004
45390
|
try {
|
|
45005
|
-
const content =
|
|
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
|
|
45042
|
-
import { join as
|
|
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 :
|
|
45440
|
+
const absPath = file_path.startsWith("/") ? file_path : join11(projectRoot, file_path);
|
|
45055
45441
|
try {
|
|
45056
|
-
const content =
|
|
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
|
|
45198
|
-
import { join as
|
|
45199
|
-
import { existsSync as
|
|
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 :
|
|
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 =
|
|
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,
|
|
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 =
|
|
45650
|
+
const base = resolve2(fromDir, importPath);
|
|
45265
45651
|
for (const ext of extensions) {
|
|
45266
45652
|
const candidate = base + ext;
|
|
45267
|
-
if (
|
|
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 =
|
|
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 =
|
|
45303
|
-
const parsed = parseFile(
|
|
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 (
|
|
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
|
|
45704
|
+
function importMatchesTarget2(importPath, sourceFile, targetRelPath, targetName) {
|
|
45319
45705
|
if (!importPath.startsWith("."))
|
|
45320
45706
|
return false;
|
|
45321
|
-
const sourceDir =
|
|
45322
|
-
const resolved =
|
|
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${
|
|
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+${
|
|
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+${
|
|
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+${
|
|
45783
|
+
if (new RegExp(`^(pub\\s+)?fn\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
|
|
45398
45784
|
return "definition";
|
|
45399
45785
|
}
|
|
45400
|
-
if (new RegExp(`^def\\s+${
|
|
45786
|
+
if (new RegExp(`^def\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
|
|
45401
45787
|
return "definition";
|
|
45402
45788
|
}
|
|
45403
|
-
if (new RegExp(`extends\\s+${
|
|
45789
|
+
if (new RegExp(`extends\\s+${escapeRegex3(symbolName)}`).test(trimmed)) {
|
|
45404
45790
|
return "extends";
|
|
45405
45791
|
}
|
|
45406
|
-
if (new RegExp(`implements\\s+.*${
|
|
45792
|
+
if (new RegExp(`implements\\s+.*${escapeRegex3(symbolName)}`).test(trimmed)) {
|
|
45407
45793
|
return "implements";
|
|
45408
45794
|
}
|
|
45409
|
-
if (new RegExp(`:\\s*${
|
|
45795
|
+
if (new RegExp(`:\\s*${escapeRegex3(symbolName)}[<\\[|\\s,;>)]`).test(trimmed)) {
|
|
45410
45796
|
return "type_ref";
|
|
45411
45797
|
}
|
|
45412
|
-
if (new RegExp(`${
|
|
45798
|
+
if (new RegExp(`${escapeRegex3(symbolName)}\\s*\\(`).test(trimmed)) {
|
|
45413
45799
|
return "call";
|
|
45414
45800
|
}
|
|
45415
45801
|
return "reference";
|
|
45416
45802
|
}
|
|
45417
|
-
function
|
|
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.
|
|
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.
|
|
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
|
});
|