@cortexkit/aft-opencode 0.30.3 → 0.31.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
@@ -13720,6 +13720,36 @@ function formatZoomText(targetLabel, response) {
13720
13720
  return out.join(`
13721
13721
  `);
13722
13722
  }
13723
+ function formatZoomMultiTargetResult(entries) {
13724
+ const rendered = entries.map((entry) => {
13725
+ const { targetLabel, name, response } = entry;
13726
+ if (response.success === false) {
13727
+ const message = typeof response.message === "string" && response.message.length > 0 ? response.message : "zoom failed";
13728
+ return { targetLabel, name, success: false, error: message };
13729
+ }
13730
+ return {
13731
+ targetLabel,
13732
+ name,
13733
+ success: true,
13734
+ content: formatZoomText(targetLabel, response)
13735
+ };
13736
+ });
13737
+ const complete = rendered.every((entry) => entry.success);
13738
+ const sections = [];
13739
+ if (!complete) {
13740
+ sections.push("Incomplete zoom results: one or more symbols failed.");
13741
+ }
13742
+ for (const entry of rendered) {
13743
+ if (entry.success) {
13744
+ sections.push(entry.content ?? "");
13745
+ } else {
13746
+ sections.push(`Symbol "${entry.name}" not found in ${entry.targetLabel}: ${entry.error ?? "zoom failed"}`);
13747
+ }
13748
+ }
13749
+ return { complete, entries: rendered, text: sections.join(`
13750
+
13751
+ `) };
13752
+ }
13723
13753
  // src/bg-notifications.ts
13724
13754
  import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
13725
13755
 
@@ -32186,6 +32216,7 @@ var optionalInt = (min, max) => z2.number().int().min(min).max(max).optional();
32186
32216
  var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
32187
32217
  callers: 60000,
32188
32218
  trace_to: 60000,
32219
+ trace_to_symbol: 60000,
32189
32220
  trace_data: 60000,
32190
32221
  impact: 60000,
32191
32222
  grep: 60000,
@@ -32195,6 +32226,68 @@ var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
32195
32226
  function timeoutForCommand(command) {
32196
32227
  return LONG_RUNNING_COMMAND_TIMEOUT_MS[command];
32197
32228
  }
