The src/tools/ directory provides the core tool implementations for the oh-my-opencode-slim plugin. It exposes three main categories of tools:
These tools are consumed by the OpenCode plugin system and exposed to AI agents for code navigation, analysis, and modification tasks.
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
All tools follow the OpenCode plugin tool schema:
export const toolName: ToolDefinition = tool({
description: string,
args: { /* Zod schema */ },
execute: async (args, context) => { /* implementation */ }
});
Both grep/ and ast-grep/ use a similar CLI execution pattern:
The LSP module implements a singleton LSPServerManager with:
All tools enforce strict safety limits:
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
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
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
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
tool, ToolDefinition)spawn), file operations (Bun.write)BackgroundTaskManager for background task toolsSUBAGENT_NAMES, PluginConfig, TmuxConfigextractZip for binary extractionAll tools are exported from src/tools/index.ts:
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';
--no-follow, --color=never, --no-heading, --line-number, --with-filename-n, -H, --color=never~/.cache/oh-my-opencode-slim/bin/rg (Linux/macOS), %LOCALAPPDATA%\oh-my-opencode-slim\bin\rg.exe (Windows)~/.cache/oh-my-opencode-slim/bin/sg (Linux/macOS), %LOCALAPPDATA%\oh-my-opencode-slim\bin\sg.exe (Windows)All tools follow a consistent error handling pattern:
runRg(), runRgCount() - subprocess execution with timeoutgrep tool definitionGrepMatch, GrepResult, CountResult, GrepOptionsformatGrepResult() - output formattingresolveGrepCli(), resolveGrepCliWithAutoInstall()downloadAndInstallRipgrep(), getInstalledRipgrepPath()LSPServerManager (singleton), LSPClient classlsp_goto_definition, lsp_find_references, lsp_diagnostics, lsp_renamewithLspClient(), formatters, applyWorkspaceEdit()findServerForExtension(), getLanguageId(), isServerInstalled()BUILTIN_SERVERS, EXT_TO_LANG, LSP_INSTALL_HINTS, safety limitsrunSg(), getAstGrepPath(), startBackgroundInit()ast_grep_search, ast_grep_replaceCliLanguage, CliMatch, SgResult, CLI_LANGUAGESformatSearchResult(), formatReplaceResult(), getEmptyResultHint()findSgCliPathSync(), checkEnvironment(), safety limitsdownloadAstGrep(), ensureAstGrepBinary(), cache management