diff --git a/README.ja.md b/README.ja.md index bd0096a66..6728f5e61 100644 --- a/README.ja.md +++ b/README.ja.md @@ -526,17 +526,13 @@ Ask @explore for the policy on this feature あなたがエディタで使っているその機能、他のエージェントは触ることができません。 最高の同僚に最高の道具を渡してください。これでリファクタリングも、ナビゲーションも、分析も、エージェントが適切に行えるようになります。 -- **lsp_hover**: その位置の型情報、ドキュメント、シグネチャを取得 - **lsp_goto_definition**: シンボル定義へジャンプ - **lsp_find_references**: ワークスペース全体で使用箇所を検索 -- **lsp_document_symbols**: ファイルのシンボルアウトラインを取得 -- **lsp_workspace_symbols**: プロジェクト全体から名前でシンボルを検索 +- **lsp_symbols**: ファイルからシンボルを取得 (scope='document') またはワークスペース全体を検索 (scope='workspace') - **lsp_diagnostics**: ビルド前にエラー/警告を取得 - **lsp_servers**: 利用可能な LSP サーバー一覧 - **lsp_prepare_rename**: 名前変更操作の検証 - **lsp_rename**: ワークスペース全体でシンボル名を変更 -- **lsp_code_actions**: 利用可能なクイックフィックス/リファクタリングを取得 -- **lsp_code_action_resolve**: コードアクションを適用 - **ast_grep_search**: AST 認識コードパターン検索 (25言語対応) - **ast_grep_replace**: AST 認識コード置換 diff --git a/README.md b/README.md index cb5c6436b..460f342f6 100644 --- a/README.md +++ b/README.md @@ -549,17 +549,13 @@ Syntax highlighting, autocomplete, refactoring, navigation, analysis—and now a The features in your editor? Other agents can't touch them. Hand your best tools to your best colleagues. Now they can properly refactor, navigate, and analyze. -- **lsp_hover**: Type info, docs, signatures at position - **lsp_goto_definition**: Jump to symbol definition - **lsp_find_references**: Find all usages across workspace -- **lsp_document_symbols**: Get file symbol outline -- **lsp_workspace_symbols**: Search symbols by name across project +- **lsp_symbols**: Get symbols from file (scope='document') or search across workspace (scope='workspace') - **lsp_diagnostics**: Get errors/warnings before build - **lsp_servers**: List available LSP servers - **lsp_prepare_rename**: Validate rename operation - **lsp_rename**: Rename symbol across workspace -- **lsp_code_actions**: Get available quick fixes/refactorings -- **lsp_code_action_resolve**: Apply code action - **ast_grep_search**: AST-aware code pattern search (25 languages) - **ast_grep_replace**: AST-aware code replacement - **call_omo_agent**: Spawn specialized explore/librarian agents. Supports `run_in_background` parameter for async execution. diff --git a/README.zh-cn.md b/README.zh-cn.md index 7ad2fec4a..cb19a0257 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -548,17 +548,13 @@ gh repo star code-yeongyu/oh-my-opencode 你编辑器中的功能?其他智能体无法触及。 把你最好的工具交给你最好的同事。现在它们可以正确地重构、导航和分析。 -- **lsp_hover**:位置处的类型信息、文档、签名 - **lsp_goto_definition**:跳转到符号定义 - **lsp_find_references**:查找工作区中的所有使用 -- **lsp_document_symbols**:获取文件符号概览 -- **lsp_workspace_symbols**:按名称在项目中搜索符号 +- **lsp_symbols**:从文件获取符号 (scope='document') 或在工作区中搜索 (scope='workspace') - **lsp_diagnostics**:在构建前获取错误/警告 - **lsp_servers**:列出可用的 LSP 服务器 - **lsp_prepare_rename**:验证重命名操作 - **lsp_rename**:在工作区中重命名符号 -- **lsp_code_actions**:获取可用的快速修复/重构 -- **lsp_code_action_resolve**:应用代码操作 - **ast_grep_search**:AST 感知的代码模式搜索(25 种语言) - **ast_grep_replace**:AST 感知的代码替换 - **call_omo_agent**:生成专业的 explore/librarian 智能体。支持 `run_in_background` 参数进行异步执行。 diff --git a/src/config/schema.ts b/src/config/schema.ts index 3b49c2732..950a359f6 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -198,7 +198,7 @@ export const DynamicContextPruningConfigSchema = z.object({ /** Tools that should never be pruned */ protected_tools: z.array(z.string()).default([ "task", "todowrite", "todoread", - "lsp_rename", "lsp_code_action_resolve", + "lsp_rename", "session_read", "session_write", "session_search", ]), /** Pruning strategies configuration */ diff --git a/src/features/builtin-commands/templates/init-deep.ts b/src/features/builtin-commands/templates/init-deep.ts index 05f2dd115..5fe2bb930 100644 --- a/src/features/builtin-commands/templates/init-deep.ts +++ b/src/features/builtin-commands/templates/init-deep.ts @@ -117,13 +117,13 @@ If \`--create-new\`: Read all existing first (preserve context) → then delete lsp_servers() # Check availability # Entry points (parallel) -lsp_document_symbols(filePath="src/index.ts") -lsp_document_symbols(filePath="main.py") +lsp_symbols(filePath="src/index.ts", scope="document") +lsp_symbols(filePath="main.py", scope="document") # Key symbols (parallel) -lsp_workspace_symbols(filePath=".", query="class") -lsp_workspace_symbols(filePath=".", query="interface") -lsp_workspace_symbols(filePath=".", query="function") +lsp_symbols(filePath=".", scope="workspace", query="class") +lsp_symbols(filePath=".", scope="workspace", query="interface") +lsp_symbols(filePath=".", scope="workspace", query="function") # Centrality for top exports lsp_find_references(filePath="...", line=X, character=Y) diff --git a/src/features/builtin-commands/templates/refactor.ts b/src/features/builtin-commands/templates/refactor.ts index c1174982c..94513a4bb 100644 --- a/src/features/builtin-commands/templates/refactor.ts +++ b/src/features/builtin-commands/templates/refactor.ts @@ -148,20 +148,15 @@ While background agents are running, use direct tools: ### LSP Tools for Precise Analysis: \`\`\`typescript -// Get symbol information at target location -lsp_hover(filePath, line, character) // Type info, docs, signatures - // Find definition(s) lsp_goto_definition(filePath, line, character) // Where is it defined? // Find ALL usages across workspace lsp_find_references(filePath, line, character, includeDeclaration=true) -// Get file structure -lsp_document_symbols(filePath) // Hierarchical outline - -// Search symbols by name -lsp_workspace_symbols(filePath, query="[target_symbol]") +// Get file structure (scope='document') or search symbols (scope='workspace') +lsp_symbols(filePath, scope="document") // Hierarchical outline +lsp_symbols(filePath, scope="workspace", query="[target_symbol]") // Search by name // Get current diagnostics lsp_diagnostics(filePath) // Errors, warnings before we start @@ -593,7 +588,7 @@ You already know these tools. Use them intelligently: ## LSP Tools Leverage the full LSP toolset (\`lsp_*\`) for precision analysis. Key patterns: -- **Understand before changing**: \`lsp_hover\`, \`lsp_goto_definition\` to grasp context +- **Understand before changing**: \`lsp_goto_definition\` to grasp context - **Impact analysis**: \`lsp_find_references\` to map all usages before modification - **Safe refactoring**: \`lsp_prepare_rename\` → \`lsp_rename\` for symbol renames - **Continuous verification**: \`lsp_diagnostics\` after every change diff --git a/src/hooks/anthropic-context-window-limit-recovery/executor.ts b/src/hooks/anthropic-context-window-limit-recovery/executor.ts index 8508e3c46..dbfaad19f 100644 --- a/src/hooks/anthropic-context-window-limit-recovery/executor.ts +++ b/src/hooks/anthropic-context-window-limit-recovery/executor.ts @@ -320,7 +320,6 @@ export async function executeCompact( "todowrite", "todoread", "lsp_rename", - "lsp_code_action_resolve", ], }; diff --git a/src/hooks/anthropic-context-window-limit-recovery/pruning-executor.ts b/src/hooks/anthropic-context-window-limit-recovery/pruning-executor.ts index b360602be..376c602f9 100644 --- a/src/hooks/anthropic-context-window-limit-recovery/pruning-executor.ts +++ b/src/hooks/anthropic-context-window-limit-recovery/pruning-executor.ts @@ -11,7 +11,6 @@ const DEFAULT_PROTECTED_TOOLS = new Set([ "todowrite", "todoread", "lsp_rename", - "lsp_code_action_resolve", "session_read", "session_write", "session_search", diff --git a/src/hooks/tool-output-truncator.ts b/src/hooks/tool-output-truncator.ts index 09713d646..9fe7362d2 100644 --- a/src/hooks/tool-output-truncator.ts +++ b/src/hooks/tool-output-truncator.ts @@ -13,8 +13,7 @@ const TRUNCATABLE_TOOLS = [ "Glob", "safe_glob", "lsp_find_references", - "lsp_document_symbols", - "lsp_workspace_symbols", + "lsp_symbols", "lsp_diagnostics", "ast_grep_search", "interactive_bash", diff --git a/src/tools/AGENTS.md b/src/tools/AGENTS.md index fd895d479..ee73fed95 100644 --- a/src/tools/AGENTS.md +++ b/src/tools/AGENTS.md @@ -30,7 +30,7 @@ tools/ ## TOOL CATEGORIES | Category | Tools | Purpose | |----------|-------|---------| -| LSP | lsp_hover, lsp_goto_definition, lsp_find_references, lsp_diagnostics, lsp_rename, etc. | IDE-grade code intelligence (11 tools) | +| LSP | lsp_goto_definition, lsp_find_references, lsp_symbols, lsp_diagnostics, lsp_rename, etc. | IDE-grade code intelligence (7 tools) | | AST | ast_grep_search, ast_grep_replace | Structural pattern matching/rewriting | | Search | grep, glob | Timeout-safe file and content search | | Session | session_list, session_read, session_search, session_info | History navigation and retrieval | diff --git a/src/tools/index.ts b/src/tools/index.ts index 3ec21b0a3..405602bb1 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,15 +1,11 @@ import { - lsp_hover, lsp_goto_definition, lsp_find_references, - lsp_document_symbols, - lsp_workspace_symbols, + lsp_symbols, lsp_diagnostics, lsp_servers, lsp_prepare_rename, lsp_rename, - lsp_code_actions, - lsp_code_action_resolve, lspManager, } from "./lsp" @@ -60,17 +56,13 @@ export function createBackgroundTools(manager: BackgroundManager, client: Openco } export const builtinTools: Record = { - lsp_hover, lsp_goto_definition, lsp_find_references, - lsp_document_symbols, - lsp_workspace_symbols, + lsp_symbols, lsp_diagnostics, lsp_servers, lsp_prepare_rename, lsp_rename, - lsp_code_actions, - lsp_code_action_resolve, ast_grep_search, ast_grep_replace, grep, diff --git a/src/tools/lsp/tools.ts b/src/tools/lsp/tools.ts index c2f170959..b0120c981 100644 --- a/src/tools/lsp/tools.ts +++ b/src/tools/lsp/tools.ts @@ -7,19 +7,16 @@ import { } from "./constants" import { withLspClient, - formatHoverResult, formatLocation, formatDocumentSymbol, formatSymbolInfo, formatDiagnostic, filterDiagnosticsBySeverity, formatPrepareRenameResult, - formatCodeActions, applyWorkspaceEdit, formatApplyResult, } from "./utils" import type { - HoverResult, Location, LocationLink, DocumentSymbol, @@ -28,33 +25,10 @@ import type { PrepareRenameResult, PrepareRenameDefaultBehavior, WorkspaceEdit, - CodeAction, - Command, } from "./types" -export const lsp_hover: ToolDefinition = tool({ - description: "Get type info, docs, and signature for a symbol at position.", - args: { - filePath: tool.schema.string(), - line: tool.schema.number().min(1).describe("1-based"), - character: tool.schema.number().min(0).describe("0-based"), - }, - execute: async (args, context) => { - try { - const result = await withLspClient(args.filePath, async (client) => { - return (await client.hover(args.filePath, args.line, args.character)) as HoverResult | null - }) - const output = formatHoverResult(result) - return output - } catch (e) { - const output = `Error: ${e instanceof Error ? e.message : String(e)}` - return output - } - }, -}) - export const lsp_goto_definition: ToolDefinition = tool({ description: "Jump to symbol definition. Find WHERE something is defined.", args: { @@ -129,75 +103,68 @@ export const lsp_find_references: ToolDefinition = tool({ }, }) -export const lsp_document_symbols: ToolDefinition = tool({ - description: "Get hierarchical outline of all symbols in a file.", +export const lsp_symbols: ToolDefinition = tool({ + description: "Get symbols from file (document) or search across workspace. Use scope='document' for file outline, scope='workspace' for project-wide symbol search.", args: { - filePath: tool.schema.string(), + filePath: tool.schema.string().describe("File path for LSP context"), + scope: tool.schema.enum(["document", "workspace"]).default("document").describe("'document' for file symbols, 'workspace' for project-wide search"), + query: tool.schema.string().optional().describe("Symbol name to search (required for workspace scope)"), + limit: tool.schema.number().optional().describe("Max results (default 50)"), }, execute: async (args, context) => { try { - const result = await withLspClient(args.filePath, async (client) => { - return (await client.documentSymbols(args.filePath)) as DocumentSymbol[] | SymbolInfo[] | null - }) + const scope = args.scope ?? "document" + + if (scope === "workspace") { + if (!args.query) { + return "Error: 'query' is required for workspace scope" + } + + const result = await withLspClient(args.filePath, async (client) => { + return (await client.workspaceSymbols(args.query!)) as SymbolInfo[] | null + }) - if (!result || result.length === 0) { - const output = "No symbols found" - return output - } + if (!result || result.length === 0) { + return "No symbols found" + } - const total = result.length - const truncated = total > DEFAULT_MAX_SYMBOLS - const limited = truncated ? result.slice(0, DEFAULT_MAX_SYMBOLS) : result - - const lines: string[] = [] - if (truncated) { - lines.push(`Found ${total} symbols (showing first ${DEFAULT_MAX_SYMBOLS}):`) - } - - if ("range" in limited[0]) { - lines.push(...(limited as DocumentSymbol[]).map((s) => formatDocumentSymbol(s))) + const total = result.length + const limit = Math.min(args.limit ?? DEFAULT_MAX_SYMBOLS, DEFAULT_MAX_SYMBOLS) + const truncated = total > limit + const limited = result.slice(0, limit) + const lines = limited.map(formatSymbolInfo) + if (truncated) { + lines.unshift(`Found ${total} symbols (showing first ${limit}):`) + } + return lines.join("\n") } else { - lines.push(...(limited as SymbolInfo[]).map(formatSymbolInfo)) + const result = await withLspClient(args.filePath, async (client) => { + return (await client.documentSymbols(args.filePath)) as DocumentSymbol[] | SymbolInfo[] | null + }) + + if (!result || result.length === 0) { + return "No symbols found" + } + + const total = result.length + const limit = Math.min(args.limit ?? DEFAULT_MAX_SYMBOLS, DEFAULT_MAX_SYMBOLS) + const truncated = total > limit + const limited = truncated ? result.slice(0, limit) : result + + const lines: string[] = [] + if (truncated) { + lines.push(`Found ${total} symbols (showing first ${limit}):`) + } + + if ("range" in limited[0]) { + lines.push(...(limited as DocumentSymbol[]).map((s) => formatDocumentSymbol(s))) + } else { + lines.push(...(limited as SymbolInfo[]).map(formatSymbolInfo)) + } + return lines.join("\n") } - return lines.join("\n") } catch (e) { - const output = `Error: ${e instanceof Error ? e.message : String(e)}` - return output - } - }, -}) - -export const lsp_workspace_symbols: ToolDefinition = tool({ - description: "Search symbols by name across ENTIRE workspace.", - args: { - filePath: tool.schema.string(), - query: tool.schema.string().describe("Symbol name (fuzzy match)"), - limit: tool.schema.number().optional().describe("Max results"), - }, - execute: async (args, context) => { - try { - const result = await withLspClient(args.filePath, async (client) => { - return (await client.workspaceSymbols(args.query)) as SymbolInfo[] | null - }) - - if (!result || result.length === 0) { - const output = "No symbols found" - return output - } - - const total = result.length - const limit = Math.min(args.limit ?? DEFAULT_MAX_SYMBOLS, DEFAULT_MAX_SYMBOLS) - const truncated = total > limit - const limited = result.slice(0, limit) - const lines = limited.map(formatSymbolInfo) - if (truncated) { - lines.unshift(`Found ${total} symbols (showing first ${limit}):`) - } - const output = lines.join("\n") - return output - } catch (e) { - const output = `Error: ${e instanceof Error ? e.message : String(e)}` - return output + return `Error: ${e instanceof Error ? e.message : String(e)}` } }, }) @@ -317,89 +284,3 @@ export const lsp_rename: ToolDefinition = tool({ } }, }) - -export const lsp_code_actions: ToolDefinition = tool({ - description: "Get available quick fixes, refactorings, and source actions (organize imports, fix all).", - args: { - filePath: tool.schema.string(), - startLine: tool.schema.number().min(1).describe("1-based"), - startCharacter: tool.schema.number().min(0).describe("0-based"), - endLine: tool.schema.number().min(1).describe("1-based"), - endCharacter: tool.schema.number().min(0).describe("0-based"), - kind: tool.schema - .enum([ - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports", - "source.fixAll", - ]) - .optional() - .describe("Filter by code action kind"), - }, - execute: async (args, context) => { - try { - const only = args.kind ? [args.kind] : undefined - const result = await withLspClient(args.filePath, async (client) => { - return (await client.codeAction( - args.filePath, - args.startLine, - args.startCharacter, - args.endLine, - args.endCharacter, - only - )) as (CodeAction | Command)[] | null - }) - const output = formatCodeActions(result) - return output - } catch (e) { - const output = `Error: ${e instanceof Error ? e.message : String(e)}` - return output - } - }, -}) - -export const lsp_code_action_resolve: ToolDefinition = tool({ - description: "Resolve and APPLY a code action from lsp_code_actions.", - args: { - filePath: tool.schema.string(), - codeAction: tool.schema.string().describe("Code action JSON from lsp_code_actions"), - }, - execute: async (args, context) => { - try { - const codeAction = JSON.parse(args.codeAction) as CodeAction - const resolved = await withLspClient(args.filePath, async (client) => { - return (await client.codeActionResolve(codeAction)) as CodeAction | null - }) - - if (!resolved) { - const output = "Failed to resolve code action" - return output - } - - const lines: string[] = [] - lines.push(`Action: ${resolved.title}`) - if (resolved.kind) lines.push(`Kind: ${resolved.kind}`) - - if (resolved.edit) { - const result = applyWorkspaceEdit(resolved.edit) - lines.push(formatApplyResult(result)) - } else { - lines.push("No edit to apply") - } - - if (resolved.command) { - lines.push(`Command: ${resolved.command.title} (${resolved.command.command}) - not executed`) - } - - const output = lines.join("\n") - return output - } catch (e) { - const output = `Error: ${e instanceof Error ? e.message : String(e)}` - return output - } - }, -})