openfox 2.0.38 → 2.0.39

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.
@@ -16,6 +16,8 @@ allowedTools:
16
16
  - load_skill
17
17
  - dev_server
18
18
  - background_process
19
+ - trace_code
20
+ - mcp_config
19
21
  ---
20
22
 
21
23
  # Build Mode
@@ -9,6 +9,7 @@ allowedTools:
9
9
  - run_command
10
10
  - web_fetch
11
11
  - session_metadata
12
+ - trace_code
12
13
  ---
13
14
 
14
15
  You are a code reviewer. Review the **git diff** of the modified files rather than reading the full files.
@@ -8,6 +8,7 @@ allowedTools:
8
8
  - read_file
9
9
  - run_command
10
10
  - web_fetch
11
+ - trace_code
11
12
  ---
12
13
 
13
14
  You are a codebase exploration expert.
@@ -15,6 +15,7 @@ allowedTools:
15
15
  - background_process
16
16
  - mcp_config
17
17
  - dev_server
18
+ - trace_code
18
19
  ---
19
20
 
20
21
  # Plan Mode
@@ -5,8 +5,8 @@ import {
5
5
  } from "./chunk-JGNAYJJZ.js";
6
6
  import {
7
7
  runChatTurn
8
- } from "./chunk-FFQCZ2MI.js";
9
- import "./chunk-HJ7KRYWX.js";
8
+ } from "./chunk-BG5ILIWN.js";
9
+ import "./chunk-UR4A2HYT.js";
10
10
  import "./chunk-DL6ZILAF.js";
11
11
  import "./chunk-PBGOZMVY.js";
12
12
  import "./chunk-VRGRAQDG.js";
@@ -145,4 +145,4 @@ export {
145
145
  startChatSession,
146
146
  stopSessionExecution
147
147
  };
