# src/tools/ Codemap ## Responsibility The `src/tools/` directory provides the core tool implementations for the oh-my-opencode-slim plugin. It exposes three main categories of tools: 1. **Grep** - Fast regex-based content search using ripgrep (with fallback to system grep) 2. **LSP** - Language Server Protocol integration for code intelligence (definition, references, diagnostics, rename) 3. **AST-grep** - AST-aware structural code search and replacement across 25 languages 4. **Background Tasks** - Fire-and-forget agent task management with automatic notification These tools are consumed by the OpenCode plugin system and exposed to AI agents for code navigation, analysis, and modification tasks. --- ## Design ### Architecture Overview ``` src/tools/ ├── index.ts # Central export point ├── background.ts # Background task tools ├── grep/ # Regex search (ripgrep-based) │ ├── cli.ts # CLI execution layer │ ├── tools.ts # Tool definition │ ├── types.ts # TypeScript interfaces │ ├── utils.ts # Output formatting │ ├── constants.ts # Safety limits & CLI resolution │ └── downloader.ts # Binary auto-download ├── lsp/ # Language Server Protocol │ ├── client.ts # LSP client & connection pooling │ ├── tools.ts # 4 tool definitions │ ├── types.ts # LSP type re-exports │ ├── utils.ts # Formatters & workspace edit application │ ├── config.ts # Server discovery & language mapping │ └── constants.ts # Built-in server configs └── ast-grep/ # AST-aware search ├── cli.ts # CLI execution layer ├── tools.ts # 2 tool definitions ├── types.ts # TypeScript interfaces ├── utils.ts # Output formatting & hints ├── constants.ts # CLI resolution & environment checks └── downloader.ts # Binary auto-download ``` ### Key Patterns #### 1. Tool Definition Pattern All tools follow the OpenCode plugin tool schema: ```typescript export const toolName: ToolDefinition = tool({ description: string, args: { /* Zod schema */ }, execute: async (args, context) => { /* implementation */ } }); ``` #### 2. CLI Abstraction Layer Both `grep/` and `ast-grep/` use a similar CLI execution pattern: - **cli.ts**: Low-level subprocess spawning with timeout handling - **tools.ts**: High-level tool definitions that call CLI functions - **constants.ts**: CLI path resolution with fallback chain - **downloader.ts**: Binary auto-download for missing dependencies #### 3. Connection Pooling (LSP) The LSP module implements a singleton `LSPServerManager` with: - **Connection pooling**: Reuse LSP clients per workspace root - **Reference counting**: Track active usage - **Idle cleanup**: Auto-shutdown after 5 minutes of inactivity - **Initialization tracking**: Prevent concurrent initialization #### 4. Safety Limits All tools enforce strict safety limits: - **Timeout**: 60s (grep), 300s (ast-grep, LSP) - **Output size**: 10MB (grep), 1MB (ast-grep) - **Match limits**: 500 matches (grep), 200 diagnostics/references (LSP) - **Depth limits**: 20 directories (grep) #### 5. Error Handling - Graceful degradation (ripgrep → grep fallback) - Clear error messages with installation hints - Timeout handling with process cleanup - Truncation detection and reporting --- ## Flow ### Grep Tool Flow ``` User Request ↓ grep tool (tools.ts) ↓ runRg() (cli.ts) ↓ resolveGrepCli() (constants.ts) ├─→ OpenCode bundled rg ├─→ System PATH rg ├─→ Cached download └─→ System grep (fallback) ↓ buildArgs() → Safety flags + user options ↓ spawn([cli, ...args]) with timeout ↓ parseOutput() → GrepMatch[] ↓ formatGrepResult() (utils.ts) ↓ Group by file → Return formatted output ``` ### LSP Tool Flow ``` User Request (e.g., lsp_goto_definition) ↓ Tool definition (tools.ts) ↓ withLspClient() (utils.ts) ├─→ findServerForExtension() (config.ts) │ ├─→ Match extension to BUILTIN_SERVERS │ └─→ isServerInstalled() → PATH check ├─→ findWorkspaceRoot() → .git, package.json, etc. └─→ lspManager.getClient() (client.ts) ├─→ Check cache (root::serverId) ├─→ If cached: increment refCount, return └─→ If new: ├─→ new LSPClient(root, server) ├─→ client.start() → spawn server ├─→ client.initialize() → LSP handshake └─→ Store in pool with refCount=1 ↓ client.definition() / references() / diagnostics() / rename() ├─→ openFile() → textDocument/didOpen └─→ Send LSP request ↓ Format result (formatLocation, formatDiagnostic, etc.) ↓ lspManager.releaseClient() → decrement refCount ↓ Return formatted output ``` **LSP Client Lifecycle:** ``` start() ├─→ spawn(command) ├─→ Create JSON-RPC connection (vscode-jsonrpc) ├─→ Register handlers (diagnostics, configuration) └─→ Wait for process to stabilize ↓ initialize() ├─→ sendRequest('initialize', capabilities) └─→ sendNotification('initialized') ↓ [Operational phase] ├─→ openFile() → textDocument/didOpen ├─→ definition() / references() / diagnostics() / rename() └─→ Receive notifications (diagnostics) ↓ stop() ├─→ sendRequest('shutdown') ├─→ sendNotification('exit') └─→ kill process ``` ### AST-grep Tool Flow ``` User Request (ast_grep_search or ast_grep_replace) ↓ Tool definition (tools.ts) ↓ runSg() (cli.ts) ├─→ getAstGrepPath() │ ├─→ Check cached path │ ├─→ findSgCliPathSync() │ │ ├─→ Cached binary │ │ ├─→ @ast-grep/cli package │ │ ├─→ Platform-specific package │ │ └─→ Homebrew (macOS) │ └─→ ensureAstGrepBinary() → download if missing └─→ Build args: pattern, lang, rewrite, globs, paths ↓ spawn([sg, 'run', '-p', pattern, '--lang', lang, ...]) ↓ Parse JSON output → CliMatch[] ↓ Handle truncation (max_output_bytes, max_matches) ↓ formatSearchResult() / formatReplaceResult() (utils.ts) ├─→ Group by file ├─→ Truncate long text └─→ Add summary ↓ Add empty result hints (getEmptyResultHint) ↓ Return formatted output ``` ### Background Task Flow ``` User Request (background_task) ↓ Tool definition (background.ts) ↓ manager.launch() ├─→ Create task with unique ID ├─→ Store in BackgroundTaskManager └─→ Return task_id immediately (~1ms) ↓ [Background execution] ├─→ Agent runs independently ├─→ Completes with result/error └─→ Auto-notify parent session ↓ User Request (background_output) ↓ manager.getResult(task_id) ├─→ If timeout > 0: waitForCompletion() └─→ Return status/result/error ↓ User Request (background_cancel) ↓ manager.cancel(task_id) or manager.cancel(all) └─→ Cancel running tasks only ``` --- ## Integration ### Dependencies #### External Dependencies - **@opencode-ai/plugin**: Tool definition schema (`tool`, `ToolDefinition`) - **vscode-jsonrpc**: LSP JSON-RPC protocol implementation - **vscode-languageserver-protocol**: LSP type definitions - **bun**: Subprocess spawning (`spawn`), file operations (`Bun.write`) #### Internal Dependencies - **src/background**: `BackgroundTaskManager` for background task tools - **src/config**: `SUBAGENT_NAMES`, `PluginConfig`, `TmuxConfig` - **src/utils**: `extractZip` for binary extraction ### Consumers #### Direct Consumers - **src/index.ts**: Main plugin entry point imports all tools - **src/cli/index.ts**: CLI entry point may use tools directly #### Tool Registry All tools are exported from `src/tools/index.ts`: ```typescript export { grep } from './grep'; export { ast_grep_search, ast_grep_replace } from './ast-grep'; export { lsp_diagnostics, lsp_find_references, lsp_goto_definition, lsp_rename, lspManager, } from './lsp'; export { createBackgroundTools } from './background'; ``` ### Configuration #### LSP Server Configuration - **BUILTIN_SERVERS** (lsp/constants.ts): Pre-configured servers for 12 languages - **EXT_TO_LANG** (lsp/constants.ts): Extension to language ID mapping - **LSP_INSTALL_HINTS** (lsp/constants.ts): Installation instructions per server #### Grep Configuration - **Safety limits** (grep/constants.ts): Max depth, filesize, count, columns, timeout - **RG_SAFETY_FLAGS**: `--no-follow`, `--color=never`, `--no-heading`, `--line-number`, `--with-filename` - **GREP_SAFETY_FLAGS**: `-n`, `-H`, `--color=never` #### AST-grep Configuration - **CLI_LANGUAGES** (ast-grep/types.ts): 25 supported languages - **LANG_EXTENSIONS** (ast-grep/constants.ts): Language to file extension mapping - **Safety limits**: Timeout (300s), max output (1MB), max matches (500) ### Binary Management #### Ripgrep (grep/downloader.ts) - **Version**: 14.1.1 - **Platforms**: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64 - **Install location**: `~/.cache/oh-my-opencode-slim/bin/rg` (Linux/macOS), `%LOCALAPPDATA%\oh-my-opencode-slim\bin\rg.exe` (Windows) - **Fallback**: System grep if ripgrep unavailable #### AST-grep (ast-grep/downloader.ts) - **Version**: 0.40.0 (synced with @ast-grep/cli package) - **Platforms**: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64, win32-arm64, win32-ia32 - **Install location**: `~/.cache/oh-my-opencode-slim/bin/sg` (Linux/macOS), `%LOCALAPPDATA%\oh-my-opencode-slim\bin\sg.exe` (Windows) - **Fallback**: Manual installation instructions ### Error Handling Integration All tools follow a consistent error handling pattern: 1. Try-catch around execution 2. Return formatted error messages 3. Include installation hints for missing binaries 4. Graceful degradation (fallback tools) 5. Timeout handling with process cleanup ### Performance Considerations - **Connection pooling**: LSP clients reused across tool calls - **Idle cleanup**: LSP clients shutdown after 5 minutes inactivity - **Output truncation**: Prevent memory issues with large outputs - **Timeout enforcement**: All subprocess operations have timeouts - **Caching**: CLI paths cached to avoid repeated filesystem checks - **Background tasks**: Fire-and-forget pattern for long-running operations --- ## File-by-File Summary ### Root Level - **index.ts**: Central export point for all tools - **background.ts**: Background task management (3 tools: launch, output, cancel) ### grep/ - **index.ts**: Re-exports grep module - **cli.ts**: `runRg()`, `runRgCount()` - subprocess execution with timeout - **tools.ts**: `grep` tool definition - **types.ts**: `GrepMatch`, `GrepResult`, `CountResult`, `GrepOptions` - **utils.ts**: `formatGrepResult()` - output formatting - **constants.ts**: Safety limits, `resolveGrepCli()`, `resolveGrepCliWithAutoInstall()` - **downloader.ts**: `downloadAndInstallRipgrep()`, `getInstalledRipgrepPath()` ### lsp/ - **index.ts**: Re-exports LSP module and types - **client.ts**: `LSPServerManager` (singleton), `LSPClient` class - **tools.ts**: 4 tools: `lsp_goto_definition`, `lsp_find_references`, `lsp_diagnostics`, `lsp_rename` - **types.ts**: LSP type re-exports from vscode-languageserver-protocol - **utils.ts**: `withLspClient()`, formatters, `applyWorkspaceEdit()` - **config.ts**: `findServerForExtension()`, `getLanguageId()`, `isServerInstalled()` - **constants.ts**: `BUILTIN_SERVERS`, `EXT_TO_LANG`, `LSP_INSTALL_HINTS`, safety limits ### ast-grep/ - **index.ts**: Re-exports ast-grep module - **cli.ts**: `runSg()`, `getAstGrepPath()`, `startBackgroundInit()` - **tools.ts**: 2 tools: `ast_grep_search`, `ast_grep_replace` - **types.ts**: `CliLanguage`, `CliMatch`, `SgResult`, `CLI_LANGUAGES` - **utils.ts**: `formatSearchResult()`, `formatReplaceResult()`, `getEmptyResultHint()` - **constants.ts**: `findSgCliPathSync()`, `checkEnvironment()`, safety limits - **downloader.ts**: `downloadAstGrep()`, `ensureAstGrepBinary()`, cache management