32229
+ function asPlainObject(value) {
32230
+ if (!value || typeof value !== "object" || Array.isArray(value))
32231
+ return;
32232
+ return value;
32233
+ }
32234
+ function candidateLocation(candidate) {
32235
+ const file2 = typeof candidate.file === "string" && candidate.file.length > 0 ? candidate.file : undefined;
32236
+ if (!file2)
32237
+ return;
32238
+ const line = typeof candidate.line === "number" && Number.isFinite(candidate.line) ? candidate.line : undefined;
32239
+ return line === undefined ? file2 : `${file2}:${line}`;
32240
+ }
32241
+ function stringifyData(data) {
32242
+ if (data === undefined)
32243
+ return;
32244
+ try {
32245
+ return JSON.stringify(data, null, 2);
32246
+ } catch {
32247
+ return String(data);
32248
+ }
32249
+ }
32250
+ function formatBridgeErrorMessage(command, response, params = {}) {
32251
+ const code = typeof response.code === "string" && response.code.length > 0 ? response.code : undefined;
32252
+ const message = typeof response.message === "string" && response.message.length > 0 ? response.message : `${command} failed`;
32253
+ const data = asPlainObject(response.data);
32254
+ const rawCandidates = Array.isArray(response.candidates) ? response.candidates : Array.isArray(data?.candidates) ? data.candidates : undefined;
32255
+ const rawSymbol = typeof response.symbol === "string" && response.symbol.length > 0 ? response.symbol : typeof data?.symbol === "string" && data.symbol.length > 0 ? data.symbol : undefined;
32256
+ if (code === "ambiguous_target" || code === "target_symbol_not_in_file") {
32257
+ const candidates = (rawCandidates ?? []).map(asPlainObject).filter((candidate) => candidate !== undefined).map(candidateLocation).filter((candidate) => candidate !== undefined);
32258
+ if (candidates.length > 0) {
32259
+ const symbol2 = typeof params.toSymbol === "string" && params.toSymbol.length > 0 ? params.toSymbol : rawSymbol;
32260
+ const target = symbol2 ? `multiple symbols named "${symbol2}"` : message.replace(/[.!?]+$/, "");
32261
+ const action = code === "ambiguous_target" ? "Pass toFile to disambiguate" : "Try one of these files for toFile";
32262
+ return `${command}: ${code} — ${target}. ${action}:
32263
+ ${candidates.map((candidate) => ` - ${candidate}`).join(`
32264
+ `)}`;
32265
+ }
32266
+ }
32267
+ if (!code)
32268
+ return message;
32269
+ const lines = [`${command}: ${code} — ${message}`];
32270
+ const extras = collectStructuredExtras(response);
32271
+ if (extras)
32272
+ lines.push(`data: ${extras}`);
32273
+ return lines.join(`
32274
+ `);
32275
+ }
32276
+ function collectStructuredExtras(response) {
32277
+ const reserved = new Set(["id", "success", "code", "message", "data"]);
32278
+ const extras = {};
32279
+ for (const [key, value] of Object.entries(response)) {
32280
+ if (reserved.has(key))
32281
+ continue;
32282
+ extras[key] = value;
32283
+ }
32284
+ if (Object.keys(extras).length === 0) {
32285
+ return stringifyData(response.data);
32286
+ }
32287
+ if (response.data !== undefined)
32288
+ extras.data = response.data;
32289
+ return stringifyData(extras);
32290
+ }
32198
32291
  function canonicalizeDirectory(dir) {
32199
32292
  const trimmed = dir.replace(/[/\\]+$/, "");
32200
32293
  try {
@@ -32424,9 +32517,7 @@ function astTools(ctx) {
32424
32517
  ` + `IMPORTANT: Patterns must be complete AST nodes (valid code fragments).
32425
32518
  ` + `For functions, include params and body: 'export async function $NAME($$$) { $$$ }' not just 'export async function $NAME'.
32426
32519
 
32427
- ` + `Examples: pattern='console.log($MSG)' lang='typescript', pattern='async function $NAME($$$) { $$$ }' lang='javascript', pattern='def $FUNC($$$): $$$' lang='python'
32428
-
32429
- ` + "Returns: Text summary — 'Found N match(es) across M file(s)' followed by file:line blocks with matched text and captured meta-variables.",
32520
+ ` + "Examples: pattern='console.log($MSG)' lang='typescript', pattern='async function $NAME($$$) { $$$ }' lang='javascript', pattern='def $FUNC($$$): $$$' lang='python'",
32430
32521
  args: {
32431
32522
  pattern: z3.string().describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."),
32432
32523
  lang: z3.enum(SUPPORTED_LANGS).describe("Target language"),
@@ -32518,9 +32609,7 @@ ${hint}`;
32518
32609
 
32519
32610
  ` + `Example: pattern='console.log($MSG)' rewrite='logger.info($MSG)' lang='typescript' — replaces all console.log calls with logger.info across TypeScript files.
32520
32611
 
32521
- ` + `**Warning: This tool modifies files directly.** Use dryRun=true to preview. Consider creating an aft_safety checkpoint before bulk replacements.
32522
-
32523
- ` + "Returns: Text summary — 'Replaced N match(es) across M file(s)' (or '[DRY RUN] Would replace...') followed by per-file output. In dry-run mode, each file is shown with its unified diff so you can verify the rewrite before applying (e.g. catch literal $$$ from anonymous-variadic typos). Diff preview is capped at 8KB total; remaining files are summarized.",
32612
+ ` + "**Warning: This tool modifies files directly.** Use dryRun=true to preview (shows per-file unified diff, capped at 8KB). Consider creating an aft_safety checkpoint before bulk replacements.",
32524
32613
  args: {
32525
32614
  pattern: z3.string().describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."),
32526
32615
  rewrite: z3.string().describe("Replacement pattern (can use $VAR from pattern)"),
@@ -33045,7 +33134,7 @@ var FOREGROUND_WAIT_WINDOW_MS = 5000;
33045
33134
  var FOREGROUND_POLL_INTERVAL_MS = 100;
33046
33135
  var BASH_TRANSPORT_TIMEOUT_MS = 30000;
33047
33136
  var DEFAULT_HARD_TIMEOUT_MS = 30 * 60 * 1000;
33048
- var BASH_DESCRIPTION = `Hoisted bash tool with output compression, command rewriting to AFT tools, optional background execution, and PTY mode for interactive programs. By default, output is compressed; pass compressed: false for raw output. Pass background: true to spawn in the background and get a task_id for bash_status/bash_kill. Pass pty: true with background: true for interactive REPLs and drive them with bash_status({ outputMode: "screen" }) plus bash_write. Use bash_watch to block on or register for pattern matches and exit events.`;
33137
+ var BASH_DESCRIPTION = `Hoisted bash tool with output compression, command rewriting to AFT tools, optional background execution, and PTY mode for interactive programs. By default, output is compressed; pass compressed: false for raw output. Pass background: true to spawn in the background and get a taskId for bash_status/bash_kill. Pass pty: true for interactive REPLs and drive them with bash_status({ outputMode: "screen" }) plus bash_write (pty implies background automatically). Use bash_watch to block on or register for pattern matches and exit events.`;
33049
33138
  async function withPermissionLoop(ctx, runtime, params, bridgeCall, options) {
33050
33139
  const first = await bridgeCall(ctx, runtime, "bash", params, options);
33051
33140
  if (first.success !== false || first.code !== "permission_required")
@@ -33076,7 +33165,7 @@ function createBashTool(ctx) {
33076
33165
  timeout: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Hard kill cap in milliseconds (positive integer). When omitted, the task can run up to 30 minutes. Foreground bash returns inline if the command finishes within ~5s; otherwise it's automatically promoted to background and a completion reminder is delivered when the task actually finishes."),
33077
33166
  workdir: z4.string().optional().describe("Working directory for command execution. Relative paths resolve through the bridge; defaults to the current tool context/project root when omitted."),
33078
33167
  description: z4.string().optional().describe("Short 5-10 word human-readable summary shown in OpenCode UI metadata instead of raw shell syntax."),
33079
- background: z4.boolean().optional().describe("When true, spawn the command in the background and return a task_id for bash_status/bash_kill instead of waiting for completion. Defaults to false."),
33168
+ background: z4.boolean().optional().describe("When true, spawn the command in the background and return a taskId for bash_status/bash_kill instead of waiting for completion. Defaults to false."),
33080
33169
  compressed: z4.boolean().optional().describe("When true or omitted, return compressed output with noisy terminal control sequences reduced. Set to false for raw output."),
33081
33170
  pty: z4.boolean().optional().describe('When true, spawn the command in a real PTY for interactive programs (python/node/bash REPLs, vim). Implies background: true automatically. Unavailable in subagent sessions. Inspect with bash_status({ taskId, outputMode: "screen" }) and drive interactively with bash_write — its input accepts either a string OR an array like [ "iHello", { key: "esc" }, ":wq", { key: "enter" } ] for atomic text+key sequences.'),
33082
33171
  ptyRows: optionalInt(1, 60).describe("PTY terminal height in rows — ignored when pty is false. Defaults to 24 when pty: true. Minimum 1, maximum 60."),
@@ -33230,7 +33319,7 @@ function createBashTool(ctx) {
33230
33319
  }
33231
33320
  function createBashStatusTool(ctx) {
33232
33321
  return {
33233
- description: "Read-only snapshot of a background or PTY bash task's current state and output. Returns immediately. Use bash_watch to block on or register for pattern matches and exit events.",
33322
+ description: "Read-only snapshot of a background or PTY bash task's current state and output. Returns immediately. Never waits. Use bash_watch to block on or register for pattern matches and exit events.",
33234
33323
  args: {
33235
33324
  taskId: z4.string().describe("Background task ID returned by bash({ background: true }), e.g. bash-6b454047."),
33236
33325
  outputMode: z4.enum(["screen", "raw", "both"]).optional().describe("PTY output rendering mode. Defaults to screen for PTY tasks and preserves existing behavior for piped tasks when omitted.")
@@ -34224,7 +34313,7 @@ function createEditTool(ctx, writeToolName = "write") {
34224
34313
  replaceAll: z7.boolean().optional().describe("Replace all occurrences"),
34225
34314
  occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER).describe("0-indexed occurrence to replace when multiple matches exist"),
34226
34315
  symbol: z7.string().optional().describe("Named symbol to replace (function, class, type)"),
34227
- content: z7.string().optional().describe("New content for symbol replace or file write"),
34316
+ content: z7.string().optional().describe("Replacement content for symbol mode or operations[].command='write'. For whole-file writes, use the `write` tool."),
34228
34317
  appendContent: z7.string().optional().describe("Text to append to the end of filePath; creates the file if needed"),
34229
34318
  edits: z7.array(z7.record(z7.string(), z7.unknown())).optional().describe("Batch edits — array of { oldString: string, newString: string } or { startLine: number (1-based), endLine: number (1-based, inclusive), content: string }"),
34230
34319
  operations: z7.array(z7.record(z7.string(), z7.unknown())).optional().describe("Transaction — array of { file: string, command: 'edit_match' | 'write', match?: string, replacement?: string, content?: string } for multi-file edits with rollback. Note: uses 'file'/'match'/'replacement' (not filePath/oldString/newString)")
@@ -34428,7 +34517,7 @@ Example patch:
34428
34517
  \`\`\`
34429
34518
 
34430
34519
  **Behavior:**
34431
- - All file changes are applied with checkpoint-based rollback if any file fails, previous changes are rolled back (best-effort)
34520
+ - Per-file commit: each file's edits apply independently. If a later file fails, earlier successful changes are kept. A pre-patch checkpoint is created automatically — use \`aft_safety\` undo if you need to revert.
34432
34521
  - Files are backed up before modification
34433
34522
  - Parent directories are created automatically for new files
34434
34523
  - Fuzzy matching for context anchors (handles whitespace and Unicode differences)
@@ -34710,11 +34799,7 @@ ${fileList}`;
34710
34799
  }
34711
34800
  var DELETE_DESCRIPTION = `Delete one or more files (or directories) with backup.
34712
34801
 
34713
- ` + "Each file is backed up before deletion — use aft_safety undo to recover any of them. " + `For directories, every file inside is individually backed up before the tree is removed.
34714
-
34715
- Directory deletion requires recursive: true. Without it, passing a directory returns an error.
34716
-
34717
- Returns: { success, complete, deleted: [paths], skipped_files: [{file, reason}] }. Partial success is allowed: files that can be deleted are deleted; files that fail (missing, permission denied, etc.) are reported in skipped_files. \`complete: false\` indicates at least one file was skipped.`;
34802
+ ` + "Each file is backed up before deletion — use aft_safety undo to recover any of them. " + "For directories, every file inside is individually backed up before the tree is removed.\n\nDirectory deletion requires recursive: true. Without it, passing a directory returns an error.\n\nPartial success is allowed: deletable files are deleted; failed ones are reported in `skipped_files` with `complete: false`.";
34718
34803
  function createDeleteTool(ctx) {
34719
34804
  return {
34720
34805
  description: DELETE_DESCRIPTION,
@@ -34767,14 +34852,13 @@ function createDeleteTool(ctx) {
34767
34852
  }
34768
34853
  };
34769
34854
  }
34770
- var MOVE_DESCRIPTION = `Move or rename a file with backup. Creates parent directories for destination automatically
34771
- Note: This moves/renames files at the OS level.`;
34855
+ var MOVE_DESCRIPTION = "Move or rename a file with backup. Creates parent directories for destination automatically\nNote: This moves/renames files at the OS level. To move a code symbol (function, class, type) between files while updating imports, use `aft_refactor` op='move' instead.";
34772
34856
  function createMoveTool(ctx) {
34773
34857
  return {
34774
34858
  description: MOVE_DESCRIPTION,
34775
34859
  args: {
34776
- filePath: z7.string().describe("Source file path to move"),
34777
- destination: z7.string().describe("Destination file path")
34860
+ filePath: z7.string().describe("Source file path to move (absolute or relative to project root)"),
34861
+ destination: z7.string().describe("Destination file path (absolute or relative to project root)")
34778
34862
  },
34779
34863
  execute: async (args, context) => {
34780
34864
  const filePath = path4.isAbsolute(args.filePath) ? args.filePath : path4.resolve(context.directory, args.filePath);
@@ -34894,12 +34978,7 @@ function importTools(ctx) {
34894
34978
  ` + `Ops:
34895
34979
  ` + `- 'add': Add an import. Auto-detects group (stdlib/external/internal), deduplicates. Requires 'module'. Optional 'names', 'defaultImport', 'typeOnly'.
34896
34980
  ` + `- 'remove': Remove an import or a specific named import. Requires 'module'. Provide 'removeName' to remove a single named import; omit to remove the entire import.
34897
- ` + `- 'organize': Re-sort and re-group all imports by language convention, deduplicate. Requires only 'filePath'. Use aft_safety checkpoint/undo for recovery before broad cleanup.
34898
-
34899
- ` + `Returns:
34900
- ` + `- add: { file, added, module, group?, already_present?, formatted?, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }
34901
- ` + `- remove: { file, removed, module, name?, formatted, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }
34902
- ` + "- organize: { file, groups: [{ name, count }], removed_duplicates, formatted?, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }",
34981
+ ` + "- 'organize': Re-sort and re-group all imports by language convention, deduplicate. Requires only 'filePath'. Use aft_safety checkpoint/undo for recovery before broad cleanup.",
34903
34982
  args: {
34904
34983
  op: z8.enum(["add", "remove", "organize"]).describe("Import operation"),
34905
34984
  filePath: z8.string().describe("Path to the file (absolute or relative to project root)"),
@@ -34957,29 +35036,13 @@ import { tool as tool9 } from "@opencode-ai/plugin";
34957
35036
  var z9 = tool9.schema;
34958
35037
  function lspTools(ctx) {
34959
35038
  const diagnosticsTool = {
34960
- description: "On-demand LSP file/scope check. Spawns the relevant language server (if " + "registered for the file's extension), opens the document, prefers LSP 3.17 " + "pull diagnostics when supported, and falls back to push + waitMs otherwise. " + "NOT a project-wide type checker — for full coverage run `tsc --noEmit`, " + "`cargo check`, `pyright`, etc.\n" + `
34961
- ` + `Returns: {
34962
- ` + ` diagnostics: Array<{file, line, column, end_line, end_column, severity, message, code, source}>,
34963
- ` + ` total: number,
34964
- ` + ` files_with_errors: number,
34965
- ` + ` complete: boolean, // true = trustable absence; false = partial
34966
- ` + ` lsp_servers_used: Array<{ // honest per-server status
34967
- ` + ` server_id, scope: 'file'|'workspace', status: 'pull_ok'|'pull_unchanged'|'push_only'|'no_root_marker (...)' |
34968
- ` + ` 'binary_not_installed: <name>'|'spawn_failed: ...'|'pull_failed: ...'|'workspace_pull_unsupported'|...
34969
- ` + ` }>,
34970
- ` + ` unchecked_files?: string[], // directory mode only — files we have no info for
34971
- ` + ` walk_truncated?: boolean, // directory walk hit the 200-file cap
34972
- ` + ` note?: string // present when no LSP server is registered for the file's extension
34973
- ` + `}
34974
- ` + `
34975
- ` + `**Reading the response honestly:**
34976
- ` + "- `total: 0, complete: true, lsp_servers_used: [{status: 'pull_ok'}]` → file is genuinely clean.\n" + "- `total: 0, lsp_servers_used: []` → **nothing was checked** (no server registered for this extension). Tell the user, don't claim 'no errors'.\n" + "- `lsp_servers_used: [{status: 'binary_not_installed: <name>'}]` → server matched the extension but its binary isn't on PATH. Tell the user to install it.\n" + "- `lsp_servers_used: [{status: 'no_root_marker (...)'}]` → server is registered but couldn't find a workspace root marker walking up from this file. The user's project layout doesn't match what the server expects.\n" + "- `complete: false` (directory mode) → some files in the directory weren't checked; see `unchecked_files`.\n" + `
34977
- ` + "**When this tool gives an unhelpful answer**, run `npx @cortexkit/aft doctor lsp <filePath>` from a terminal to get a full per-server breakdown (registered servers, binary resolution, root-marker resolution, spawn outcome).",
35039
+ description: "On-demand LSP file/scope check. NOT a project-wide type checker — use `tsc --noEmit`, `cargo check`, `pyright` etc. for full coverage.\n" + `
35040
+ ` + "Honesty: `total: 0` is only clean when `complete: true` AND `lsp_servers_used[].status` includes `pull_ok`. Empty `lsp_servers_used`, or any `binary_not_installed`/`spawn_failed`/`no_root_marker`/`push_only` without diagnostics means the file wasn't actually checked — say so, don't report 'clean'. For per-server breakdown run `npx @cortexkit/aft doctor lsp <filePath>`.",
34978
35041
  args: {
34979
35042
  filePath: z9.string().optional().describe("Path to a file to check. Mutually exclusive with 'directory'. Omit both to dump all cached diagnostics."),
34980
- directory: z9.string().optional().describe("Path to a directory. Returns cached diagnostics + workspace pull from active servers; lists files we have no info for in 'unchecked_files'. Capped at 200 walked files."),
35043
+ directory: z9.string().optional().describe("Path to a directory. Returns cached diagnostics + workspace pull; capped at 200 walked files."),
34981
35044
  severity: z9.enum(["error", "warning", "information", "hint", "all"]).optional().describe("Filter by severity (default: 'all')."),
34982
- waitMs: optionalInt(1, 1e4).describe("Wait up to N ms (max 10000, default 0) for push diagnostics to arrive. Only matters for servers that don't support LSP 3.17 pull (bash-language-server, yaml-language-server). Use after an edit to let the server re-analyze.")
35045
+ waitMs: optionalInt(1, 1e4).describe("Wait up to N ms (max 10000) for push diagnostics. Push-only servers like bash-language-server and yaml-language-server use after an edit.")
34983
35046
  },
34984
35047
  execute: async (args, context) => {
34985
35048
  const filePath = args.filePath || undefined;
@@ -35021,18 +35084,21 @@ function navigationTools(ctx) {
35021
35084
  ` + `- 'call_tree': See what a function calls (forward traversal). Use to understand dependencies before modifying a function.
35022
35085
  ` + `- 'callers': Find all call sites of a symbol. Use before renaming or changing a function's signature.
35023
35086
  ` + `- 'trace_to': Trace how execution reaches a function from entry points (routes, exports, main). Use to understand context around deeply-nested code.
35087
+ ` + `- 'trace_to_symbol': Find the shortest call path from one symbol to another symbol across the codebase. Requires 'toSymbol'. If multiple targets match, the error returns candidate files; retry with 'toFile' to disambiguate.
35024
35088
  ` + `- 'impact': Analyze what breaks if a symbol changes. Returns affected callers with signatures and entry point status.
35025
35089
  ` + `- 'trace_data': Follow a value through variable assignments and function parameters across files. Requires 'symbol' (scope to trace from) and 'expression'.
35026
35090
 
35027
- ` + `All ops require both 'filePath' and 'symbol'. The 'expression' parameter is additionally required for trace_data.
35091
+ ` + `All ops require both 'filePath' and 'symbol'. The 'expression' parameter is additionally required for trace_data; 'toSymbol' is additionally required for trace_to_symbol.
35028
35092
 
35029
35093
  `,
35030
35094
  args: {
35031
- op: z10.enum(["call_tree", "callers", "trace_to", "impact", "trace_data"]).describe("Navigation operation"),
35095
+ op: z10.enum(["call_tree", "callers", "trace_to", "trace_to_symbol", "impact", "trace_data"]).describe("Navigation operation"),
35032
35096
  filePath: z10.string().describe("Path to the source file containing the symbol (absolute or relative to project root)"),
35033
35097
  symbol: z10.string().describe("Name of the symbol to analyze"),
35034
- depth: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Max traversal depth (default: call_tree=5, callers=1, trace_to=10, impact=5, trace_data=5)"),
35035
- expression: z10.string().optional().describe("Expression to track through data flow (required for trace_data op)")
35098
+ depth: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Max traversal depth (default: call_tree=5, callers=1, trace_to=10, trace_to_symbol=10 capped at 16, impact=5, trace_data=5)"),
35099
+ expression: z10.string().optional().describe("Expression to track through data flow (required for trace_data op)"),
35100
+ toSymbol: z10.string().optional().describe("Target symbol name for trace_to_symbol; the returned path ends at this symbol"),
35101
+ toFile: z10.string().optional().describe("Optional target file for trace_to_symbol; required when toSymbol exists in multiple files")
35036
35102
  },
35037
35103
  execute: async (args, context) => {
35038
35104
  const params = {
@@ -35043,12 +35109,19 @@ function navigationTools(ctx) {
35043
35109
  params.depth = Number(args.depth);
35044
35110
  if (args.expression !== undefined)
35045
35111
  params.expression = args.expression;
35112
+ if (args.toSymbol !== undefined)
35113
+ params.toSymbol = args.toSymbol;
35114
+ if (args.toFile !== undefined)
35115
+ params.toFile = args.toFile;
35046
35116
  if (args.op === "trace_data" && typeof args.expression !== "string") {
35047
35117
  throw new Error("'expression' is required for 'trace_data' op");
35048
35118
  }
35119
+ if (args.op === "trace_to_symbol" && typeof args.toSymbol !== "string") {
35120
+ throw new Error("'toSymbol' is required for 'trace_to_symbol' op");
35121
+ }
35049
35122
  const response = await callBridge(ctx, context, args.op, params);
35050
35123
  if (response.success === false) {
35051
- throw new Error(response.message || `${args.op} failed`);
35124
+ throw new Error(formatBridgeErrorMessage(args.op, response, params));
35052
35125
  }
35053
35126
  return JSON.stringify(response);
35054
35127
  }
@@ -35057,25 +35130,83 @@ function navigationTools(ctx) {
35057
35130
  }
35058
35131
 
35059
35132
  // src/tools/reading.ts
35060
- import { resolve as resolve8 } from "node:path";
35133
+ import { dirname as dirname11, resolve as resolve8 } from "node:path";
35061
35134
  import { tool as tool11 } from "@opencode-ai/plugin";
35062
35135
  var z11 = tool11.schema;
35136
+ function getCallID3(ctx) {
35137
+ const c = ctx;
35138
+ return c.callID ?? c.callId ?? c.call_id;
35139
+ }
35140
+ function buildZoomTitle(args) {
35141
+ if (args.targets !== undefined && args.targets !== null) {
35142
+ if (Array.isArray(args.targets)) {
35143
+ if (args.targets.length === 1 && args.targets[0]) {
35144
+ return `${args.targets[0].filePath}#${args.targets[0].symbol}`;
35145
+ }
35146
+ return `${args.targets.length} targets across files`;
35147
+ }
35148
+ return `${args.targets.filePath}#${args.targets.symbol}`;
35149
+ }
35150
+ const path5 = args.filePath ?? args.url ?? "";
35151
+ if (typeof args.symbols === "string")
35152
+ return path5 ? `${path5}#${args.symbols}` : args.symbols;
35153
+ if (Array.isArray(args.symbols) && args.symbols.length > 0) {
35154
+ if (args.symbols.length === 1)
35155
+ return path5 ? `${path5}#${args.symbols[0]}` : args.symbols[0];
35156
+ return path5 ? `${path5} (${args.symbols.length} symbols)` : `${args.symbols.length} symbols`;
35157
+ }
35158
+ return path5 || "(no target)";
35159
+ }
35063
35160
  function readingTools(ctx) {
35064
35161
  return {
35065
35162
  aft_outline: {
35066
- description: `Structural outline of source code, documentation files, or remote URLs. For code, returns symbols (functions, classes, types) with line ranges. For Markdown and HTML, returns heading hierarchy. Use this to explore structure before reading specific sections with aft_zoom.
35067
-
35068
- ` + "Pass a single `target`:\n" + ` • file path → outline that file (with signatures)
35163
+ description: "Structural outline of source code, documentation files, or remote URLs. For code, returns symbols (functions, classes, types) with line ranges. For Markdown and HTML, returns heading hierarchy. Use this to explore structure before reading specific sections with aft_zoom. Set `files: true` with a directory target for a flat indexed file tree with language, symbol count, and byte metadata.\n\n" + "Pass a single `target`:\n" + ` • file path → outline that file (with signatures)
35069
35164
  ` + ` • directory path → outline all source files under it (recursively, up to 200 files)
35070
35165
  ` + ` • URL (http:// or https://) → fetch and outline a remote HTML/Markdown document
35071
- ` + " • array of paths → outline multiple files in one call",
35166
+ ` + " • array of paths → outline multiple files in one call; with files:true, every path must be a directory",
35072
35167
  args: {
35073
- target: z11.union([z11.string(), z11.array(z11.string())]).describe("What to outline: a file path, directory path, URL, or array of file paths. The mode is auto-detected: URLs by `http://`/`https://` prefix, directories by stat, arrays as multi-file.")
35168
+ target: z11.union([z11.string(), z11.array(z11.string())]).describe("What to outline: a file path, directory path, URL, or array of paths. The mode is auto-detected: URLs by `http://`/`https://` prefix, directories by stat, arrays as multi-file."),
35169
+ files: z11.boolean().optional().describe("Directory-only mode: when true, target must be a directory or array of directories and the result is a flat file tree with path, language, symbol count, and byte size instead of a symbol outline.")
35074
35170
  },
35075
35171
  execute: async (args, context) => {
35076
35172
  const target = args.target;
35173
+ const filesMode = args.files === true;
35077
35174
  const hasUrl = typeof target === "string" && (target.startsWith("http://") || target.startsWith("https://"));
35078
35175
  const isArray = Array.isArray(target) && target.length > 0;
35176
+ if (filesMode) {
35177
+ if (Array.isArray(target)) {
35178
+ if (target.length === 0) {
35179
+ throw new Error("'target' must be a non-empty string or array of strings");
35180
+ }
35181
+ const permissionDenied2 = await assertOutlineFilesExternalPermissions(context, target);
35182
+ if (permissionDenied2)
35183
+ return permissionDeniedResponse(permissionDenied2);
35184
+ const response3 = await callBridge(ctx, context, "outline", { target, files: true });
35185
+ if (response3.success === false) {
35186
+ throw new Error(response3.message || "outline failed");
35187
+ }
35188
+ return formatOutlineFilesText(response3);
35189
+ }
35190
+ if (typeof target !== "string" || target.length === 0) {
35191
+ throw new Error("'target' must be a non-empty string or array of strings");
35192
+ }
35193
+ const resolvedPath = resolve8(context.directory, target);
35194
+ const permissionDenied = await assertOutlineFilesExternalPermissions(context, resolvedPath);
35195
+ if (permissionDenied)
35196
+ return permissionDeniedResponse(permissionDenied);
35197
+ let isDirectory2 = false;
35198
+ try {
35199
+ const { stat } = await import("node:fs/promises");
35200
+ const st = await stat(resolvedPath);
35201
+ isDirectory2 = st.isDirectory();
35202
+ } catch {}
35203
+ const params = isDirectory2 ? { directory: resolvedPath, files: true } : { file: target, files: true };
35204
+ const response2 = await callBridge(ctx, context, "outline", params);
35205
+ if (response2.success === false) {
35206
+ throw new Error(response2.message || "outline failed");
35207
+ }
35208
+ return formatOutlineFilesText(response2);
35209
+ }
35079
35210
  if (hasUrl) {
35080
35211
  const response2 = await callBridge(ctx, context, "outline", { file: target });
35081
35212
  if (response2.success === false) {
@@ -35118,29 +35249,76 @@ function readingTools(ctx) {
35118
35249
  }
35119
35250
  },
35120
35251
  aft_zoom: {
35121
- description: `Inspect code symbols or documentation sections. For code, returns the full source of a symbol with call-graph annotations (what it calls and what calls it). For Markdown and HTML, returns the section content under the given heading.
35122
-
35123
- Provide exactly ONE of 'filePath' or 'url'. Pass either 'symbol' for a single lookup or 'symbols' for multiple in one call.`,
35252
+ description: "Inspect code symbols or documentation sections. For code, returns the full source of a symbol with call-graph annotations (what it calls and what calls it). For Markdown and HTML, returns the section content under the given heading.\n\nUse exactly ONE mode: `{ filePath, symbols }`, `{ url, symbols }`, or `{ targets }`. `symbols` can be a string or array (one or many lookups in the same file/URL). Use `targets` for cross-file batches: `{ filePath, symbol }` or an array of them.",
35124
35253
  args: {
35125
35254
  filePath: z11.string().optional().describe("Path to file (absolute or relative to project root)"),
35126
35255
  url: z11.string().optional().describe("HTTP/HTTPS URL of an HTML or Markdown document to fetch and zoom into"),
35127
- symbol: z11.string().optional().describe("Symbol name for code, or heading text for Markdown/HTML"),
35128
- symbols: z11.array(z11.string()).optional().describe("Array of symbol names or heading texts for a single batched call"),
35256
+ symbols: z11.union([z11.string(), z11.array(z11.string())]).optional().describe("Symbol name for code, or heading text for Markdown/HTML. Pass a string for one lookup or an array for batched lookups in the same file/URL."),
35257
+ targets: z11.union([
35258
+ z11.object({
35259
+ filePath: z11.string().describe("Path to file (absolute or relative to project root)"),
35260
+ symbol: z11.string().describe("Symbol name in that file")
35261
+ }),
35262
+ z11.array(z11.object({
35263
+ filePath: z11.string().describe("Path to file (absolute or relative to project root)"),
35264
+ symbol: z11.string().describe("Symbol name in that file")
35265
+ }))
35266
+ ]).optional().describe("Cross-file batch: `{ filePath, symbol }` or an array of them. Mutually exclusive with filePath/url/symbols."),
35129
35267
  contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Lines of context before/after the symbol (default: 3)")
35130
35268
  },
35131
35269
  execute: async (args, context) => {
35132
35270
  const hasFilePath = typeof args.filePath === "string" && args.filePath.length > 0;
35133
35271
  const hasUrl = typeof args.url === "string" && args.url.length > 0;
35272
+ const hasTargets = args.targets !== undefined && args.targets !== null;
35273
+ const hasSymbols = args.symbols !== undefined && args.symbols !== null && (typeof args.symbols === "string" ? args.symbols.length > 0 : Array.isArray(args.symbols) && args.symbols.length > 0);
35274
+ const zoomCallID = getCallID3(context);
35275
+ if (zoomCallID) {
35276
+ const title = buildZoomTitle(args);
35277
+ storeToolMetadata(context.sessionID, zoomCallID, { title, metadata: { title } });
35278
+ }
35279
+ if (hasTargets) {
35280
+ if (hasFilePath || hasUrl || hasSymbols) {
35281
+ throw new Error("'targets' is mutually exclusive with 'filePath', 'url', and 'symbols'");
35282
+ }
35283
+ const targets = Array.isArray(args.targets) ? args.targets : [args.targets];
35284
+ if (targets.length === 0) {
35285
+ throw new Error("'targets' must be a non-empty object or array");
35286
+ }
35287
+ for (const [i, entry] of targets.entries()) {
35288
+ if (!entry || typeof entry.filePath !== "string" || entry.filePath.length === 0) {
35289
+ throw new Error(`targets[${i}].filePath must be a non-empty string`);
35290
+ }
35291
+ if (typeof entry.symbol !== "string" || entry.symbol.length === 0) {
35292
+ throw new Error(`targets[${i}].symbol must be a non-empty string`);
35293
+ }
35294
+ }
35295
+ const responses = await Promise.all(targets.map((t) => {
35296
+ const params2 = { file: t.filePath, symbol: t.symbol };
35297
+ if (args.contextLines !== undefined)
35298
+ params2.context_lines = args.contextLines;
35299
+ return callBridge(ctx, context, "zoom", params2).catch((err) => ({
35300
+ success: false,
35301
+ message: err instanceof Error ? err.message : String(err)
35302
+ }));
35303
+ }));
35304
+ const entries = targets.map((t, i) => ({
35305
+ targetLabel: t.filePath,
35306
+ name: t.symbol,
35307
+ response: responses[i] ?? { success: false, message: "missing zoom response" }
35308
+ }));
35309
+ return formatZoomMultiTargetResult(entries).text;
35310
+ }
35134
35311
  if (!hasFilePath && !hasUrl) {
35135
- throw new Error("Provide exactly one of 'filePath' or 'url'");
35312
+ throw new Error("Provide exactly one of 'filePath', 'url', or 'targets'");
35136
35313
  }
35137
35314
  if (hasFilePath && hasUrl) {
35138
35315
  throw new Error("Provide exactly ONE of 'filePath' or 'url' — not both");
35139
35316
  }
35140
35317
  const file2 = hasUrl ? args.url : args.filePath;
35141
35318
  const targetLabel = (hasUrl ? args.url : args.filePath) ?? file2;
35142
- if (Array.isArray(args.symbols) && args.symbols.length > 0) {
35143
- const results = await Promise.all(args.symbols.map((sym) => {
35319
+ const symbolsArray = hasSymbols ? typeof args.symbols === "string" ? [args.symbols] : args.symbols : undefined;
35320
+ if (symbolsArray) {
35321
+ const results = await Promise.all(symbolsArray.map((sym) => {
35144
35322
  const params2 = { file: file2, symbol: sym };
35145
35323
  if (args.contextLines !== undefined)
35146
35324
  params2.context_lines = args.contextLines;
@@ -35149,11 +35327,16 @@ Provide exactly ONE of 'filePath' or 'url'. Pass either 'symbol' for a single lo
35149
35327
  message: err instanceof Error ? err.message : String(err)
35150
35328
  }));
35151
35329
  }));
35152
- return formatZoomBatchResult(targetLabel, args.symbols, results).text;
35330
+ if (symbolsArray.length === 1) {
35331
+ const response = results[0] ?? { success: false, message: "missing zoom response" };
35332
+ if (response.success === false) {
35333
+ throw new Error(response.message || "zoom failed");
35334
+ }
35335
+ return formatZoomText(targetLabel, response);
35336
+ }
35337
+ return formatZoomBatchResult(targetLabel, symbolsArray, results).text;
35153
35338
  }
35154
35339
  const params = { file: file2 };
35155
- if (typeof args.symbol === "string")
35156
- params.symbol = args.symbol;
35157
35340
  if (args.contextLines !== undefined)
35158
35341
  params.context_lines = args.contextLines;
35159
35342
  const data = await callBridge(ctx, context, "zoom", params);
@@ -35190,6 +35373,24 @@ function formatZoomBatchResult(targetLabel, symbols, responses) {
35190
35373
 
35191
35374
  `) };
35192
35375
  }
35376
+ var MAX_UNCHECKED_FILES_IN_FOOTER = 10;
35377
+ async function assertOutlineFilesExternalPermissions(context, target) {
35378
+ const targets = Array.isArray(target) ? target : [target];
35379
+ const checkedParents = new Set;
35380
+ for (const rawTarget of targets) {
35381
+ if (typeof rawTarget !== "string" || rawTarget.length === 0)
35382
+ continue;
35383
+ const resolvedPath = resolve8(context.directory, rawTarget);
35384
+ const parentDir = dirname11(resolvedPath);
35385
+ if (checkedParents.has(parentDir))
35386
+ continue;
35387
+ checkedParents.add(parentDir);
35388
+ const denial = await assertExternalDirectoryPermission(context, resolvedPath);
35389
+ if (denial)
35390
+ return denial;
35391
+ }
35392
+ return;
35393
+ }
35193
35394
  function formatOutlineText(response) {
35194
35395
  const text = response.text ?? "";
35195
35396
  const skipped = response.skipped_files;
@@ -35204,6 +35405,36 @@ function formatOutlineText(response) {
35204
35405
  return `${header}Skipped ${skipped.length} file(s):
35205
35406
  ${lines}`;
35206
35407
  }
35408
+ function formatOutlineFilesText(response) {
35409
+ const text = formatOutlineText(response);
35410
+ const uncheckedFiles = Array.isArray(response.unchecked_files) ? response.unchecked_files.filter((file2) => typeof file2 === "string" && file2.length > 0) : [];
35411
+ const isPartial = response.complete === false || response.walk_truncated === true || uncheckedFiles.length > 0;
35412
+ if (!isPartial) {
35413
+ return text;
35414
+ }
35415
+ const footer = [];
35416
+ if (response.walk_truncated === true) {
35417
+ const uncheckedCount = uncheckedFiles.length;
35418
+ const suffix = uncheckedCount > 0 ? ` ${uncheckedCount} additional files in this directory were not indexed.` : " Some files in this directory were not indexed.";
35419
+ footer.push(`⚠ Partial result: walk truncated at 200 files.${suffix}`);
35420
+ } else {
35421
+ const suffix = uncheckedFiles.length > 0 ? ` ${uncheckedFiles.length} files in this directory were not indexed.` : " Some files in this directory were not indexed.";
35422
+ footer.push(`⚠ Partial result:${suffix}`);
35423
+ }
35424
+ if (uncheckedFiles.length > 0) {
35425
+ footer.push("Unchecked files:");
35426
+ footer.push(...uncheckedFiles.slice(0, MAX_UNCHECKED_FILES_IN_FOOTER).map((file2) => ` ${file2}`));
35427
+ const remaining = uncheckedFiles.length - MAX_UNCHECKED_FILES_IN_FOOTER;
35428
+ if (remaining > 0) {
35429
+ footer.push(` ... +${remaining} more`);
35430
+ }
35431
+ }
35432
+ const header = text.length > 0 ? `${text}
35433
+
35434
+ ` : "";
35435
+ return `${header}${footer.join(`
35436
+ `)}`;
35437
+ }
35207
35438
 
35208
35439
  // src/tools/refactoring.ts
35209
35440
  import { tool as tool12 } from "@opencode-ai/plugin";
@@ -35279,9 +35510,7 @@ function refactoringTools(ctx) {
35279
35510
 
35280
35511
  ` + `Each op requires specific parameters — see parameter descriptions for requirements.
35281
35512
 
35282
- ` + `All ops need 'filePath'. Use aft_safety checkpoint/undo before risky refactors.
35283
-
35284
- ` + "Returns: move { ok, files_modified, consumers_updated, checkpoint_name, results }. extract returns { file, name, parameters, return_type, syntax_valid, formatted, ... }. inline returns { file, symbol, call_context, substitutions, conflicts, syntax_valid, formatted, ... }.",
35513
+ ` + "All ops need 'filePath'. Use aft_safety checkpoint/undo before risky refactors.",
35285
35514
  args: {
35286
35515
  op: z12.enum(["move", "extract", "inline"]).describe("Refactoring operation"),
35287
35516
  filePath: z12.string().describe("Path to the source file (absolute or relative to project root)"),
@@ -35390,9 +35619,7 @@ function safetyTools(ctx) {
35390
35619
 
35391
35620
  ` + `Each op requires specific parameters — see parameter descriptions for requirements.
35392
35621
 
35393
- ` + `Use checkpoint before risky multi-file changes. Use undo for quick single-file rollback.
35394
-
35395
- ` + "Returns: undo { path, backup_id }. history { file, entries }. checkpoint { ok, name }. restore { ok, name }. list { checkpoints }.",
35622
+ ` + "Use checkpoint before risky multi-file changes. Use undo for quick single-file rollback.",
35396
35623
  args: {
35397
35624
  op: z13.enum(["undo", "history", "checkpoint", "restore", "list"]).describe("Safety operation"),
35398
35625
  filePath: z13.string().optional().describe("File path (required for history, optional for undo). Absolute or relative to project root"),
@@ -35474,7 +35701,18 @@ function safetyTools(ctx) {
35474
35701
 
35475
35702
  // src/tools/search.ts
35476
35703
  import * as fs7 from "node:fs";
35704
+ import * as os2 from "node:os";
35477
35705
  import * as path6 from "node:path";
35706
+ function expandTilde(input) {
35707
+ if (!input || !input.startsWith("~"))
35708
+ return input;
35709
+ if (input === "~")
35710
+ return os2.homedir();
35711
+ if (input.startsWith("~/") || input.startsWith(`~${path6.sep}`)) {
35712
+ return path6.resolve(os2.homedir(), input.slice(2));
35713
+ }
35714
+ return input;
35715
+ }
35478
35716
  function arg(schema) {
35479
35717
  return schema;
35480
35718
  }
@@ -35506,7 +35744,8 @@ function normalizeGlob(pattern) {
35506
35744
  return pattern;
35507
35745
  }
35508
35746
  function absoluteSearchPath(context, target) {
35509
- return path6.isAbsolute(target) ? target : path6.resolve(context.directory, target);
35747
+ const expanded = expandTilde(target);
35748
+ return path6.isAbsolute(expanded) ? expanded : path6.resolve(context.directory, expanded);
35510
35749
  }
35511
35750
  function searchPathExists(context, target) {
35512
35751
  return fs7.existsSync(absoluteSearchPath(context, target));
@@ -35570,16 +35809,16 @@ function splitIncludeArg(raw) {
35570
35809
  }
35571
35810
  function searchTools(ctx) {
35572
35811
  const grepTool = {
35573
- description: "Search file contents using regular expressions. Returns matching lines with file paths, line numbers, and context.",
35812
+ description: "Search file contents using regular expressions. Returns matching lines with file paths and line numbers (no surrounding context lines — use `read` for that). Always case-sensitive. Capped at 100 matches; if you hit the cap, narrow with `path` or `include` and re-run.",
35574
35813
  args: {
35575
35814
  pattern: arg(exports_external.string().describe("Regular expression pattern to search for")),
35576
35815
  include: arg(exports_external.string().optional().describe("File pattern to include (e.g. '*.ts', '*.{ts,tsx}')")),
35577
- path: arg(exports_external.string().optional().describe("Directory to search in, relative to project root"))
35816
+ path: arg(exports_external.string().optional().describe("Directory to search (absolute or relative to project root)"))
35578
35817
  },
35579
35818
  execute: async (args, context) => {
35580
35819
  const pattern = String(args.pattern);
35581
35820
  const includeArg = args.include ? String(args.include) : undefined;
35582
- const pathArg = args.path ? String(args.path) : undefined;
35821
+ const pathArg = args.path ? expandTilde(String(args.path)) : undefined;
35583
35822
  const grepDenied = await askGrepPermission(context, pattern, {
35584
35823
  path: pathArg,
35585
35824
  include: includeArg
@@ -35612,11 +35851,11 @@ function searchTools(ctx) {
35612
35851
  description: "Find files matching a glob pattern. Returns matching file paths sorted by modification time.",
35613
35852
  args: {
35614
35853
  pattern: arg(exports_external.string().describe("Glob pattern to match (e.g. '**/*.ts', 'src/**/*.test.*')")),
35615
- path: arg(exports_external.string().optional().describe("Directory to search in, relative to project root"))
35854
+ path: arg(exports_external.string().optional().describe("Directory to search (absolute or relative to project root)"))
35616
35855
  },
35617
35856
  execute: async (args, context) => {
35618
- let globPattern = String(args.pattern);
35619
- let globPath = args.path ? String(args.path) : undefined;
35857
+ let globPattern = expandTilde(String(args.pattern));
35858
+ let globPath = args.path ? expandTilde(String(args.path)) : undefined;
35620
35859
  if (!globPath && globPattern.startsWith("/")) {
35621
35860
  const metaIdx = globPattern.search(/[*?{}[\]]/);
35622
35861
  if (metaIdx > 0) {
@@ -35735,14 +35974,7 @@ function structureTools(ctx) {
35735
35974
  ` + `- 'add_decorator': Add Python decorator to function/class. Requires 'target' and 'decorator' (without @). Optional 'position'.
35736
35975
  ` + `- 'add_struct_tags': Add/update Go struct field tags. Requires 'target' (struct name), 'field', 'tag', 'value'.
35737
35976
 
35738
- ` + `Each op requires specific parameters — see parameter descriptions for requirements. Use aft_safety checkpoint/undo before risky transforms.
35739
-
35740
- ` + `Returns:
35741
- ` + `- add_member: { file, scope, position, formatted, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }
35742
- ` + `- add_derive: { file, target, derives, formatted, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }
35743
- ` + `- wrap_try_catch: { file, target, formatted, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }
35744
- ` + `- add_decorator: { file, target, decorator, formatted, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }
35745
- ` + "- add_struct_tags: { file, target, field, tag_string, formatted, syntax_valid?, format_skipped_reason?, validation_errors?, validate_skipped_reason?, backup_id?, lsp_diagnostics? }",
35977
+ ` + "Each op requires specific parameters — see parameter descriptions for requirements. Use aft_safety checkpoint/undo before risky transforms.",
35746
35978
  args: {
35747
35979
  op: z15.enum(["add_member", "add_derive", "wrap_try_catch", "add_decorator", "add_struct_tags"]).describe("Transformation operation"),
35748
35980
  filePath: z15.string().describe("Path to the source file (absolute or relative to project root)"),
@@ -35851,7 +36083,7 @@ function buildWorkflowHints(opts) {
35851
36083
  const hasBash = !opts.disabledTools.has(bashName);
35852
36084
  const hasBgBash = hasBash && opts.bashBackgroundEnabled && !opts.disabledTools.has(bashStatusName);
35853
36085
  if (hasOutline && hasZoom) {
35854
- sections.push(`**Web/URL access**: \`aft_outline({ url })\` first for structure, then \`aft_zoom({ url, symbol: "<heading>" })\` for the specific section.`);
36086
+ sections.push(`**Web/URL access**: \`aft_outline({ target: url })\` first for structure, then \`aft_zoom({ url, symbols: "<heading>" })\` for the specific section.`);
35855
36087
  }
35856
36088
  if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
35857
36089
  const locator = hasGrep ? `\`${grepName}\`` : "`aft_search`";
@@ -35863,6 +36095,7 @@ function buildWorkflowHints(opts) {
35863
36095
  "- `callers` — find all call sites before changing a function signature",
35864
36096
  "- `impact` — blast radius (which functions/files will need updates)",
35865
36097
  "- `trace_to` — how execution reaches this code from entry points (routes, exports, main)",
36098
+ "- `trace_to_symbol` — shortest call path from one symbol to another",
35866
36099
  "- `trace_data` — follow a value through assignments and parameters across files"
35867
36100
  ].join(`
35868
36101
  `));
@@ -35975,13 +36208,13 @@ var PLUGIN_VERSION = (() => {
35975
36208
  return "0.0.0";
35976
36209
  }
35977
36210
  })();
35978
- var ANNOUNCEMENT_VERSION = "0.30.0";
36211
+ var ANNOUNCEMENT_VERSION = "0.31.0";
35979
36212
  var ANNOUNCEMENT_FEATURES = [
35980
- 'PTY support for interactive bash. `bash({pty: true, background: true})` spawns commands inside a real terminal runs Python/Node REPLs, vim, htop, less, and nested TUIs. Use `ptyRows`/`ptyCols` (up to 60×140) to size the terminal, `bash_status({outputMode: "screen"})` for the rendered view, and `bash_write` to send keystrokes.',
35981
- "New `bash_watch` tool unifies pattern notifications and sync waits. `bash_watch({taskId, pattern?, timeoutMs?})` blocks inline; `bash_watch({taskId, pattern, background: true})` registers an async watcher that fires `[BG BASH NOTIFY]` on match or task exit and suppresses the default completion reminder.",
35982
- "`bash_status` is now a pure snapshot tool wait and watch semantics moved to `bash_watch`.",
35983
- "URL fetches in `aft_outline` and `aft_zoom` no longer hang indefinitely on slow servers body reads abort with a clear stall error after 15 seconds without data.",
35984
- "Post-restart bg-bash replay is fixed: previously-delivered task completions no longer fire fresh reminders after OpenCode restarts."
36213
+ "`aft_navigate` has a new `trace_to_symbol` op for path-between-symbols queries. One call returns the shortest call path from one function to another with file + line for every hop. Honest errors for ambiguous, missing, or unreachable targets including a candidate list to disambiguate.",
36214
+ '`aft_outline target: "<dir>", files: true` returns an indexed file tree with language, symbol count, and byte size per file. Lets agents pick which files to actually open without first reading symbol bodies. Honest truncation (`complete: false` + `walk_truncated`/`unchecked_files`) when the directory exceeds the walk cap.',
36215
+ '`aft_zoom` adds `targets` for cross-file batchespull bodies from different files in one call. Schema breaking change: `symbol` is removed; pass `symbols: "name"` (string) or `symbols: ["a", "b"]` (array). Same shape works for `url` mode so you can grab multiple sections from one URL fetch.',
36216
+ "Bun/npm/pnpm test compressors now match on output shape, not just on the head token. Wrapper invocations like `bun run --cwd packages/foo test`, `npm test`, and `pnpm test` keep failing-test bodies on the first run instead of dropping them to a generic summary.",
36217
+ "Tool descriptions trimmed: ~1.2K agent-facing tokens removed (-19% on OpenCode, -2% on Pi) by dropping redundant `Returns:` blocks and compressing `lsp_diagnostics` while preserving its load-bearing honesty guidance."
35985
36218
  ];
35986
36219
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
35987
36220
  var plugin = async (input) => initializePluginForDirectory(input);
@@ -67,6 +67,8 @@ export declare function coerceOptionalInt(v: unknown, paramName: string, min: nu
67
67
  export declare const LONG_RUNNING_COMMAND_TIMEOUT_MS: Record<string, number>;
68
68
  /** Returns the per-command timeout override, or undefined to use the bridge default. */
69
69
  export declare function timeoutForCommand(command: string): number | undefined;
70
+ /** Format bridge failure envelopes without dropping structured error data. */
71
+ export declare function formatBridgeErrorMessage(command: string, response: Record<string, unknown>, params?: Record<string, unknown>): string;
70
72
  /**
71
73
  * Minimum shape of the per-tool-call context provided by the OpenCode SDK.
72
74
  *
@@ -1 +1 @@
1
- {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../src/tools/_shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAQhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,GACR,CAAC;AAEhD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,OAAO,EACV,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQlE,CAAC;AAEF,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAW3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,YAAY,CAEhF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA0BlC;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAE9F"}
1
+ {"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../src/tools/_shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAQhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,GACR,CAAC;AAEhD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,OAAO,EACV,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CASlE,CAAC;AAEF,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErE;AA2BD,8EAA8E;AAC9E,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,MAAM,CAsDR;AAsBD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAW3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,YAAY,CAEhF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA0BlC;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAE9F"}
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkDjD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA4S3E"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkDjD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA0S3E"}
@@ -1 +1 @@
1
- {"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyEjD,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,MAChD,CAAC;AAqRtC;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAkKjE;AAkmCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA0B/E;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA8FnF"}
1
+ {"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyEjD,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,MAChD,CAAC;AAqRtC;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAkKjE;AAwmCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA0B/E;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA8FnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../src/tools/imports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYjD;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAiF9E"}
1
+ {"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../src/tools/imports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYjD;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA6E9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../../src/tools/lsp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA+E3E"}
1
+ {"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../../src/tools/lsp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAsD3E"}
@@ -1,7 +1,7 @@
1
1
  import type { ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { PluginContext } from "../types.js";
3
3
  /**
4
- * Tool definitions for navigation commands: configure, call_tree, callers, trace_to, impact, and trace_data.
4
+ * Tool definitions for navigation commands: configure, call_tree, callers, trace_to, trace_to_symbol, impact, and trace_data.
5
5
  */
6
6
  export declare function navigationTools(ctx: PluginContext): Record<string, ToolDefinition>;
7
7
  //# sourceMappingURL=navigation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/tools/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAkDlF"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/tools/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAoElF"}
@@ -23,5 +23,6 @@ export declare function readingTools(ctx: PluginContext): Record<string, ToolDef
23
23
  * Exported for regression tests.
24
24
  */
25
25
  export declare function formatZoomBatchResult(targetLabel: string, symbols: string[], responses: Record<string, unknown>[]): ZoomBatchResult;
26
+ export declare function formatOutlineFilesText(response: Record<string, unknown>): string;
26
27
  export {};
27
28
  //# sourceMappingURL=reading.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reading.d.ts","sourceRoot":"","sources":["../../src/tools/reading.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,UAAU,qBAAqB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAmJ/E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GACnC,eAAe,CAyBjB"}
1
+ {"version":3,"file":"reading.d.ts","sourceRoot":"","sources":["../../src/tools/reading.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAsCjD,UAAU,qBAAqB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAmS/E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GACnC,eAAe,CAyBjB;AA+CD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA2ChF"}
@@ -1 +1 @@
1
- {"version":3,"file":"refactoring.d.ts","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAcjD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA0InF"}
1
+ {"version":3,"file":"refactoring.d.ts","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAcjD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAyInF"}
@@ -1 +1 @@
1
- {"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAajD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAsH9E"}
1
+ {"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAajD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAqH9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgHjD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BrD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA6H9E"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiIjD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BrD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAuI9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/tools/structure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYjD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAkKjF"}
1
+ {"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/tools/structure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYjD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA4JjF"}
package/dist/tui.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/tui/index.tsx
2
2
  import { createMemo as createMemo2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
3
3
  // package.json
4
- var version = "0.30.3";
4
+ var version = "0.31.0";
5
5
 
6
6
  // src/shared/rpc-client.ts
7
7
  import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-hints.d.ts","sourceRoot":"","sources":["../src/workflow-hints.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,WAAW,EAAE,SAAS,GAAG,aAAa,GAAG,KAAK,CAAC;IAC/C,4EAA4E;IAC5E,aAAa,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,eAAe,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4DAA4D;IAC5D,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAuEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAQjG"}
1
+ {"version":3,"file":"workflow-hints.d.ts","sourceRoot":"","sources":["../src/workflow-hints.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,WAAW,EAAE,SAAS,GAAG,aAAa,GAAG,KAAK,CAAC;IAC/C,4EAA4E;IAC5E,aAAa,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,eAAe,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4DAA4D;IAC5D,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAwEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAQjG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/aft-opencode",
3
- "version": "0.30.3",
3
+ "version": "0.31.0",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
6
6
  "main": "dist/index.js",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@clack/prompts": "^1.2.0",
32
- "@cortexkit/aft-bridge": "0.30.3",
32
+ "@cortexkit/aft-bridge": "0.31.0",
33
33
  "@opencode-ai/plugin": "^1.15.5",
34
34
  "@opencode-ai/sdk": "^1.15.5",
35
35
  "@xterm/headless": "^5.5.0",
@@ -38,12 +38,12 @@
38
38
  "zod": "^4.1.8"
39
39
  },
40
40
  "optionalDependencies": {
41
- "@cortexkit/aft-darwin-arm64": "0.30.3",
42
- "@cortexkit/aft-darwin-x64": "0.30.3",
43
- "@cortexkit/aft-linux-arm64": "0.30.3",
44
- "@cortexkit/aft-linux-x64": "0.30.3",
45
- "@cortexkit/aft-win32-arm64": "0.30.3",
46
- "@cortexkit/aft-win32-x64": "0.30.3"
41
+ "@cortexkit/aft-darwin-arm64": "0.31.0",
42
+ "@cortexkit/aft-darwin-x64": "0.31.0",
43
+ "@cortexkit/aft-linux-arm64": "0.31.0",
44
+ "@cortexkit/aft-linux-x64": "0.31.0",
45
+ "@cortexkit/aft-win32-arm64": "0.31.0",
46
+ "@cortexkit/aft-win32-x64": "0.31.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^22.0.0",