opencode-codebase-index 0.18.0 → 0.18.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.
package/dist/index.js CHANGED
@@ -13119,11 +13119,12 @@ function getConfigPaths(projectRoot, host, options) {
13119
13119
  ].map((configPath) => pathNormalize(configPath));
13120
13120
  }
13121
13121
 
13122
- // src/tools/index.ts
13123
- import { tool as tool2 } from "@opencode-ai/plugin";
13124
-
13125
- // src/tools/pr-impact.ts
13126
- import { tool } from "@opencode-ai/plugin";
13122
+ // node_modules/@opencode-ai/plugin/dist/tool.js
13123
+ import { z } from "zod";
13124
+ function tool(input) {
13125
+ return input;
13126
+ }
13127
+ tool.schema = z;
13127
13128
 
13128
13129
  // src/tools/format-pr-impact.ts
13129
13130
  function formatPrImpact(result) {
@@ -13177,16 +13178,16 @@ function formatPrImpact(result) {
13177
13178
  }
13178
13179
 
13179
13180
  // src/tools/pr-impact.ts
13180
- var z = tool.schema;
13181
+ var z2 = tool.schema;
13181
13182
  var pr_impact = tool({
13182
13183
  description: "Analyze the impact of a pull request or branch by examining changed files, affected symbols, transitive callers, communities touched, hub nodes, and risk level. Use to understand blast radius before merging.",
13183
13184
  args: {
13184
- pr: z.number().optional().describe("Pull request number to analyze"),
13185
- branch: z.string().optional().describe("Branch name to analyze (defaults to current branch)"),
13186
- maxDepth: z.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
13187
- hubThreshold: z.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
13188
- checkConflicts: z.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
13189
- direction: z.enum(["callers", "callees", "both"]).optional().default("both").describe("Call-graph traversal direction: 'callers' for upstream, 'callees' for downstream, 'both' for union (default: both)")
13185
+ pr: z2.number().optional().describe("Pull request number to analyze"),
13186
+ branch: z2.string().optional().describe("Branch name to analyze (defaults to current branch)"),
13187
+ maxDepth: z2.number().optional().default(5).describe("Maximum traversal depth for transitive callers (default: 5)"),
13188
+ hubThreshold: z2.number().optional().default(10).describe("Minimum caller count to flag a symbol as a hub node (default: 10)"),
13189
+ checkConflicts: z2.boolean().optional().default(false).describe("Check for conflicting open PRs touching the same communities (default: false)"),
13190
+ direction: z2.enum(["callers", "callees", "both"]).optional().default("both").describe("Call-graph traversal direction: 'callers' for upstream, 'callees' for downstream, 'both' for union (default: both)")
13190
13191
  },
13191
13192
  async execute(args, context) {
13192
13193
  const indexer = getIndexerForProject2(context?.worktree);
@@ -14163,7 +14164,7 @@ function transformForVisualization(symbols, edges, options = {}) {
14163
14164
  }
14164
14165
 
14165
14166
  // src/tools/index.ts
14166
- var z2 = tool2.schema;
14167
+ var z3 = tool.schema;
14167
14168
  var DEFAULT_HOST = "opencode";
14168
14169
  var CHUNK_TYPE_VALUES = [
14169
14170
  "function",
@@ -14185,34 +14186,34 @@ function initializeTools2(projectRoot, config) {
14185
14186
  function getIndexerForProject2(directory) {
14186
14187
  return getIndexerForProject(directory, DEFAULT_HOST);
14187
14188
  }
14188
- var codebase_context = tool2({
14189
+ var codebase_context = tool({
14189
14190
  description: "PREFERRED FIRST TOOL for repository questions. Returns a deduplicated, file-diverse evidence pack within tokenBudget. Provide from and to for dependency paths, symbol for definitions, or only query for conceptual discovery.",
14190
14191
  args: {
14191
- query: z2.string().describe("The repository question or behavior to locate"),
14192
- from: z2.string().nullable().optional().describe("Source symbol for a dependency path"),
14193
- to: z2.string().nullable().optional().describe("Target symbol for a dependency path"),
14194
- symbol: z2.string().nullable().optional().describe("Exact symbol for authoritative definition lookup"),
14195
- limit: z2.number().int().min(MIN_CONTEXT_RESULT_LIMIT).max(MAX_CONTEXT_RESULT_LIMIT).nullable().optional().default(10).describe(`Maximum number of results (${MIN_CONTEXT_RESULT_LIMIT}-${MAX_CONTEXT_RESULT_LIMIT})`),
14196
- maxDepth: z2.number().int().min(MIN_CONTEXT_PATH_DEPTH).max(MAX_CONTEXT_PATH_DEPTH).nullable().optional().default(10).describe(`Maximum call-path traversal depth (${MIN_CONTEXT_PATH_DEPTH}-${MAX_CONTEXT_PATH_DEPTH})`),
14197
- fileType: z2.string().nullable().optional().describe("Filter by file extension"),
14198
- directory: z2.string().nullable().optional().describe("Filter by directory path"),
14199
- tokenBudget: z2.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).nullable().optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET).describe(`Maximum response tokens (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`)
14192
+ query: z3.string().describe("The repository question or behavior to locate"),
14193
+ from: z3.string().nullable().optional().describe("Source symbol for a dependency path"),
14194
+ to: z3.string().nullable().optional().describe("Target symbol for a dependency path"),
14195
+ symbol: z3.string().nullable().optional().describe("Exact symbol for authoritative definition lookup"),
14196
+ limit: z3.number().int().min(MIN_CONTEXT_RESULT_LIMIT).max(MAX_CONTEXT_RESULT_LIMIT).nullable().optional().default(10).describe(`Maximum number of results (${MIN_CONTEXT_RESULT_LIMIT}-${MAX_CONTEXT_RESULT_LIMIT})`),
14197
+ maxDepth: z3.number().int().min(MIN_CONTEXT_PATH_DEPTH).max(MAX_CONTEXT_PATH_DEPTH).nullable().optional().default(10).describe(`Maximum call-path traversal depth (${MIN_CONTEXT_PATH_DEPTH}-${MAX_CONTEXT_PATH_DEPTH})`),
14198
+ fileType: z3.string().nullable().optional().describe("Filter by file extension"),
14199
+ directory: z3.string().nullable().optional().describe("Filter by directory path"),
14200
+ tokenBudget: z3.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).nullable().optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET).describe(`Maximum response tokens (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`)
14200
14201
  },
14201
14202
  async execute(args, context) {
14202
14203
  return (await resolveCodebaseContext(context?.worktree, DEFAULT_HOST, args)).text;
14203
14204
  }
14204
14205
  });
14205
- var codebase_peek = tool2({
14206
+ var codebase_peek = tool({
14206
14207
  description: "Quick lookup of code locations by meaning. Returns only metadata (file, line, name, type) WITHOUT code content. Use this first to find WHERE code is, then use Read tool to examine specific files. Saves tokens by not returning full code blocks. Best for: discovery, navigation, finding multiple related locations.",
14207
14208
  args: {
14208
- query: z2.string().describe("Natural language description of what code you're looking for."),
14209
- limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
14210
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14211
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14212
- chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14213
- blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
14214
- blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
14215
- blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
14209
+ query: z3.string().describe("Natural language description of what code you're looking for."),
14210
+ limit: z3.number().optional().default(10).describe("Maximum number of results to return"),
14211
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14212
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14213
+ chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14214
+ blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
14215
+ blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
14216
+ blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
14216
14217
  },
14217
14218
  async execute(args, context) {
14218
14219
  const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
@@ -14228,12 +14229,12 @@ var codebase_peek = tool2({
14228
14229
  return formatCodebasePeek(results);
14229
14230
  }
14230
14231
  });
14231
- var index_codebase = tool2({
14232
+ var index_codebase = tool({
14232
14233
  description: "Index the codebase for semantic search. Creates vector embeddings of code chunks. Incremental - only re-indexes changed files (~50ms when nothing changed). Run before first codebase_search.",
14233
14234
  args: {
14234
- force: z2.boolean().optional().default(false).describe("Force reindex even if already indexed"),
14235
- estimateOnly: z2.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
14236
- verbose: z2.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
14235
+ force: z3.boolean().optional().default(false).describe("Force reindex even if already indexed"),
14236
+ estimateOnly: z3.boolean().optional().default(false).describe("Only show cost estimate without indexing"),
14237
+ verbose: z3.boolean().optional().default(false).describe("Show detailed info about skipped files and parsing failures")
14237
14238
  },
14238
14239
  async execute(args, context) {
14239
14240
  const result = await runIndexCodebase(context?.worktree, DEFAULT_HOST, args, (title, metadata) => {
@@ -14244,14 +14245,14 @@ var index_codebase = tool2({
14244
14245
  return formatIndexStats(result.stats, args.verbose ?? false);
14245
14246
  }
14246
14247
  });
14247
- var index_status = tool2({
14248
+ var index_status = tool({
14248
14249
  description: "Check the status of the codebase index. Shows whether the codebase is indexed, how many chunks are stored, and the embedding provider being used.",
14249
14250
  args: {},
14250
14251
  async execute(_args, context) {
14251
14252
  return formatStatus(await getIndexStatus(context?.worktree, DEFAULT_HOST));
14252
14253
  }
14253
14254
  });
14254
- var index_health_check = tool2({
14255
+ var index_health_check = tool({
14255
14256
  description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
14256
14257
  args: {},
14257
14258
  async execute(_args, context) {
@@ -14260,33 +14261,33 @@ var index_health_check = tool2({
14260
14261
  return formatHealthCheck(result.health);
14261
14262
  }
14262
14263
  });
14263
- var index_metrics = tool2({
14264
+ var index_metrics = tool({
14264
14265
  description: "Get metrics and performance statistics for the codebase index. Shows indexing stats, search timings, cache hit rates, and API usage. Requires debug.enabled=true and debug.metrics=true in config.",
14265
14266
  args: {},
14266
14267
  async execute(_args, context) {
14267
14268
  return (await getIndexMetrics(context?.worktree, DEFAULT_HOST)).text;
14268
14269
  }
14269
14270
  });
14270
- var index_logs = tool2({
14271
+ var index_logs = tool({
14271
14272
  description: "Get recent debug logs from the codebase indexer. Shows timestamped log entries with level and category. Requires debug.enabled=true in config.",
14272
14273
  args: {
14273
- limit: z2.number().optional().default(20).describe("Maximum number of log entries to return"),
14274
- category: z2.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
14275
- level: z2.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
14274
+ limit: z3.number().optional().default(20).describe("Maximum number of log entries to return"),
14275
+ category: z3.enum(["search", "embedding", "cache", "gc", "branch", "general"]).optional().describe("Filter by log category"),
14276
+ level: z3.enum(["error", "warn", "info", "debug"]).optional().describe("Filter by minimum log level")
14276
14277
  },
14277
14278
  async execute(args, context) {
14278
14279
  return (await getIndexLogs(context?.worktree, DEFAULT_HOST, args)).text;
14279
14280
  }
14280
14281
  });
14281
- var find_similar = tool2({
14282
+ var find_similar = tool({
14282
14283
  description: "Find code similar to a given snippet. Use for duplicate detection, pattern discovery, or refactoring prep. Paste code and find semantically similar implementations elsewhere in the codebase.",
14283
14284
  args: {
14284
- code: z2.string().describe("The code snippet to find similar code for"),
14285
- limit: z2.number().optional().default(10).describe("Maximum number of results to return"),
14286
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14287
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14288
- chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14289
- excludeFile: z2.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
14285
+ code: z3.string().describe("The code snippet to find similar code for"),
14286
+ limit: z3.number().optional().default(10).describe("Maximum number of results to return"),
14287
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14288
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14289
+ chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14290
+ excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
14290
14291
  },
14291
14292
  async execute(args, context) {
14292
14293
  const results = await findSimilarCode(context?.worktree, DEFAULT_HOST, args.code, {
@@ -14302,18 +14303,18 @@ var find_similar = tool2({
14302
14303
  return formatSearchResults(results);
14303
14304
  }
14304
14305
  });
14305
- var codebase_search = tool2({
14306
+ var codebase_search = tool({
14306
14307
  description: "Search codebase by MEANING, not keywords. Returns full code content. Use when you need to see actual implementation. For just finding WHERE code is (saves ~90% tokens), use codebase_peek instead. For known identifiers like 'validateToken', use grep - it's faster.",
14307
14308
  args: {
14308
- query: z2.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
14309
- limit: z2.number().optional().default(5).describe("Maximum number of results to return"),
14310
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14311
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14312
- chunkType: z2.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14313
- contextLines: z2.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
14314
- blameAuthor: z2.string().optional().describe("Filter by git blame author name or email"),
14315
- blameSha: z2.string().optional().describe("Filter by git blame commit SHA or prefix"),
14316
- blameSince: z2.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
14309
+ query: z3.string().describe("Natural language description of what code you're looking for. Describe behavior, not syntax."),
14310
+ limit: z3.number().optional().default(5).describe("Maximum number of results to return"),
14311
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
14312
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
14313
+ chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
14314
+ contextLines: z3.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
14315
+ blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
14316
+ blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
14317
+ blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
14317
14318
  },
14318
14319
  async execute(args, context) {
14319
14320
  const results = await searchCodebase(context?.worktree, DEFAULT_HOST, args.query, {
@@ -14332,13 +14333,13 @@ var codebase_search = tool2({
14332
14333
  return formatSearchResults(results, "score");
14333
14334
  }
14334
14335
  });
14335
- var implementation_lookup = tool2({
14336
+ var implementation_lookup = tool({
14336
14337
  description: "Jump to symbol definition. Find WHERE something is defined. Returns the authoritative source location(s) for a function, class, method, type, or variable. Prefers real implementation files over tests, docs, examples, and fixtures. Use when you need the definition site, not all usages.",
14337
14338
  args: {
14338
- query: z2.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
14339
- limit: z2.number().optional().default(5).describe("Maximum number of results"),
14340
- fileType: z2.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
14341
- directory: z2.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
14339
+ query: z3.string().describe("Symbol name or natural language description (e.g., 'validateToken', 'where is the payment handler defined')"),
14340
+ limit: z3.number().optional().default(5).describe("Maximum number of results"),
14341
+ fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py')"),
14342
+ directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils')")
14342
14343
  },
14343
14344
  async execute(args, context) {
14344
14345
  const results = await implementationLookup(context?.worktree, DEFAULT_HOST, args.query, {
@@ -14349,13 +14350,13 @@ var implementation_lookup = tool2({
14349
14350
  return formatDefinitionLookup(results, args.query);
14350
14351
  }
14351
14352
  });
14352
- var call_graph = tool2({
14353
+ var call_graph = tool({
14353
14354
  description: "Query the call graph to find callers or callees of a function/method. Use to understand code flow and dependencies between functions. Supports relationship types: Call, MethodCall, Constructor, Import, Inherits, Implements.",
14354
14355
  args: {
14355
- name: z2.string().describe("Function or method name to query"),
14356
- direction: z2.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
14357
- symbolId: z2.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
14358
- relationshipType: z2.enum(RELATIONSHIP_TYPE_VALUES).optional().describe("Filter by relationship type. Omit to show all.")
14356
+ name: z3.string().describe("Function or method name to query"),
14357
+ direction: z3.enum(["callers", "callees"]).default("callers").describe("Direction: 'callers' finds who calls this function, 'callees' finds what this function calls"),
14358
+ symbolId: z3.string().optional().describe("Symbol ID (required for 'callees' direction, returned by previous call_graph queries)"),
14359
+ relationshipType: z3.enum(RELATIONSHIP_TYPE_VALUES).optional().describe("Filter by relationship type. Omit to show all.")
14359
14360
  },
14360
14361
  async execute(args, context) {
14361
14362
  if (args.direction === "callees") {
@@ -14369,49 +14370,49 @@ var call_graph = tool2({
14369
14370
  return formatCallGraphCallers(args.name, callers, args.relationshipType);
14370
14371
  }
14371
14372
  });
14372
- var call_graph_path = tool2({
14373
+ var call_graph_path = tool({
14373
14374
  description: "Find the shortest connection path between two symbols in the call graph. Given a source and target function/method name, returns the chain of calls connecting them.",
14374
14375
  args: {
14375
- from: z2.string().describe("Source function/method name (starting point)"),
14376
- to: z2.string().describe("Target function/method name (destination)"),
14377
- maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
14376
+ from: z3.string().describe("Source function/method name (starting point)"),
14377
+ to: z3.string().describe("Target function/method name (destination)"),
14378
+ maxDepth: z3.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
14378
14379
  },
14379
14380
  async execute(args, context) {
14380
14381
  const path24 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
14381
14382
  return formatCallGraphPath(args.from, args.to, path24);
14382
14383
  }
14383
14384
  });
14384
- var add_knowledge_base = tool2({
14385
+ var add_knowledge_base = tool({
14385
14386
  description: "Add a folder as a knowledge base to the semantic search index. The folder will be indexed alongside the main project code. Supports absolute paths or relative paths (relative to the project root).",
14386
14387
  args: {
14387
- path: z2.string().describe("Path to the folder to add as a knowledge base (absolute or relative to the project root)")
14388
+ path: z3.string().describe("Path to the folder to add as a knowledge base (absolute or relative to the project root)")
14388
14389
  },
14389
14390
  async execute(args, context) {
14390
14391
  return addKnowledgeBase(context?.worktree, DEFAULT_HOST, args.path);
14391
14392
  }
14392
14393
  });
14393
- var list_knowledge_bases = tool2({
14394
+ var list_knowledge_bases = tool({
14394
14395
  description: "List all configured knowledge base folders that are indexed alongside the main project.",
14395
14396
  args: {},
14396
14397
  async execute(_args, context) {
14397
14398
  return listKnowledgeBases(context?.worktree, DEFAULT_HOST);
14398
14399
  }
14399
14400
  });
14400
- var remove_knowledge_base = tool2({
14401
+ var remove_knowledge_base = tool({
14401
14402
  description: "Remove a knowledge base folder from the semantic search index.",
14402
14403
  args: {
14403
- path: z2.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
14404
+ path: z3.string().describe("Path of the knowledge base to remove (must match the configured path exactly)")
14404
14405
  },
14405
14406
  async execute(args, context) {
14406
14407
  return removeKnowledgeBase(context?.worktree, DEFAULT_HOST, args.path.trim());
14407
14408
  }
14408
14409
  });
14409
- var index_visualize = tool2({
14410
+ var index_visualize = tool({
14410
14411
  description: "Generate an interactive HTML visualization of recent code movement and the call graph. Starts with temporal onboarding context from Git history, then supports module, symbol, hotspot, and cycle drill-down.",
14411
14412
  args: {
14412
- directory: z2.string().optional().describe("Filter to symbols in this directory (e.g., 'src/services')"),
14413
- maxNodes: z2.number().optional().default(5e3).describe("Maximum nodes to include (default 5000)"),
14414
- includeOrphans: z2.boolean().optional().default(false).describe("Include symbols with no call relationships")
14413
+ directory: z3.string().optional().describe("Filter to symbols in this directory (e.g., 'src/services')"),
14414
+ maxNodes: z3.number().optional().default(5e3).describe("Maximum nodes to include (default 5000)"),
14415
+ includeOrphans: z3.boolean().optional().default(false).describe("Include symbols with no call relationships")
14415
14416
  },
14416
14417
  async execute(args, context) {
14417
14418
  const projectRoot = context?.worktree ?? process.cwd();