agentsbestfriend 0.10.0 → 0.11.1

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.
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: abf
3
+ description: "Use AgentsBestFriend (ABF) MCP tools for codebase navigation, search, and analysis instead of native file-reading tools. USE WHEN: exploring unfamiliar code, understanding architecture, searching across files, tracing imports/dependencies, analyzing change impact, reading large files, or needing project context. Prefer ABF tools over read_file, grep_search, semantic_search, and list_dir for multi-file workflows — they return pre-structured, token-efficient results in a single call."
4
+ ---
5
+
6
+ # AgentsBestFriend (ABF) — Codebase Navigation Skill
7
+
8
+ ABF is a local MCP server that provides AI-optimized tools for navigating, searching, and analyzing code repositories. These tools return structured, compact results that save 50–80% of tokens compared to manually reading files.
9
+
10
+ ## When to Use ABF Tools
11
+
12
+ **Always prefer ABF tools when you need to:**
13
+ - Understand a project's structure or tech stack
14
+ - Search for code across files (exact, keyword, or semantic)
15
+ - Read specific functions or classes from large files
16
+ - Trace imports, dependencies, or reverse dependencies
17
+ - Assess the impact of changing a symbol
18
+ - Get multi-file context around an entry point
19
+
20
+ **Use native tools only when:**
21
+ - Making edits to files (ABF is read-only)
22
+ - Checking for lint/compile errors
23
+ - Running terminal commands
24
+ - The ABF MCP server is not connected
25
+
26
+ ## Decision Matrix
27
+
28
+ | Task | ABF Tool | Instead of |
29
+ |------|----------|------------|
30
+ | Orient in a new project | `abf_project_overview` | Reading README + listing directories + checking package.json |
31
+ | Find where something is defined | `abf_search` (exact mode) | `grep_search` |
32
+ | Find files related to a concept | `abf_search` (keyword mode) | Multiple `semantic_search` + `grep_search` calls |
33
+ | Read a specific function from a large file | `abf_chunk` with symbol name | `read_file` (which loads entire file or requires guessing line ranges) |
34
+ | List all exports of a file | `abf_symbols` | `read_file` and scanning manually |
35
+ | Understand what a file imports and who imports it | `abf_dependencies` | Multiple `grep_search` calls for import statements |
36
+ | Get multi-file context around one entry point | `abf_context_bundle` | 5–10 calls to `read_file` + `abf_symbols` + `abf_dependencies` |
37
+ | Find all usages of a function/class | `abf_impact` | `grep_search` with manual filtering |
38
+ | Understand project conventions | `abf_conventions` | Reading multiple config files manually |
39
+ | Search by file purpose/description | `abf_file_summary` | No native equivalent |
40
+ | Check git history or blame | `abf_git` | `run_in_terminal` with git commands |
41
+
42
+ ## Recommended Workflows
43
+
44
+ ### 1. First Contact with a Codebase
45
+
46
+ ```
47
+ 1. abf_project_overview → architecture, tech stack, entry points
48
+ 2. abf_conventions → coding style, patterns, naming
49
+ 3. abf_search (keyword: "main topic") → find relevant files
50
+ ```
51
+
52
+ ### 2. Understanding a Specific File
53
+
54
+ ```
55
+ 1. abf_symbols (file) → see all exports/functions at a glance
56
+ 2. abf_chunk (file, symbol: "name") → read just the function you care about
57
+ 3. abf_dependencies (file) → see what it imports and who imports it
58
+ ```
59
+
60
+ ### 3. Deep Dive into a Feature
61
+
62
+ ```
63
+ 1. abf_context_bundle (entry file, depth: 2, include: "smart")
64
+ → full source of entry + signatures of all dependencies in ONE call
65
+ 2. If needed: abf_chunk to read specific dependency functions
66
+ ```
67
+
68
+ ### 4. Change Impact Analysis
69
+
70
+ ```
71
+ 1. abf_impact (symbol: "functionName") → all files and lines referencing it
72
+ 2. abf_context_bundle (entry, focus_symbol: "functionName", reverse: true)
73
+ → focused view of the function + its callers
74
+ ```
75
+
76
+ ### 5. Searching for Code
77
+
78
+ ```
79
+ - Know the exact name? → abf_search mode: "exact"
80
+ - Exploring a concept? → abf_search mode: "keyword"
81
+ - Describe what you need? → abf_file_summary (searches LLM-generated descriptions)
82
+ - Semantic similarity? → abf_search mode: "semantic" (requires Ollama)
83
+ ```
84
+
85
+ ## Tool Reference
86
+
87
+ ### abf_project_overview
88
+ **When:** Starting work on a project, or when asked "what does this project do?"
89
+ **Saves:** Reading README + package.json + listing directories manually (3–5 calls → 1)
90
+ - Returns: tech stack, frameworks, entry points, directory structure, language distribution, architectural patterns
91
+ - No index required — works immediately
92
+
93
+ ### abf_search
94
+ **When:** Looking for code, files, or patterns across the project
95
+ **Saves:** Multiple grep_search or semantic_search calls
96
+ - `mode: "exact"` — ripgrep-powered, supports regex, returns matching lines with context
97
+ - `mode: "keyword"` — scores every file by keyword density, best for exploration
98
+ - `mode: "semantic"` — embedding similarity (requires Ollama + index with embeddings)
99
+ - Use `path_filter` to narrow scope (e.g. `"src/**/*.ts"`)
100
+
101
+ ### abf_context_bundle
102
+ **When:** You need to understand a file in the context of its dependencies
103
+ **Saves:** 5–10 calls to read_file + abf_symbols + abf_dependencies (biggest saver)
104
+ - `include: "smart"` (default) — full source for entry, signatures for deps
105
+ - `include: "signatures"` — compact type signatures only (minimal tokens)
106
+ - `include: "full"` — full source code for all files up to depth
107
+ - `focus_symbol` — only follows imports relevant to one function/class
108
+ - `reverse: true` — also shows who imports this file
109
+ - `depth: 0–4` — how far to follow the import graph
110
+
111
+ ### abf_chunk
112
+ **When:** You need to read a specific function/class from a file without loading the entire file
113
+ **Saves:** read_file loading hundreds of irrelevant lines
114
+ - Call with `symbol: "functionName"` to get its full source code directly
115
+ - Call without symbol first to get a chunk overview, then use `chunk_index` to retrieve specific sections
116
+
117
+ ### abf_symbols
118
+ **When:** You need to see what a file exports without reading its full content
119
+ **Saves:** read_file + mentally parsing the file structure
120
+ - Returns: function signatures, classes, interfaces, types, variables with line ranges
121
+ - Shows export status (★ = exported) and nesting
122
+
123
+ ### abf_dependencies
124
+ **When:** Tracing what a file imports or finding who depends on it
125
+ **Saves:** Multiple grep_search calls for import statements
126
+ - Returns both imports and reverse dependencies (imported_by)
127
+
128
+ ### abf_impact
129
+ **When:** Assessing how widely a symbol is used before changing it
130
+ **Saves:** grep_search + manual filtering of false positives
131
+ - Returns all files and specific lines that reference the symbol
132
+ - Classifies usage type (call, import, type reference, etc.)
133
+
134
+ ### abf_file_summary
135
+ **When:** Searching by file purpose rather than exact code text
136
+ **Saves:** No native equivalent — unique capability
137
+ - Full-text search across LLM-generated file descriptions
138
+ - Requires summaries to be generated first (`abf_index` action: summarize)
139
+
140
+ ### abf_conventions
141
+ **When:** Understanding project style before making changes
142
+ **Saves:** Reading eslint, tsconfig, prettier, and other config files manually
143
+ - Detects naming patterns, design patterns, folder structure conventions
144
+ - Returns confidence scores and examples
145
+
146
+ ### abf_git
147
+ **When:** Checking history, blame, or diffs
148
+ **Saves:** Running git commands in terminal and parsing output
149
+ - Actions: log, file_history, blame, diff
150
+ - Structured output ready for analysis
151
+
152
+ ### abf_index
153
+ **When:** Managing the ABF index
154
+ - `status` — check index health
155
+ - `rebuild` — full re-index
156
+ - `update` — incremental update
157
+ - `summarize` — generate LLM summaries (requires Ollama)
158
+
159
+ ### abf_ping
160
+ **When:** Verifying ABF is running
161
+ - Returns server version, project root, and status
package/dist/index.js CHANGED
@@ -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 join13(chunks, separator) {
12408
+ function join14(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 = join13;
12418
+ sql2.join = join14;
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((join13) => join13.alias === tableName)) {
15403
+ if (typeof tableName === "string" && this.config.joins?.some((join14) => join14.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((join13) => join13.alias === tableName)) {
16293
+ if (typeof tableName === "string" && this.config.joins.some((join14) => join14.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: join13 } = await import("path");
18345
+ const { join: join14 } = await import("path");
18346
18346
  let indexSizeBytes = 0;
18347
18347
  try {
18348
- const dbPath = join13(projectRoot, ".abf", "index.db");
18348
+ const dbPath = join14(projectRoot, ".abf", "index.db");
18349
18349
  indexSizeBytes = statSync3(dbPath).size;
18350
18350
  } catch {
18351
18351
  }
@@ -19265,8 +19265,9 @@ __export(init_exports, {
19265
19265
  initCommand: () => initCommand
19266
19266
  });
19267
19267
  import * as clack from "@clack/prompts";
19268
- import { resolve as resolve3 } from "path";
19268
+ import { resolve as resolve3, dirname as dirname3, join as join13 } from "path";
19269
19269
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync10, appendFileSync } from "fs";
19270
+ import { fileURLToPath } from "url";
19270
19271
  import { execFile as execFile5 } from "child_process";
19271
19272
  import { promisify as promisify5 } from "util";
19272
19273
  async function initCommand(projectPath) {
@@ -19357,6 +19358,7 @@ async function initCommand(projectPath) {
19357
19358
  "LLM enrichment skipped (Ollama not available). Run `abf index --summarize` later."
19358
19359
  );
19359
19360
  }
19361
+ await installSkill(root);
19360
19362
  const installMcp = await clack.confirm({
19361
19363
  message: "Install ABF as an MCP server for your coding agents?",
19362
19364
  initialValue: true
@@ -19415,7 +19417,7 @@ async function initCommand(projectPath) {
19415
19417
  clack.outro(isNew ? "Project initialized!" : "Index rebuilt!");
19416
19418
  return;
19417
19419
  }
19418
- const mcpCommand = mcpSource === "npx" ? "npx agentsbestfriend start" : "abf start";
19420
+ const mcpCommand = mcpSource === "npx" ? "npx -y agentsbestfriend start" : "abf start";
19419
19421
  const addMcpArgs = ["add-mcp", mcpCommand, "--name", "abf", "-y"];
19420
19422
  for (const agent of selectedAgents) {
19421
19423
  addMcpArgs.push("-a", agent);
@@ -19453,6 +19455,117 @@ You can install manually: npx add-mcp "${mcpCommand}" --name abf -y`
19453
19455
  }
19454
19456
  clack.outro(isNew ? "Project initialized!" : "Index rebuilt!");
19455
19457
  }
19458
+ function getSkillDir() {
19459
+ const candidates = [
19460
+ // Installed via npm/npx — assets is sibling to dist/
19461
+ join13(dirname3(fileURLToPath(import.meta.url)), "..", "assets", "skills"),
19462
+ // Monorepo dev — two levels up from dist/
19463
+ join13(
19464
+ dirname3(fileURLToPath(import.meta.url)),
19465
+ "..",
19466
+ "..",
19467
+ "assets",
19468
+ "skills"
19469
+ )
19470
+ ];
19471
+ for (const candidate of candidates) {
19472
+ const resolved = resolve3(candidate);
19473
+ if (existsSync6(join13(resolved, "abf", "SKILL.md"))) return resolved;
19474
+ }
19475
+ return null;
19476
+ }
19477
+ async function installSkill(projectRoot) {
19478
+ const doInstall = await clack.confirm({
19479
+ message: "Install ABF workflow skill? (Helps agents prefer ABF tools over native read_file/grep)",
19480
+ initialValue: true
19481
+ });
19482
+ if (clack.isCancel(doInstall) || !doInstall) return;
19483
+ const skillDir = getSkillDir();
19484
+ if (!skillDir) {
19485
+ clack.log.warn("Could not find bundled skill asset \u2014 skipping.");
19486
+ return;
19487
+ }
19488
+ const agentChoice = await clack.select({
19489
+ message: "Install skill for which agents?",
19490
+ options: [
19491
+ {
19492
+ value: "all",
19493
+ label: "All detected agents",
19494
+ hint: "Auto-detects installed agents"
19495
+ },
19496
+ {
19497
+ value: "pick",
19498
+ label: "Let me pick"
19499
+ }
19500
+ ]
19501
+ });
19502
+ if (clack.isCancel(agentChoice)) return;
19503
+ let skillAgentArgs = [];
19504
+ if (agentChoice === "pick") {
19505
+ const skillAgents = AGENTS.filter((a) => a.skillAgent);
19506
+ const selected = await clack.multiselect({
19507
+ message: "Which agents should get the ABF skill?",
19508
+ options: skillAgents.map((a) => ({
19509
+ value: a.skillAgent,
19510
+ label: a.label
19511
+ })),
19512
+ required: true
19513
+ });
19514
+ if (clack.isCancel(selected)) return;
19515
+ for (const agent of selected) {
19516
+ skillAgentArgs.push("-a", agent);
19517
+ }
19518
+ }
19519
+ const scope = await clack.select({
19520
+ message: "Skill installation scope?",
19521
+ options: [
19522
+ {
19523
+ value: "project",
19524
+ label: "Project (default)",
19525
+ hint: "Committed with your repo, shared with team"
19526
+ },
19527
+ {
19528
+ value: "global",
19529
+ label: "Global",
19530
+ hint: "Available across all projects"
19531
+ }
19532
+ ]
19533
+ });
19534
+ if (clack.isCancel(scope)) return;
19535
+ const skillArgs = [
19536
+ "skills",
19537
+ "add",
19538
+ skillDir,
19539
+ "--skill",
19540
+ "abf",
19541
+ "--copy",
19542
+ "-y"
19543
+ ];
19544
+ if (agentChoice === "all") {
19545
+ skillArgs.push("--all");
19546
+ } else {
19547
+ skillArgs.push(...skillAgentArgs);
19548
+ }
19549
+ if (scope === "global") {
19550
+ skillArgs.push("-g");
19551
+ }
19552
+ const skillSpinner = clack.spinner();
19553
+ skillSpinner.start("Installing ABF skill...");
19554
+ try {
19555
+ await execFileAsync5("npx", skillArgs, {
19556
+ cwd: projectRoot,
19557
+ timeout: 6e4
19558
+ });
19559
+ skillSpinner.stop("ABF skill installed successfully");
19560
+ } catch (err) {
19561
+ skillSpinner.stop("Skill installation failed");
19562
+ const msg = err instanceof Error ? err.message : String(err);
19563
+ clack.log.warn(
19564
+ `Could not install skill: ${msg}
19565
+ You can install manually: npx skills add ${skillDir} --skill abf --copy -y`
19566
+ );
19567
+ }
19568
+ }
19456
19569
  var execFileAsync5, AGENTS;
19457
19570
  var init_init = __esm({
19458
19571
  "src/commands/init.ts"() {
@@ -19461,16 +19574,20 @@ var init_init = __esm({
19461
19574
  init_llm();
19462
19575
  execFileAsync5 = promisify5(execFile5);
19463
19576
  AGENTS = [
19464
- { value: "cursor", label: "Cursor" },
19465
- { value: "vscode", label: "VS Code / GitHub Copilot" },
19466
- { value: "claude-code", label: "Claude Code" },
19467
- { value: "claude-desktop", label: "Claude Desktop" },
19468
- { value: "codex", label: "Codex" },
19469
- { value: "cline", label: "Cline" },
19470
- { value: "zed", label: "Zed" },
19471
- { value: "gemini-cli", label: "Gemini CLI" },
19472
- { value: "goose", label: "Goose" },
19473
- { value: "opencode", label: "OpenCode" }
19577
+ { value: "cursor", label: "Cursor", skillAgent: "cursor" },
19578
+ {
19579
+ value: "vscode",
19580
+ label: "VS Code / GitHub Copilot",
19581
+ skillAgent: "github-copilot"
19582
+ },
19583
+ { value: "claude-code", label: "Claude Code", skillAgent: "claude-code" },
19584
+ { value: "claude-desktop", label: "Claude Desktop", skillAgent: void 0 },
19585
+ { value: "codex", label: "Codex", skillAgent: "codex" },
19586
+ { value: "cline", label: "Cline", skillAgent: "cline" },
19587
+ { value: "zed", label: "Zed", skillAgent: void 0 },
19588
+ { value: "gemini-cli", label: "Gemini CLI", skillAgent: "gemini-cli" },
19589
+ { value: "goose", label: "Goose", skillAgent: "goose" },
19590
+ { value: "opencode", label: "OpenCode", skillAgent: "opencode" }
19474
19591
  ];
19475
19592
  }
19476
19593
  });
@@ -43471,7 +43588,7 @@ function registerPingTool(server) {
43471
43588
  const status = {
43472
43589
  status: "ok",
43473
43590
  server: "agents-best-friend",
43474
- version: "0.10.0",
43591
+ version: "0.11.1",
43475
43592
  projectRoot,
43476
43593
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
43477
43594
  };
@@ -46002,7 +46119,7 @@ Use reverse=true to also find files that import the entry file.`, {
46002
46119
 
46003
46120
  // ../server/dist/server.js
46004
46121
  var SERVER_NAME = "agents-best-friend";
46005
- var SERVER_VERSION = "0.10.0";
46122
+ var SERVER_VERSION = "0.11.1";
46006
46123
  function createAbfServer() {
46007
46124
  const server = new McpServer({
46008
46125
  name: SERVER_NAME,
@@ -46052,7 +46169,7 @@ async function startCommand() {
46052
46169
 
46053
46170
  // src/index.ts
46054
46171
  var program = new Command();
46055
- program.name("abf").description("AgentsBestFriend \u2014 AI-first code navigation and analysis tools").version("0.10.0");
46172
+ program.name("abf").description("AgentsBestFriend \u2014 AI-first code navigation and analysis tools").version("0.11.1");
46056
46173
  program.command("start").description("Start the MCP server in stdio mode (for AI agent connections)").action(async () => {
46057
46174
  await startCommand();
46058
46175
  });