148
- //# sourceMappingURL=chat-handler-TPKAEATB.js.map
148
+ //# sourceMappingURL=chat-handler-4YZGM7V7.js.map
@@ -202,7 +202,7 @@ async function runCli(options) {
202
202
  if (!configExists) {
203
203
  await runNetworkSetup(mode);
204
204
  }
205
- const { runServe } = await import("./serve-VIWHYE6X.js");
205
+ const { runServe } = await import("./serve-RZWSK3AA.js");
206
206
  const serveOptions = { mode };
207
207
  if (values.port) serveOptions.port = parseInt(values.port);
208
208
  if (values["no-browser"] === true) serveOptions.openBrowser = false;
@@ -214,4 +214,4 @@ async function runCli(options) {
214
214
  export {
215
215
  runCli
216
216
  };
217
- //# sourceMappingURL=chunk-7W2WATAO.js.map
217
+ //# sourceMappingURL=chunk-4RYP3RBY.js.map
@@ -12,7 +12,7 @@ import {
12
12
  loadAllAgentsDefault,
13
13
  processEventsForConversation,
14
14
  runTopLevelAgentLoop
15
- } from "./chunk-HJ7KRYWX.js";
15
+ } from "./chunk-UR4A2HYT.js";
16
16
  import {
17
17
  TurnMetrics,
18
18
  WORKFLOW_KICKOFF_PROMPT,
@@ -310,4 +310,4 @@ export {
310
310
  runAgentTurn,
311
311
  injectWorkflowKickoffIfNeeded
312
312
  };
313
- //# sourceMappingURL=chunk-FFQCZ2MI.js.map
313
+ //# sourceMappingURL=chunk-BG5ILIWN.js.map
@@ -21,7 +21,7 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-GZZW4XSI.js";
24
+ } from "./chunk-P4SOUTLD.js";
25
25
  import {
26
26
  agentExists,
27
27
  createToolRegistry,
@@ -61,7 +61,7 @@ import {
61
61
  setMcpTools,
62
62
  setNotifyMcpServersChanged,
63
63
  skillExists
64
- } from "./chunk-HJ7KRYWX.js";
64
+ } from "./chunk-UR4A2HYT.js";
65
65
  import {
66
66
  getPathSeparator,
67
67
  isAbsolutePath
@@ -148,7 +148,7 @@ import {
148
148
  import express from "express";
149
149
  import cors from "cors";
150
150
  import { createServer as createHttpServer } from "http";
151
- import { fileURLToPath as fileURLToPath4 } from "url";
151
+ import { fileURLToPath as fileURLToPath5 } from "url";
152
152
  import { dirname as dirname5, resolve as resolve3, join as join5 } from "path";
153
153
  import { readFile } from "fs/promises";
154
154
  import { createServer as createViteServer } from "vite";
@@ -1594,6 +1594,7 @@ var McpManager = class {
1594
1594
  // src/server/lsp/server.ts
1595
1595
  import { spawn } from "child_process";
1596
1596
  import { extname } from "path";
1597
+ import { fileURLToPath } from "url";
1597
1598
  import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node.js";
1598
1599
  var LSP = {
1599
1600
  initialize: "initialize",
@@ -1603,7 +1604,12 @@ var LSP = {
1603
1604
  didOpen: "textDocument/didOpen",
1604
1605
  didChange: "textDocument/didChange",
1605
1606
  didClose: "textDocument/didClose",
1606
- publishDiagnostics: "textDocument/publishDiagnostics"
1607
+ publishDiagnostics: "textDocument/publishDiagnostics",
1608
+ definition: "textDocument/definition",
1609
+ references: "textDocument/references",
1610
+ typeDefinition: "textDocument/typeDefinition",
1611
+ hover: "textDocument/hover",
1612
+ workspaceSymbol: "workspace/symbol"
1607
1613
  };
1608
1614
  var DiagnosticSeverity = {
1609
1615
  Error: 1,
@@ -1611,7 +1617,38 @@ var DiagnosticSeverity = {
1611
1617
  Information: 3,
1612
1618
  Hint: 4
1613
1619
  };
1620
+ function hoverInfo(contents, range) {
1621
+ return range ? { contents, range } : { contents };
1622
+ }
1614
1623
  var DIAGNOSTIC_WAIT_MS = 2e3;
1624
+ var SYMBOL_KIND_NAMES = {
1625
+ 1: "File",
1626
+ 2: "Module",
1627
+ 3: "Namespace",
1628
+ 4: "Package",
1629
+ 5: "Class",
1630
+ 6: "Method",
1631
+ 7: "Property",
1632
+ 8: "Field",
1633
+ 9: "Constructor",
1634
+ 10: "Enum",
1635
+ 11: "Interface",
1636
+ 12: "Function",
1637
+ 13: "Variable",
1638
+ 14: "Constant",
1639
+ 15: "String",
1640
+ 16: "Number",
1641
+ 17: "Boolean",
1642
+ 18: "Array",
1643
+ 19: "Object",
1644
+ 20: "Key",
1645
+ 21: "Null",
1646
+ 22: "EnumMember",
1647
+ 23: "Struct",
1648
+ 24: "Event",
1649
+ 25: "Operator",
1650
+ 26: "TypeParameter"
1651
+ };
1615
1652
  function mapSeverity(severity) {
1616
1653
  switch (severity) {
1617
1654
  case DiagnosticSeverity.Error:
@@ -1906,6 +1943,174 @@ var LspServer = class {
1906
1943
  return () => this.diagnosticsCallbacks.delete(callback);
1907
1944
  }
1908
1945
  // ============================================================================
1946
+ // Code Navigation Queries
1947
+ // ============================================================================
1948
+ /**
1949
+ * Convert a file:// URI to a local path.
1950
+ * Handles Windows paths (file:///C:/...) and URL-encoded characters.
1951
+ */
1952
+ uriToPath(uri) {
1953
+ try {
1954
+ return fileURLToPath(uri);
1955
+ } catch {
1956
+ return uri.replace(/^file:\/\//, "");
1957
+ }
1958
+ }
1959
+ /**
1960
+ * Convert a local path to a file:// URI.
1961
+ */
1962
+ pathToUri(path) {
1963
+ return `file://${path}`;
1964
+ }
1965
+ /**
1966
+ * Convert an LSP location to our internal CodeLocation.
1967
+ */
1968
+ toCodeLocation(loc) {
1969
+ return {
1970
+ path: this.uriToPath(loc.uri),
1971
+ line: loc.range.start.line,
1972
+ character: loc.range.start.character,
1973
+ endLine: loc.range.end.line,
1974
+ endCharacter: loc.range.end.character
1975
+ };
1976
+ }
1977
+ /**
1978
+ * Ensure the server is running before making a query.
1979
+ */
1980
+ requireConnection() {
1981
+ if (this.state !== "running" || !this.connection) {
1982
+ throw new Error("LSP server is not running");
1983
+ }
1984
+ return this.connection;
1985
+ }
1986
+ /**
1987
+ * Normalize a single Location or Location[] response into CodeLocation[].
1988
+ */
1989
+ normalizeLocations(response) {
1990
+ if (!response) return [];
1991
+ if (Array.isArray(response)) {
1992
+ return response.map((loc) => this.toCodeLocation(loc));
1993
+ }
1994
+ return [this.toCodeLocation(response)];
1995
+ }
1996
+ /**
1997
+ * Find the definition of a symbol at the given position.
1998
+ * Sends textDocument/definition request.
1999
+ */
2000
+ async getDefinition(path, line, character) {
2001
+ try {
2002
+ const conn = this.requireConnection();
2003
+ const uri = this.pathToUri(path);
2004
+ const result = await conn.sendRequest(LSP.definition, {
2005
+ textDocument: { uri },
2006
+ position: { line, character }
2007
+ });
2008
+ return this.normalizeLocations(result);
2009
+ } catch {
2010
+ return [];
2011
+ }
2012
+ }
2013
+ /**
2014
+ * Find all references to a symbol at the given position.
2015
+ * Sends textDocument/references request.
2016
+ */
2017
+ async getReferences(path, line, character) {
2018
+ try {
2019
+ const conn = this.requireConnection();
2020
+ const uri = this.pathToUri(path);
2021
+ const result = await conn.sendRequest(LSP.references, {
2022
+ textDocument: { uri },
2023
+ position: { line, character },
2024
+ context: { includeDeclaration: true }
2025
+ });
2026
+ return this.normalizeLocations(result);
2027
+ } catch {
2028
+ return [];
2029
+ }
2030
+ }
2031
+ /**
2032
+ * Find the type definition of a symbol at the given position.
2033
+ * Sends textDocument/typeDefinition request.
2034
+ */
2035
+ async getTypeDefinition(path, line, character) {
2036
+ try {
2037
+ const conn = this.requireConnection();
2038
+ const uri = this.pathToUri(path);
2039
+ const result = await conn.sendRequest(LSP.typeDefinition, {
2040
+ textDocument: { uri },
2041
+ position: { line, character }
2042
+ });
2043
+ return this.normalizeLocations(result);
2044
+ } catch {
2045
+ return [];
2046
+ }
2047
+ }
2048
+ /**
2049
+ * Search workspace for a symbol by name.
2050
+ * Sends workspace/symbol request.
2051
+ */
2052
+ async findWorkspaceSymbol(query) {
2053
+ try {
2054
+ const conn = this.requireConnection();
2055
+ const result = await conn.sendRequest(LSP.workspaceSymbol, { query });
2056
+ if (!Array.isArray(result)) return [];
2057
+ return result.map((sym) => {
2058
+ const info = {
2059
+ name: sym.name,
2060
+ kind: this.symbolKindToString(sym.kind),
2061
+ location: this.toCodeLocation(sym.location)
2062
+ };
2063
+ if (sym.containerName) {
2064
+ info.containerName = sym.containerName;
2065
+ }
2066
+ return info;
2067
+ });
2068
+ } catch {
2069
+ return [];
2070
+ }
2071
+ }
2072
+ /**
2073
+ * Get hover information for a symbol at the given position.
2074
+ * Sends textDocument/hover request.
2075
+ */
2076
+ async getHoverInfo(path, line, character) {
2077
+ try {
2078
+ const conn = this.requireConnection();
2079
+ const uri = this.pathToUri(path);
2080
+ const result = await conn.sendRequest(LSP.hover, {
2081
+ textDocument: { uri },
2082
+ position: { line, character }
2083
+ });
2084
+ if (!result) return null;
2085
+ const contents = this.extractHoverContents(result.contents);
2086
+ const range = result.range ? {
2087
+ start: { line: result.range.start.line, character: result.range.start.character },
2088
+ end: { line: result.range.end.line, character: result.range.end.character }
2089
+ } : void 0;
2090
+ return hoverInfo(contents, range);
2091
+ } catch {
2092
+ return null;
2093
+ }
2094
+ }
2095
+ /**
2096
+ * Extract a plain-text string from hover contents (which can be
2097
+ * MarkupContent, MarkedString, or an array of either).
2098
+ */
2099
+ extractHoverContents(contents) {
2100
+ if (typeof contents === "string") return contents;
2101
+ if (Array.isArray(contents)) {
2102
+ return contents.map((c) => typeof c === "string" ? c : c.value).join("\n\n");
2103
+ }
2104
+ return contents.value;
2105
+ }
2106
+ /**
2107
+ * Convert LSP SymbolKind number to a human-readable string.
2108
+ * See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#symbolKind
2109
+ */
2110
+ symbolKindToString(kind) {
2111
+ return SYMBOL_KIND_NAMES[kind] ?? "Unknown";
2112
+ }
2113
+ // ============================================================================
1909
2114
  // Status
1910
2115
  // ============================================================================
1911
2116
  isRunning() {
@@ -1917,13 +2122,22 @@ var LspServer = class {
1917
2122
  getLanguage() {
1918
2123
  return this.config.id;
1919
2124
  }
2125
+ hasOpenDocuments() {
2126
+ return this.openDocuments.size > 0;
2127
+ }
2128
+ getExtensions() {
2129
+ return this.config.extensions;
2130
+ }
1920
2131
  };
1921
2132
 
2133
+ // src/server/lsp/manager.ts
2134
+ import { readFileSync as readFileSync2 } from "fs";
2135
+
1922
2136
  // src/server/utils/which.ts
1923
2137
  import { access, constants } from "fs/promises";
1924
2138
  import { join, dirname } from "path";
1925
- import { fileURLToPath } from "url";
1926
- var __dirname = dirname(fileURLToPath(import.meta.url));
2139
+ import { fileURLToPath as fileURLToPath2 } from "url";
2140
+ var __dirname = dirname(fileURLToPath2(import.meta.url));
1927
2141
  function getBundledBinPaths() {
1928
2142
  const paths = [];
1929
2143
  paths.push(join(process.cwd(), "node_modules", ".bin"));
@@ -1973,9 +2187,9 @@ async function which(command, workdir) {
1973
2187
  // src/server/lsp/languages.ts
1974
2188
  import { extname as extname2, basename } from "path";
1975
2189
  import { readFileSync } from "fs";
1976
- import { fileURLToPath as fileURLToPath2 } from "url";
2190
+ import { fileURLToPath as fileURLToPath3 } from "url";
1977
2191
  import { dirname as dirname2, resolve } from "path";
1978
- var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
2192
+ var __dirname2 = dirname2(fileURLToPath3(import.meta.url));
1979
2193
  var languagesJson = JSON.parse(readFileSync(resolve(__dirname2, "languages.json"), "utf-8"));
1980
2194
  var LANGUAGES = languagesJson;
1981
2195
  var extensionToLanguage = /* @__PURE__ */ new Map();
@@ -2151,6 +2365,85 @@ var LspManager = class {
2151
2365
  }
2152
2366
  return null;
2153
2367
  }
2368
+ // ============================================================================
2369
+ // Code Navigation Queries
2370
+ // ============================================================================
2371
+ /**
2372
+ * Find the definition of a symbol at the given position.
2373
+ */
2374
+ async getDefinition(path, line, character) {
2375
+ const server = await this.getServerForFile(path);
2376
+ if (!server) return [];
2377
+ return server.getDefinition(path, line, character);
2378
+ }
2379
+ /**
2380
+ * Find all references to a symbol at the given position.
2381
+ */
2382
+ async getReferences(path, line, character) {
2383
+ const server = await this.getServerForFile(path);
2384
+ if (!server) return [];
2385
+ return server.getReferences(path, line, character);
2386
+ }
2387
+ /**
2388
+ * Find the type definition of a symbol at the given position.
2389
+ */
2390
+ async getTypeDefinition(path, line, character) {
2391
+ const server = await this.getServerForFile(path);
2392
+ if (!server) return [];
2393
+ return server.getTypeDefinition(path, line, character);
2394
+ }
2395
+ /**
2396
+ * Search workspace for a symbol by name.
2397
+ * Uses the first available server that supports workspace/symbol.
2398
+ */
2399
+ async findWorkspaceSymbol(query) {
2400
+ for (const server of this.servers.values()) {
2401
+ if (server.isRunning()) {
2402
+ const results = await server.findWorkspaceSymbol(query);
2403
+ if (results.length > 0) return results;
2404
+ }
2405
+ }
2406
+ return [];
2407
+ }
2408
+ /**
2409
+ * Open a file to seed the LSP server, then search for a workspace symbol.
2410
+ *
2411
+ * Some LSP servers (e.g., typescript-language-server) only index projects
2412
+ * after a file is opened via textDocument/didOpen. This method:
2413
+ * 1. Detects the language from the file and starts the appropriate server
2414
+ * 2. Reads the file from disk and sends didOpen to trigger project indexing
2415
+ * 3. Sends a hover request to flush the notification queue (JSON-RPC processes
2416
+ * requests sequentially, so the server must finish didOpen before responding)
2417
+ * 4. Queries workspace/symbol for the given symbol
2418
+ */
2419
+ async seedAndFindWorkspaceSymbol(query, filePath) {
2420
+ const server = await this.getServerForFile(filePath);
2421
+ if (!server?.isRunning()) return [];
2422
+ try {
2423
+ const content = readFileSync2(filePath, "utf-8");
2424
+ await server.didOpen(filePath, content);
2425
+ await server.getHoverInfo(filePath, 0, 0);
2426
+ } catch (error) {
2427
+ logger.warn("Failed to seed LSP server", {
2428
+ path: filePath,
2429
+ error: error instanceof Error ? error.message : String(error),
2430
+ sessionId: this.sessionId
2431
+ });
2432
+ return [];
2433
+ }
2434
+ return server.findWorkspaceSymbol(query);
2435
+ }
2436
+ /**
2437
+ * Get hover information for a symbol at the given position.
2438
+ */
2439
+ async getHoverInfo(path, line, character) {
2440
+ const server = await this.getServerForFile(path);
2441
+ if (!server) return null;
2442
+ return server.getHoverInfo(path, line, character);
2443
+ }
2444
+ // ============================================================================
2445
+ // Shutdown
2446
+ // ============================================================================
2154
2447
  /**
2155
2448
  * Shutdown all LSP servers.
2156
2449
  */
@@ -3105,9 +3398,9 @@ function createSkillRoutes(configDir, projectDir) {
3105
3398
  // src/server/commands/registry.ts
3106
3399
  import { writeFile, mkdir, unlink } from "fs/promises";
3107
3400
  import { join as join3, dirname as dirname3 } from "path";
3108
- import { fileURLToPath as fileURLToPath3 } from "url";
3401
+ import { fileURLToPath as fileURLToPath4 } from "url";
3109
3402
  import matter from "gray-matter";
3110
- var __bundleDir = dirname3(fileURLToPath3(import.meta.url));
3403
+ var __bundleDir = dirname3(fileURLToPath4(import.meta.url));
3111
3404
  var DEFAULTS_DIR = join3(__bundleDir, "defaults");
3112
3405
  var DEFAULTS_DIR_ALT = join3(__bundleDir, "command-defaults");
3113
3406
  var COMMAND_EXTENSION = ".command.md";
@@ -3544,7 +3837,7 @@ import { Router as Router7 } from "express";
3544
3837
  import { spawn as spawn2 } from "child_process";
3545
3838
 
3546
3839
  // src/constants.ts
3547
- var VERSION = "2.0.38";
3840
+ var VERSION = "2.0.39";
3548
3841
 
3549
3842
  // src/server/routes/auto-update.ts
3550
3843
  var updateInProgress = false;
@@ -3659,7 +3952,7 @@ function createAutoUpdateRoutes(options = {}) {
3659
3952
  }
3660
3953
 
3661
3954
  // src/server/index.ts
3662
- var __dirname3 = dirname5(fileURLToPath4(import.meta.url));
3955
+ var __dirname3 = dirname5(fileURLToPath5(import.meta.url));
3663
3956
  async function createServerHandle(config4) {
3664
3957
  setRuntimeConfig(config4);
3665
3958
  setLogLevel(config4.logging?.level ?? void 0, config4.mode);
@@ -3741,7 +4034,7 @@ async function createServerHandle(config4) {
3741
4034
  setMcpTools(mcpTools);
3742
4035
  logger.info("MCP tools registered", { count: mcpTools.length });
3743
4036
  }
3744
- const { signalMcpReady } = await import("./server-MEJPUI4S.js");
4037
+ const { signalMcpReady } = await import("./server-YPCAJFOC.js");
3745
4038
  signalMcpReady();
3746
4039
  });
3747
4040
  const app = express();
@@ -3934,7 +4227,7 @@ async function createServerHandle(config4) {
3934
4227
  app.get("/api/sessions/:id", async (req, res) => {
3935
4228
  const { getEventStore: getEventStore2 } = await import("./events-KZZPCW2Q.js");
3936
4229
  const { buildMessagesFromStoredEvents } = await import("./folding-ENYH4LMG.js");
3937
- const { getPendingQuestionsForSession } = await import("./tools-ATO5MSWX.js");
4230
+ const { getPendingQuestionsForSession } = await import("./tools-ZYUJUI4R.js");
3938
4231
  const session = sessionManager.getSession(req.params.id);
3939
4232
  if (!session) {
3940
4233
  return res.status(404).json({ error: "Session not found" });
@@ -4085,7 +4378,7 @@ async function createServerHandle(config4) {
4085
4378
  if (!callId || approved === void 0) {
4086
4379
  return res.status(400).json({ error: "callId and approved are required" });
4087
4380
  }
4088
- const { providePathConfirmation } = await import("./tools-ATO5MSWX.js");
4381
+ const { providePathConfirmation } = await import("./tools-ZYUJUI4R.js");
4089
4382
  const result = providePathConfirmation(callId, approved, alwaysAllow);
4090
4383
  if (!result.found) {
4091
4384
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
@@ -4093,7 +4386,7 @@ async function createServerHandle(config4) {
4093
4386
  const { getEventStore: getEventStore2 } = await import("./events-KZZPCW2Q.js");
4094
4387
  const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-ENYH4LMG.js");
4095
4388
  const { createSessionStateMessage } = await import("./protocol-BKNLAEPJ.js");
4096
- const { getPendingQuestionsForSession } = await import("./tools-ATO5MSWX.js");
4389
+ const { getPendingQuestionsForSession } = await import("./tools-ZYUJUI4R.js");
4097
4390
  const eventStore = getEventStore2();
4098
4391
  const events = eventStore.getEvents(sessionId);
4099
4392
  const messages = buildMessagesFromStoredEvents(events);
@@ -4114,7 +4407,7 @@ async function createServerHandle(config4) {
4114
4407
  if (!skip && typeof answer !== "string") {
4115
4408
  return res.status(400).json({ error: "answer is required when not skipping" });
4116
4409
  }
4117
- const { provideAnswer } = await import("./tools-ATO5MSWX.js");
4410
+ const { provideAnswer } = await import("./tools-ZYUJUI4R.js");
4118
4411
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
4119
4412
  if (!found) {
4120
4413
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -4150,8 +4443,8 @@ async function createServerHandle(config4) {
4150
4443
  if (!session) {
4151
4444
  return res.status(404).json({ error: "Session not found" });
4152
4445
  }
4153
- const { stopSessionExecution } = await import("./chat-handler-TPKAEATB.js");
4154
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-ATO5MSWX.js");
4446
+ const { stopSessionExecution } = await import("./chat-handler-4YZGM7V7.js");
4447
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-ZYUJUI4R.js");
4155
4448
  const queuedMessages = sessionManager.getQueueState(sessionId);
4156
4449
  sessionManager.clearMessageQueue(sessionId);
4157
4450
  stopSessionExecution(sessionId, sessionManager);
@@ -4647,7 +4940,7 @@ async function createServerHandle(config4) {
4647
4940
  });
4648
4941
  async function rebuildMcpTools() {
4649
4942
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-B7QP6NLA.js");
4650
- const { setMcpTools: setMcpTools2 } = await import("./tools-ATO5MSWX.js");
4943
+ const { setMcpTools: setMcpTools2 } = await import("./tools-ZYUJUI4R.js");
4651
4944
  const mcpTools = createMcpTools2(mcpManager);
4652
4945
  setMcpTools2(mcpTools);
4653
4946
  }
@@ -4960,7 +5253,7 @@ async function createServerHandle(config4) {
4960
5253
  const state = sessionManager.getContextState(sessionId);
4961
5254
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
4962
5255
  });
4963
- const { QueueProcessor } = await import("./processor-NG4NH2SS.js");
5256
+ const { QueueProcessor } = await import("./processor-FB2I73B3.js");
4964
5257
  const queueProcessor = new QueueProcessor({
4965
5258
  sessionManager,
4966
5259
  providerManager,
@@ -5035,4 +5328,4 @@ export {
5035
5328
  createServerHandle,
5036
5329
  createServer
5037
5330
  };
5038
- //# sourceMappingURL=chunk-ATBNY4CL.js.map
5331
+ //# sourceMappingURL=chunk-BMRC6TQ5.js.map
@@ -2,7 +2,7 @@ import {
2
2
  injectWorkflowKickoffIfNeeded,
3
3
  runAgentTurn,
4
4
  runChatTurn
5
- } from "./chunk-FFQCZ2MI.js";
5
+ } from "./chunk-BG5ILIWN.js";
6
6
  import {
7
7
  applyDynamicContext,
8
8
  checkAborted,
@@ -16,7 +16,7 @@ import {
16
16
  loadAllAgentsDefault,
17
17
  saveItemToDir,
18
18
  spawnShellProcess
19
- } from "./chunk-HJ7KRYWX.js";
19
+ } from "./chunk-UR4A2HYT.js";
20
20
  import {
21
21
  getPlatformShell,
22
22
  onProcessEvent
@@ -2056,4 +2056,4 @@ export {
2056
2056
  signalMcpReady,
2057
2057
  createWebSocketServer
2058
2058
  };
2059
- //# sourceMappingURL=chunk-GZZW4XSI.js.map
2059
+ //# sourceMappingURL=chunk-P4SOUTLD.js.map
@@ -3452,7 +3452,7 @@ var callSubAgentTool = {
3452
3452
  };
3453
3453
  }
3454
3454
  try {
3455
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ATO5MSWX.js");
3455
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ZYUJUI4R.js");
3456
3456
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3457
3457
  const turnMetrics = new TurnMetrics();
3458
3458
  const result = await executeSubAgent({
@@ -4268,7 +4268,7 @@ function resolveAgentDef2(sessionManager, sessionId) {
4268
4268
  }
4269
4269
  async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
4270
4270
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
4271
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ATO5MSWX.js");
4271
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ZYUJUI4R.js");
4272
4272
  const tools = getToolRegistryForAgent2(agentDef).definitions;
4273
4273
  const toolFingerprint = getToolFingerprint(tools);
4274
4274
  const allAgents = await loadAllAgentsDefault();
@@ -4281,7 +4281,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
4281
4281
  async function computeSessionHash(sessionManager, sessionId) {
4282
4282
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
4283
4283
  const agentDef = await resolveAgentDef2(sessionManager, sessionId);
4284
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ATO5MSWX.js");
4284
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ZYUJUI4R.js");
4285
4285
  const tools = getToolRegistryForAgent2(agentDef).definitions;
4286
4286
  const toolFingerprint = getToolFingerprint(tools);
4287
4287
  return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
@@ -4388,7 +4388,7 @@ var mcpConfigTool = createTool(
4388
4388
  await saveGlobalConfig(mcpConfigMode, { ...globalConfig, mcpServers: updated });
4389
4389
  }
4390
4390
  async function rebuildTools() {
4391
- const { setMcpTools: setMcpTools2 } = await import("./tools-ATO5MSWX.js");
4391
+ const { setMcpTools: setMcpTools2 } = await import("./tools-ZYUJUI4R.js");
4392
4392
  const mcpTools = createMcpTools(mcpManagerForTools);
4393
4393
  setMcpTools2(mcpTools);
4394
4394
  }
@@ -4489,6 +4489,270 @@ var mcpConfigTool = createTool(
4489
4489
  }
4490
4490
  );
4491
4491
 
4492
+ // src/server/tools/trace-code.ts
4493
+ import { stat as stat2, readFile as readFile8 } from "fs/promises";
4494
+ function graphNode(location, depth, relation, symbolName, symbolKind) {
4495
+ const node = { location, depth, relation, symbolName };
4496
+ if (symbolKind) node.symbolKind = symbolKind;
4497
+ return node;
4498
+ }
4499
+ var MAX_DEPTH = 5;
4500
+ var VALID_DIRECTIONS = ["up", "down", "both"];
4501
+ var SNIPPET_RADIUS = 2;
4502
+ var CACHE_MAX_SIZE = 50;
4503
+ function locationKey(loc) {
4504
+ return `${loc.path}:${loc.line}:${loc.character}`;
4505
+ }
4506
+ function formatLocation(loc, workdir) {
4507
+ const relPath = loc.path.startsWith(workdir) ? loc.path.slice(workdir.length + 1) : loc.path;
4508
+ return `${relPath}:${loc.line + 1}:${loc.character}`;
4509
+ }
4510
+ var fileCache = /* @__PURE__ */ new Map();
4511
+ async function getCachedLines(path, approvedPaths) {
4512
+ if (!approvedPaths.has(path)) return [];
4513
+ const cached = fileCache.get(path);
4514
+ if (cached) {
4515
+ try {
4516
+ const stats = await stat2(path);
4517
+ if (stats.mtimeMs === cached.mtimeMs) {
4518
+ return cached.lines;
4519
+ }
4520
+ } catch {
4521
+ }
4522
+ }
4523
+ try {
4524
+ const stats = await stat2(path);
4525
+ const content = await readFile8(path, "utf-8");
4526
+ const lines = content.split("\n");
4527
+ fileCache.set(path, { mtimeMs: stats.mtimeMs, lines });
4528
+ if (fileCache.size > CACHE_MAX_SIZE) {
4529
+ const oldest = fileCache.keys().next();
4530
+ if (!oldest.done && oldest.value !== void 0) {
4531
+ fileCache.delete(oldest.value);
4532
+ }
4533
+ }
4534
+ return lines;
4535
+ } catch {
4536
+ return [];
4537
+ }
4538
+ }
4539
+ async function getSnippet(path, line, approvedPaths, radius = SNIPPET_RADIUS) {
4540
+ const lines = await getCachedLines(path, approvedPaths);
4541
+ if (lines.length === 0) return "";
4542
+ const start = Math.max(0, line - radius);
4543
+ const end = Math.min(lines.length - 1, line + radius);
4544
+ const lineNumWidth = String(end + 1).length;
4545
+ const result = [];
4546
+ for (let i = start; i <= end; i++) {
4547
+ const gutter = i === line ? ">" : " ";
4548
+ const lineNum = String(i + 1).padStart(lineNumWidth);
4549
+ result.push(`${gutter} ${lineNum}\u2502 ${lines[i] ?? ""}`);
4550
+ }
4551
+ return result.join("\n");
4552
+ }
4553
+ function formatEdgeLabel(relation) {
4554
+ switch (relation) {
4555
+ case "definition":
4556
+ return "definition";
4557
+ case "references":
4558
+ return "reference";
4559
+ case "type-definition":
4560
+ return "type definition";
4561
+ default:
4562
+ return relation;
4563
+ }
4564
+ }
4565
+ async function collectNodes(lsp, startLocations, symbolName, symbolKind, maxDepth, direction) {
4566
+ const visited = /* @__PURE__ */ new Set();
4567
+ const nodes = [];
4568
+ const edges = [];
4569
+ const queue = startLocations.map((loc) => ({ location: loc, depth: 0, relation: "match" }));
4570
+ let idx = 0;
4571
+ while (idx < queue.length) {
4572
+ const item = queue[idx];
4573
+ idx++;
4574
+ const key = locationKey(item.location);
4575
+ if (visited.has(key)) continue;
4576
+ visited.add(key);
4577
+ nodes.push(graphNode(item.location, item.depth, item.relation, symbolName, symbolKind));
4578
+ if (item.parentKey) {
4579
+ const relation = item.relation === "definition" ? "definition" : item.relation === "type-definition" ? "type-definition" : "references";
4580
+ edges.push({ from: item.parentKey, to: key, relation });
4581
+ }
4582
+ if (item.depth >= maxDepth) continue;
4583
+ const { path, line, character } = item.location;
4584
+ if (direction === "down" || direction === "both") {
4585
+ const defs = await lsp.getDefinition(path, line, character);
4586
+ for (const def of defs) {
4587
+ const defKey = locationKey(def);
4588
+ if (!visited.has(defKey)) {
4589
+ queue.push({ location: def, depth: item.depth + 1, relation: "definition", parentKey: key });
4590
+ }
4591
+ }
4592
+ const typeDefs = await lsp.getTypeDefinition(path, line, character);
4593
+ for (const td of typeDefs) {
4594
+ const tdKey = locationKey(td);
4595
+ if (!visited.has(tdKey)) {
4596
+ queue.push({ location: td, depth: item.depth + 1, relation: "type-definition", parentKey: key });
4597
+ }
4598
+ }
4599
+ }
4600
+ if (direction === "up" || direction === "both") {
4601
+ const refs = await lsp.getReferences(path, line, character);
4602
+ for (const ref of refs) {
4603
+ const refKey = locationKey(ref);
4604
+ if (!visited.has(refKey)) {
4605
+ queue.push({ location: ref, depth: item.depth + 1, relation: "reference", parentKey: key });
4606
+ }
4607
+ }
4608
+ }
4609
+ }
4610
+ return { nodes, edges };
4611
+ }
4612
+ async function formatOutput(symbolName, direction, depth, nodes, edges, workdir, approvedPaths) {
4613
+ const lines = [];
4614
+ lines.push(`Symbol: ${symbolName}`);
4615
+ lines.push(`Direction: ${direction} | Depth: ${depth}`);
4616
+ lines.push(`Nodes: ${nodes.length} | Edges: ${edges.length}`);
4617
+ lines.push("");
4618
+ if (nodes.length === 0) {
4619
+ lines.push("No results found.");
4620
+ return lines.join("\n");
4621
+ }
4622
+ const byDepth = /* @__PURE__ */ new Map();
4623
+ for (const node of nodes) {
4624
+ const group = byDepth.get(node.depth) ?? [];
4625
+ group.push(node);
4626
+ byDepth.set(node.depth, group);
4627
+ }
4628
+ for (const [depth2, group] of [...byDepth.entries()].sort(([a], [b]) => a - b)) {
4629
+ lines.push(`\u2500\u2500 Depth ${depth2} \u2500\u2500`);
4630
+ const fileGroups = /* @__PURE__ */ new Map();
4631
+ for (const node of group) {
4632
+ const fileKey = node.location.path;
4633
+ const fileGroup = fileGroups.get(fileKey) ?? [];
4634
+ fileGroup.push(node);
4635
+ fileGroups.set(fileKey, fileGroup);
4636
+ }
4637
+ for (const [filePath, fileNodes] of fileGroups) {
4638
+ const relPath = filePath.startsWith(workdir) ? filePath.slice(workdir.length + 1) : filePath;
4639
+ if (fileNodes.length > 5) {
4640
+ const kinds = [...new Set(fileNodes.map((n) => formatEdgeLabel(n.relation)))];
4641
+ lines.push(` ${kinds.join(", ")} \xD7 ${fileNodes.length} in ${relPath}`);
4642
+ } else {
4643
+ for (const node of fileNodes) {
4644
+ const label = formatEdgeLabel(node.relation);
4645
+ const kind = node.symbolKind ? ` (${node.symbolKind})` : "";
4646
+ const loc = formatLocation(node.location, workdir);
4647
+ lines.push(` ${label}: ${loc}${kind}`);
4648
+ const snippet = await getSnippet(node.location.path, node.location.line, approvedPaths);
4649
+ if (snippet) {
4650
+ for (const snipLine of snippet.split("\n")) {
4651
+ lines.push(` ${snipLine}`);
4652
+ }
4653
+ }
4654
+ }
4655
+ }
4656
+ }
4657
+ lines.push("");
4658
+ }
4659
+ if (edges.length > 0 && edges.length <= 20) {
4660
+ lines.push("Edges:");
4661
+ for (const edge of edges) {
4662
+ lines.push(` ${edge.from} \u2500\u2500${edge.relation}\u2500\u2500\u25B6 ${edge.to}`);
4663
+ }
4664
+ lines.push("");
4665
+ } else if (edges.length > 20) {
4666
+ lines.push(`Edges: ${edges.length} total (suppressed, too many to display individually)`);
4667
+ lines.push("");
4668
+ }
4669
+ return lines.join("\n");
4670
+ }
4671
+ var traceCodeTool = createTool(
4672
+ "trace_code",
4673
+ {
4674
+ type: "function",
4675
+ function: {
4676
+ name: "trace_code",
4677
+ description: "Trace a symbol through the codebase using LSP-powered static analysis. Finds definitions, references, and type definitions up to a configurable depth. Returns a graph of locations with inline code snippets for each node.",
4678
+ parameters: {
4679
+ type: "object",
4680
+ properties: {
4681
+ symbol: {
4682
+ type: "string",
4683
+ description: 'Symbol name to trace (e.g., "rebuildTools", "handleSubmit", "UserProfile")'
4684
+ },
4685
+ file: {
4686
+ type: "string",
4687
+ description: "File path containing the symbol. Used to seed the LSP server and detect the language. Relative to the working directory."
4688
+ },
4689
+ depth: {
4690
+ type: "number",
4691
+ description: "Graph traversal depth (default: 1, max: 5). How many hops to follow. Start with 1 for immediate defs+refs.",
4692
+ default: 1
4693
+ },
4694
+ direction: {
4695
+ type: "string",
4696
+ enum: ["up", "down", "both"],
4697
+ description: '"down" follows definitions, "up" finds references, "both" does both (default).',
4698
+ default: "both"
4699
+ }
4700
+ },
4701
+ required: ["symbol", "file"]
4702
+ }
4703
+ }
4704
+ },
4705
+ async (args, context, helpers) => {
4706
+ const symbol = args.symbol?.trim();
4707
+ if (!symbol) {
4708
+ return helpers.error("symbol is required");
4709
+ }
4710
+ const file = args.file?.trim();
4711
+ if (!file) {
4712
+ return helpers.error("file is required");
4713
+ }
4714
+ const depth = args.depth ?? 1;
4715
+ if (depth > MAX_DEPTH) {
4716
+ return helpers.error(`depth cannot exceed ${MAX_DEPTH}`);
4717
+ }
4718
+ const direction = args.direction ?? "both";
4719
+ if (!VALID_DIRECTIONS.includes(direction)) {
4720
+ return helpers.error(`direction must be one of: ${VALID_DIRECTIONS.join(", ")}`);
4721
+ }
4722
+ const lsp = context.lspManager;
4723
+ if (!lsp) {
4724
+ return helpers.error("LSP is not available. The trace_code tool requires a running LSP server.");
4725
+ }
4726
+ const fullPath = helpers.resolvePath(file);
4727
+ try {
4728
+ await stat2(fullPath);
4729
+ } catch {
4730
+ return helpers.error(`File not found: "${file}". Check that the path exists.`);
4731
+ }
4732
+ let symbols;
4733
+ try {
4734
+ symbols = await lsp.seedAndFindWorkspaceSymbol(symbol, fullPath);
4735
+ } catch {
4736
+ return helpers.error(`Failed to search for symbol "${symbol}". LSP may not be responding.`);
4737
+ }
4738
+ if (symbols.length === 0) {
4739
+ const installHint = lsp.getInstallHint(fullPath);
4740
+ const hint = installHint ? ` ${installHint}` : "";
4741
+ return helpers.error(
4742
+ `Symbol "${symbol}" not found in "${file}". Check that the symbol name is correct and that the LSP server has indexed the project.${hint}`
4743
+ );
4744
+ }
4745
+ const startLocations = symbols.map((s) => s.location);
4746
+ const symbolKind = symbols[0].kind;
4747
+ const { nodes, edges } = await collectNodes(lsp, startLocations, symbol, symbolKind, depth, direction);
4748
+ const allPaths = [...new Set(nodes.map((n) => n.location.path))];
4749
+ await helpers.checkPathAccess(allPaths);
4750
+ const approvedPaths = new Set(allPaths);
4751
+ const output = await formatOutput(symbol, direction, depth, nodes, edges, context.workdir, approvedPaths);
4752
+ return helpers.success(output);
4753
+ }
4754
+ );
4755
+
4492
4756
  // src/server/tools/index.ts
4493
4757
  var _builtInTools;
4494
4758
  function getBuiltInTools() {
@@ -4507,7 +4771,8 @@ function getBuiltInTools() {
4507
4771
  devServerTool,
4508
4772
  stepDoneTool,
4509
4773
  backgroundProcessTool,
4510
- mcpConfigTool
4774
+ mcpConfigTool,
4775
+ traceCodeTool
4511
4776
  ];
4512
4777
  }
4513
4778
  return _builtInTools;
@@ -4793,4 +5058,4 @@ export {
4793
5058
  getToolRegistryForAgent,
4794
5059
  createToolRegistry
4795
5060
  };
4796
- //# sourceMappingURL=chunk-HJ7KRYWX.js.map
5061
+ //# sourceMappingURL=chunk-UR4A2HYT.js.map
package/dist/cli/dev.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-7W2WATAO.js";
4
+ } from "../chunk-4RYP3RBY.js";
5
5
  import {
6
6
  logger
7
7
  } from "../chunk-K44MW7JJ.js";
package/dist/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-7W2WATAO.js";
4
+ } from "../chunk-4RYP3RBY.js";
5
5
  import {
6
6
  logger
7
7
  } from "../chunk-K44MW7JJ.js";
@@ -2,8 +2,8 @@ import {
2
2
  injectWorkflowKickoffIfNeeded,
3
3
  runAgentTurn,
4
4
  runChatTurn
5
- } from "./chunk-FFQCZ2MI.js";
6
- import "./chunk-HJ7KRYWX.js";
5
+ } from "./chunk-BG5ILIWN.js";
6
+ import "./chunk-UR4A2HYT.js";
7
7
  import "./chunk-DL6ZILAF.js";
8
8
  import "./chunk-PBGOZMVY.js";
9
9
  import "./chunk-VRGRAQDG.js";
@@ -42,4 +42,4 @@ export {
42
42
  runAgentTurn,
43
43
  runChatTurn
44
44
  };
45
- //# sourceMappingURL=orchestrator-ZFBVVQBO.js.map
45
+ //# sourceMappingURL=orchestrator-7B3XOZ4T.js.map
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfox",
3
- "version": "2.0.38",
3
+ "version": "2.0.39",
4
4
  "description": "Local-LLM-first agentic coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,7 +3,7 @@ import {
3
3
  finalizeTurnCompletion,
4
4
  generateSessionNameForSession
5
5
  } from "./chunk-JGNAYJJZ.js";
6
- import "./chunk-HJ7KRYWX.js";
6
+ import "./chunk-UR4A2HYT.js";
7
7
  import "./chunk-DL6ZILAF.js";
8
8
  import "./chunk-PBGOZMVY.js";
9
9
  import "./chunk-VRGRAQDG.js";
@@ -171,7 +171,7 @@ var QueueProcessor = class {
171
171
  backend: provider?.backend ?? llmClient.getBackend(),
172
172
  model: llmClient.getModel()
173
173
  };
174
- const { runChatTurn } = await import("./orchestrator-ZFBVVQBO.js");
174
+ const { runChatTurn } = await import("./orchestrator-7B3XOZ4T.js");
175
175
  const runChatTurnParams = buildRunChatTurnParams({
176
176
  sessionManager,
177
177
  sessionId,
@@ -216,4 +216,4 @@ var QueueProcessor = class {
216
216
  export {
217
217
  QueueProcessor
218
218
  };
219
- //# sourceMappingURL=processor-NG4NH2SS.js.map
219
+ //# sourceMappingURL=processor-FB2I73B3.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  VERSION,
3
3
  createServer
4
- } from "./chunk-ATBNY4CL.js";
5
- import "./chunk-GZZW4XSI.js";
6
- import "./chunk-FFQCZ2MI.js";
7
- import "./chunk-HJ7KRYWX.js";
4
+ } from "./chunk-BMRC6TQ5.js";
5
+ import "./chunk-P4SOUTLD.js";
6
+ import "./chunk-BG5ILIWN.js";
7
+ import "./chunk-UR4A2HYT.js";
8
8
  import "./chunk-DL6ZILAF.js";
9
9
  import "./chunk-PBGOZMVY.js";
10
10
  import "./chunk-VRGRAQDG.js";
@@ -199,4 +199,4 @@ async function runServe(options) {
199
199
  export {
200
200
  runServe
201
201
  };
202
- //# sourceMappingURL=serve-VIWHYE6X.js.map
202
+ //# sourceMappingURL=serve-RZWSK3AA.js.map
@@ -118,6 +118,33 @@ interface LLMClientWithModel extends LLMClient {
118
118
  setBackend(backend: Backend): void;
119
119
  }
120
120
 
121
+ interface CodeLocation {
122
+ path: string;
123
+ line: number;
124
+ character: number;
125
+ endLine: number;
126
+ endCharacter: number;
127
+ }
128
+ interface SymbolInfo {
129
+ name: string;
130
+ kind: string;
131
+ location: CodeLocation;
132
+ containerName?: string;
133
+ }
134
+ interface HoverInfo {
135
+ contents: string;
136
+ range?: {
137
+ start: {
138
+ line: number;
139
+ character: number;
140
+ };
141
+ end: {
142
+ line: number;
143
+ character: number;
144
+ };
145
+ };
146
+ }
147
+
121
148
  interface LspManagerInterface {
122
149
  /**
123
150
  * Notify the LSP that a file has changed and get diagnostics
@@ -136,6 +163,34 @@ interface LspManagerInterface {
136
163
  * Get installation hint for a file's language server, if it's not installed
137
164
  */
138
165
  getInstallHint(path: string): string | null;
166
+ /**
167
+ * Find the definition of a symbol at the given position
168
+ */
169
+ getDefinition(path: string, line: number, character: number): Promise<CodeLocation[]>;
170
+ /**
171
+ * Find all references to a symbol at the given position
172
+ */
173
+ getReferences(path: string, line: number, character: number): Promise<CodeLocation[]>;
174
+ /**
175
+ * Find the type definition of a symbol at the given position
176
+ */
177
+ getTypeDefinition(path: string, line: number, character: number): Promise<CodeLocation[]>;
178
+ /**
179
+ * Search workspace for a symbol by name
180
+ */
181
+ findWorkspaceSymbol(query: string): Promise<SymbolInfo[]>;
182
+ /**
183
+ * Open a file to seed the LSP server, then search for a workspace symbol.
184
+ * Some LSP servers (e.g., typescript-language-server) only index projects
185
+ * after a file is opened via textDocument/didOpen. This method handles
186
+ * that by opening the given file first, flushing the notification queue,
187
+ * then querying workspace/symbol.
188
+ */
189
+ seedAndFindWorkspaceSymbol(query: string, filePath: string): Promise<SymbolInfo[]>;
190
+ /**
191
+ * Get hover information for a symbol at the given position
192
+ */
193
+ getHoverInfo(path: string, line: number, character: number): Promise<HoverInfo | null>;
139
194
  /**
140
195
  * Shutdown all LSP servers
141
196
  */
@@ -242,6 +297,39 @@ declare class LspManager implements LspManagerInterface {
242
297
  * to avoid spamming the user on every edit.
243
298
  */
244
299
  getInstallHint(path: string): string | null;
300
+ /**
301
+ * Find the definition of a symbol at the given position.
302
+ */
303
+ getDefinition(path: string, line: number, character: number): Promise<CodeLocation[]>;
304
+ /**
305
+ * Find all references to a symbol at the given position.
306
+ */
307
+ getReferences(path: string, line: number, character: number): Promise<CodeLocation[]>;
308
+ /**
309
+ * Find the type definition of a symbol at the given position.
310
+ */
311
+ getTypeDefinition(path: string, line: number, character: number): Promise<CodeLocation[]>;
312
+ /**
313
+ * Search workspace for a symbol by name.
314
+ * Uses the first available server that supports workspace/symbol.
315
+ */
316
+ findWorkspaceSymbol(query: string): Promise<SymbolInfo[]>;
317
+ /**
318
+ * Open a file to seed the LSP server, then search for a workspace symbol.
319
+ *
320
+ * Some LSP servers (e.g., typescript-language-server) only index projects
321
+ * after a file is opened via textDocument/didOpen. This method:
322
+ * 1. Detects the language from the file and starts the appropriate server
323
+ * 2. Reads the file from disk and sends didOpen to trigger project indexing
324
+ * 3. Sends a hover request to flush the notification queue (JSON-RPC processes
325
+ * requests sequentially, so the server must finish didOpen before responding)
326
+ * 4. Queries workspace/symbol for the given symbol
327
+ */
328
+ seedAndFindWorkspaceSymbol(query: string, filePath: string): Promise<SymbolInfo[]>;
329
+ /**
330
+ * Get hover information for a symbol at the given position.
331
+ */
332
+ getHoverInfo(path: string, line: number, character: number): Promise<HoverInfo | null>;
245
333
  /**
246
334
  * Shutdown all LSP servers.
247
335
  */
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createServer,
3
3
  createServerHandle
4
- } from "../chunk-ATBNY4CL.js";
5
- import "../chunk-GZZW4XSI.js";
6
- import "../chunk-FFQCZ2MI.js";
7
- import "../chunk-HJ7KRYWX.js";
4
+ } from "../chunk-BMRC6TQ5.js";
5
+ import "../chunk-P4SOUTLD.js";
6
+ import "../chunk-BG5ILIWN.js";
7
+ import "../chunk-UR4A2HYT.js";
8
8
  import "../chunk-DL6ZILAF.js";
9
9
  import "../chunk-PBGOZMVY.js";
10
10
  import "../chunk-VRGRAQDG.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createWebSocketServer,
3
3
  signalMcpReady
4
- } from "./chunk-GZZW4XSI.js";
5
- import "./chunk-FFQCZ2MI.js";
6
- import "./chunk-HJ7KRYWX.js";
4
+ } from "./chunk-P4SOUTLD.js";
5
+ import "./chunk-BG5ILIWN.js";
6
+ import "./chunk-UR4A2HYT.js";
7
7
  import "./chunk-DL6ZILAF.js";
8
8
  import "./chunk-PBGOZMVY.js";
9
9
  import "./chunk-VRGRAQDG.js";
@@ -34,4 +34,4 @@ export {
34
34
  createWebSocketServer,
35
35
  signalMcpReady
36
36
  };
37
- //# sourceMappingURL=server-MEJPUI4S.js.map
37
+ //# sourceMappingURL=server-YPCAJFOC.js.map
@@ -12,7 +12,7 @@ import {
12
12
  setMcpTools,
13
13
  stepDoneTool,
14
14
  validateToolAction
15
- } from "./chunk-HJ7KRYWX.js";
15
+ } from "./chunk-UR4A2HYT.js";
16
16
  import "./chunk-DL6ZILAF.js";
17
17
  import "./chunk-PBGOZMVY.js";
18
18
  import "./chunk-VRGRAQDG.js";
@@ -57,4 +57,4 @@ export {
57
57
  stepDoneTool,
58
58
  validateToolAction
59
59
  };
60
- //# sourceMappingURL=tools-ATO5MSWX.js.map
60
+ //# sourceMappingURL=tools-ZYUJUI4R.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfox",
3
- "version": "2.0.38",
3
+ "version": "2.0.39",
4
4
  "description": "Local-LLM-first agentic coding assistant",
5
5
  "type": "module",
6
6
  "bin": {