agentsbestfriend 0.1.4 → 0.2.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
@@ -18059,9 +18059,52 @@ Summary:`;
18059
18059
  // ../core/dist/llm/pipelines.js
18060
18060
  import { readFile } from "fs/promises";
18061
18061
  import { join as join4 } from "path";
18062
+ async function generateSummaries(projectRoot, opts) {
18063
+ const start = Date.now();
18064
+ const provider = getLlmProvider();
18065
+ if (!provider) {
18066
+ throw new LlmUnavailableError("none", "LLM provider is set to 'none'");
18067
+ }
18068
+ if (!await provider.isAvailable()) {
18069
+ throw new LlmUnavailableError(provider.name, "Cannot reach Ollama. Is it running?");
18070
+ }
18071
+ const db = createProjectDb(projectRoot);
18072
+ const stats = {
18073
+ generated: 0,
18074
+ skipped: 0,
18075
+ errors: 0,
18076
+ durationMs: 0
18077
+ };
18078
+ try {
18079
+ const rows = opts?.force ? db.select({ id: files.id, path: files.path }).from(files).all() : db.select({ id: files.id, path: files.path }).from(files).where(isNull(files.summary)).all();
18080
+ const batchSize = opts?.batchSize ?? 5;
18081
+ for (let i = 0; i < rows.length; i += batchSize) {
18082
+ const batch = rows.slice(i, i + batchSize);
18083
+ for (const row of batch) {
18084
+ try {
18085
+ const absPath = join4(projectRoot, row.path);
18086
+ const content = await readFile(absPath, "utf-8");
18087
+ const summary = await provider.generateSummary(content, row.path);
18088
+ db.update(files).set({ summary }).where(eq(files.id, row.id)).run();
18089
+ stats.generated++;
18090
+ } catch (err) {
18091
+ if (err instanceof LlmUnavailableError)
18092
+ throw err;
18093
+ stats.errors++;
18094
+ }
18095
+ }
18096
+ }
18097
+ stats.skipped = rows.length === 0 ? db.select({ id: files.id }).from(files).all().length : 0;
18098
+ stats.durationMs = Date.now() - start;
18099
+ return stats;
18100
+ } finally {
18101
+ closeDb(db);
18102
+ }
18103
+ }
18062
18104
  var init_pipelines = __esm({
18063
18105
  "../core/dist/llm/pipelines.js"() {
18064
18106
  "use strict";
18107
+ init_drizzle_orm();
18065
18108
  init_connection();
18066
18109
  init_schema2();
18067
18110
  init_llm();
@@ -44517,7 +44560,7 @@ function registerGitTool(server) {
44517
44560
  line_start: external_exports3.number().int().optional().describe("Start line for blame range"),
44518
44561
  line_end: external_exports3.number().int().optional().describe("End line for blame range")
44519
44562
  }, async ({ action, file: file2, count, line_start, line_end }) => {
44520
- const cwd = process.cwd();
44563
+ const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
44521
44564
  if (!await isGitRepo(cwd)) {
44522
44565
  return {
44523
44566
  content: [
@@ -44595,16 +44638,17 @@ function errorResult(msg) {
44595
44638
 
44596
44639
  // ../server/dist/tools/index-tool.js
44597
44640
  init_indexer();
44598
- var IndexActionSchema = external_exports3.enum(["status", "rebuild", "update"]);
44641
+ init_llm();
44642
+ var IndexActionSchema = external_exports3.enum(["status", "rebuild", "update", "summarize"]);
44599
44643
  function registerIndexTool(server) {
44600
- server.tool("abf_index", "Manage the file index: check status, trigger rebuild, or incremental update.", {
44601
- action: IndexActionSchema.describe("status: show index info, rebuild: full re-index, update: incremental update")
44644
+ server.tool("abf_index", "Manage the file index: check status, trigger rebuild, incremental update, or generate LLM summaries (requires Ollama).", {
44645
+ action: IndexActionSchema.describe("status: show index info, rebuild: full re-index, update: incremental update, summarize: generate LLM file summaries (requires Ollama)")
44602
44646
  }, async ({ action }) => {
44603
- const cwd = process.cwd();
44647
+ const projectRoot = process.env.ABF_PROJECT_ROOT || process.cwd();
44604
44648
  try {
44605
44649
  switch (action) {
44606
44650
  case "status": {
44607
- const status = await getIndexStatus(cwd);
44651
+ const status = await getIndexStatus(projectRoot);
44608
44652
  const lastUp = status.lastUpdated ? status.lastUpdated.toISOString() : "never";
44609
44653
  const sizeMb = (status.indexSizeBytes / (1024 * 1024)).toFixed(2);
44610
44654
  const text4 = [
@@ -44617,7 +44661,7 @@ function registerIndexTool(server) {
44617
44661
  }
44618
44662
  case "rebuild":
44619
44663
  case "update": {
44620
- const stats = await runIndexPipeline(cwd);
44664
+ const stats = await runIndexPipeline(projectRoot);
44621
44665
  const text4 = [
44622
44666
  `Index ${action} complete (${stats.durationMs}ms)`,
44623
44667
  `Discovered: ${stats.totalDiscovered}`,
@@ -44629,6 +44673,16 @@ function registerIndexTool(server) {
44629
44673
  ].filter(Boolean).join("\n");
44630
44674
  return { content: [{ type: "text", text: text4 }] };
44631
44675
  }
44676
+ case "summarize": {
44677
+ const stats = await generateSummaries(projectRoot);
44678
+ const text4 = [
44679
+ `Summary generation complete (${stats.durationMs}ms)`,
44680
+ `Generated: ${stats.generated}`,
44681
+ `Skipped (already have summary): ${stats.skipped}`,
44682
+ stats.errors > 0 ? `Errors: ${stats.errors}` : null
44683
+ ].filter(Boolean).join("\n");
44684
+ return { content: [{ type: "text", text: text4 }] };
44685
+ }
44632
44686
  }
44633
44687
  } catch (err) {
44634
44688
  const msg = err instanceof Error ? err.message : String(err);
@@ -44647,7 +44701,7 @@ function registerSymbolsTool(server) {
44647
44701
  file_path: external_exports3.string().describe("Path to the file (relative or absolute)"),
44648
44702
  depth: external_exports3.number().int().min(1).max(5).default(2).describe("How deep to show nested symbols")
44649
44703
  }, async ({ file_path, depth }) => {
44650
- const cwd = process.cwd();
44704
+ const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
44651
44705
  const absPath = file_path.startsWith("/") ? file_path : join9(cwd, file_path);
44652
44706
  try {
44653
44707
  const content = readFileSync6(absPath, "utf-8");
@@ -44689,17 +44743,43 @@ function formatSymbolTree(symbols2, maxDepth, currentDepth) {
44689
44743
  import { readFileSync as readFileSync7 } from "fs";
44690
44744
  import { join as join10 } from "path";
44691
44745
  function registerChunkTool(server) {
44692
- server.tool("abf_chunk", "Smart file chunking by symbol boundaries. Without chunk_index: returns chunk overview. With chunk_index: returns that chunk's content.", {
44693
- file_path: external_exports3.string().describe("Path to the file"),
44694
- chunk_index: external_exports3.number().int().min(0).optional().describe("Request a specific chunk by index (0-based). Omit for overview.")
44695
- }, async ({ file_path, chunk_index }) => {
44696
- const cwd = process.cwd();
44697
- const absPath = file_path.startsWith("/") ? file_path : join10(cwd, file_path);
44746
+ server.tool("abf_chunk", `Smart file chunking by symbol boundaries. Returns actual source code.
44747
+ Use EXACTLY ONE of these modes:
44748
+ - symbol: pass a symbol name to get its full source code directly
44749
+ - chunk_index: pass a 0-based chunk index to get that chunk's code
44750
+ - (neither): returns a chunk overview listing \u2014 use this first to discover available chunks, then call again with chunk_index to retrieve code`, {
44751
+ file_path: external_exports3.string().describe("Path to the file (relative or absolute)"),
44752
+ chunk_index: external_exports3.number().int().min(0).optional().describe("0-based chunk index to retrieve that chunk's source code. Get the index from the overview first."),
44753
+ symbol: external_exports3.string().optional().describe("Name of a symbol (function, class, etc.) to retrieve its full source code directly.")
44754
+ }, async ({ file_path, chunk_index, symbol: symbol2 }) => {
44755
+ const projectRoot = process.env.ABF_PROJECT_ROOT || process.cwd();
44756
+ const absPath = file_path.startsWith("/") ? file_path : join10(projectRoot, file_path);
44698
44757
  try {
44699
44758
  const content = readFileSync7(absPath, "utf-8");
44700
44759
  const lines = content.split("\n");
44701
- const { symbols: symbols2 } = parseFile(absPath, content);
44702
- const chunks = buildChunks(symbols2, lines.length);
44760
+ const { symbols: parsedSymbols } = parseFile(absPath, content);
44761
+ if (symbol2) {
44762
+ const match = findSymbol(parsedSymbols, symbol2);
44763
+ if (!match) {
44764
+ const available = parsedSymbols.map((s) => `${s.kind} ${s.name}`).join(", ");
44765
+ return {
44766
+ content: [
44767
+ {
44768
+ type: "text",
44769
+ text: `Symbol "${symbol2}" not found in ${file_path}. Available: ${available || "(none)"}`
44770
+ }
44771
+ ]
44772
+ };
44773
+ }
44774
+ const chunkLines = lines.slice(match.startLine - 1, match.endLine);
44775
+ const text5 = [
44776
+ `${match.kind} ${match.name} (L${match.startLine}-${match.endLine})`,
44777
+ "---",
44778
+ ...chunkLines
44779
+ ].join("\n");
44780
+ return { content: [{ type: "text", text: text5 }] };
44781
+ }
44782
+ const chunks = buildChunks(parsedSymbols, lines.length);
44703
44783
  if (chunk_index !== void 0) {
44704
44784
  if (chunk_index < 0 || chunk_index >= chunks.length) {
44705
44785
  return {
@@ -44722,7 +44802,9 @@ function registerChunkTool(server) {
44722
44802
  }
44723
44803
  const overview = chunks.map((c, i) => `[${i}] ${c.label} L${c.startLine}-${c.endLine} (${c.endLine - c.startLine + 1} lines)`).join("\n");
44724
44804
  const text4 = `${chunks.length} chunks in ${file_path}:
44725
- ${overview}`;
44805
+ ${overview}
44806
+
44807
+ To get source code, call again with chunk_index=<number> or symbol=<name>.`;
44726
44808
  return { content: [{ type: "text", text: text4 }] };
44727
44809
  } catch (err) {
44728
44810
  const msg = err instanceof Error ? err.message : String(err);
@@ -44730,6 +44812,18 @@ ${overview}`;
44730
44812
  }
44731
44813
  });
44732
44814
  }
44815
+ function findSymbol(symbols2, name) {
44816
+ const lower = name.toLowerCase();
44817
+ for (const sym of symbols2) {
44818
+ if (sym.name.toLowerCase() === lower)
44819
+ return sym;
44820
+ for (const child of sym.children) {
44821
+ if (child.name.toLowerCase() === lower)
44822
+ return child;
44823
+ }
44824
+ }
44825
+ return void 0;
44826
+ }
44733
44827
  var MAX_CHUNK_LINES = 200;
44734
44828
  function buildChunks(symbols2, totalLines) {
44735
44829
  if (symbols2.length === 0) {
@@ -44812,7 +44906,7 @@ function registerDependenciesTool(server) {
44812
44906
  direction: external_exports3.enum(["imports", "imported_by", "both"]).default("both").describe("Which direction to analyze"),
44813
44907
  depth: external_exports3.number().int().min(1).max(3).default(1).describe("Depth of transitive dependencies")
44814
44908
  }, async ({ file_path, direction, depth }) => {
44815
- const cwd = process.cwd();
44909
+ const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
44816
44910
  const absPath = file_path.startsWith("/") ? file_path : join11(cwd, file_path);
44817
44911
  const relPath = file_path.startsWith("/") ? file_path.slice(cwd.length + 1) : file_path;
44818
44912
  try {
@@ -44938,7 +45032,7 @@ function registerImpactTool(server) {
44938
45032
  symbol_name: external_exports3.string().describe("The symbol (function, class, variable) name to find references for"),
44939
45033
  file_path: external_exports3.string().optional().describe("Optional: scope search to usages of this symbol from this file")
44940
45034
  }, async ({ symbol_name, file_path }) => {
44941
- const cwd = process.cwd();
45035
+ const cwd = process.env.ABF_PROJECT_ROOT || process.cwd();
44942
45036
  try {
44943
45037
  const results = await ripgrepSearch({
44944
45038
  query: `\\b${escapeRegex2(symbol_name)}\\b`,