fix: use ctx.directory instead of process.cwd() in tools for Desktop app support

Convert grep, glob, ast-grep, and session-manager tools from static exports to factory functions that receive PluginInput context. This allows them to use ctx.directory instead of process.cwd(), fixing issue #658 where tools search from wrong directory in OpenCode Desktop app.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim 2026-02-07 19:04:31 +09:00
parent 8e92704316
commit 476f154ef5
10 changed files with 282 additions and 287 deletions

View File

@ -86,6 +86,10 @@ import {
createTaskGetTool, createTaskGetTool,
createTaskList, createTaskList,
createTaskUpdateTool, createTaskUpdateTool,
createGrepTools,
createGlobTools,
createAstGrepTools,
createSessionManagerTools,
} from "./tools"; } from "./tools";
import { import {
CATEGORY_DESCRIPTIONS, CATEGORY_DESCRIPTIONS,
@ -538,6 +542,10 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
const allTools: Record<string, ToolDefinition> = { const allTools: Record<string, ToolDefinition> = {
...builtinTools, ...builtinTools,
...createGrepTools(ctx),
...createGlobTools(ctx),
...createAstGrepTools(ctx),
...createSessionManagerTools(ctx),
...backgroundTools, ...backgroundTools,
call_omo_agent: callOmoAgent, call_omo_agent: callOmoAgent,
...(lookAt ? { look_at: lookAt } : {}), ...(lookAt ? { look_at: lookAt } : {}),

View File

@ -1,12 +1,4 @@
import type { ToolDefinition } from "@opencode-ai/plugin" export { createAstGrepTools } from "./tools"
import { ast_grep_search, ast_grep_replace } from "./tools"
export const builtinTools: Record<string, ToolDefinition> = {
ast_grep_search,
ast_grep_replace,
}
export { ast_grep_search, ast_grep_replace }
export { ensureAstGrepBinary, getCachedBinaryPath, getCacheDir } from "./downloader" export { ensureAstGrepBinary, getCachedBinaryPath, getCacheDir } from "./downloader"
export { getAstGrepPath, isCliAvailable, ensureCliAvailable, startBackgroundInit } from "./cli" export { getAstGrepPath, isCliAvailable, ensureCliAvailable, startBackgroundInit } from "./cli"
export { checkEnvironment, formatEnvironmentCheck } from "./constants" export { checkEnvironment, formatEnvironmentCheck } from "./constants"

View File

@ -1,3 +1,4 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool" import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
import { CLI_LANGUAGES } from "./constants" import { CLI_LANGUAGES } from "./constants"
import { runSg } from "./cli" import { runSg } from "./cli"
@ -34,7 +35,8 @@ function getEmptyResultHint(pattern: string, lang: CliLanguage): string | null {
return null return null
} }
export const ast_grep_search: ToolDefinition = tool({ export function createAstGrepTools(ctx: PluginInput): Record<string, ToolDefinition> {
const ast_grep_search: ToolDefinition = tool({
description: description:
"Search code patterns across filesystem using AST-aware matching. Supports 25 languages. " + "Search code patterns across filesystem using AST-aware matching. Supports 25 languages. " +
"Use meta-variables: $VAR (single node), $$$ (multiple nodes). " + "Use meta-variables: $VAR (single node), $$$ (multiple nodes). " +
@ -53,7 +55,7 @@ export const ast_grep_search: ToolDefinition = tool({
const result = await runSg({ const result = await runSg({
pattern: args.pattern, pattern: args.pattern,
lang: args.lang as CliLanguage, lang: args.lang as CliLanguage,
paths: args.paths, paths: args.paths ?? [ctx.directory],
globs: args.globs, globs: args.globs,
context: args.context, context: args.context,
}) })
@ -77,7 +79,7 @@ export const ast_grep_search: ToolDefinition = tool({
}, },
}) })
export const ast_grep_replace: ToolDefinition = tool({ const ast_grep_replace: ToolDefinition = tool({
description: description:
"Replace code patterns across filesystem with AST-aware rewriting. " + "Replace code patterns across filesystem with AST-aware rewriting. " +
"Dry-run by default. Use meta-variables in rewrite to preserve matched content. " + "Dry-run by default. Use meta-variables in rewrite to preserve matched content. " +
@ -96,7 +98,7 @@ export const ast_grep_replace: ToolDefinition = tool({
pattern: args.pattern, pattern: args.pattern,
rewrite: args.rewrite, rewrite: args.rewrite,
lang: args.lang as CliLanguage, lang: args.lang as CliLanguage,
paths: args.paths, paths: args.paths ?? [ctx.directory],
globs: args.globs, globs: args.globs,
updateAll: args.dryRun === false, updateAll: args.dryRun === false,
}) })
@ -111,3 +113,5 @@ export const ast_grep_replace: ToolDefinition = tool({
}, },
}) })
return { ast_grep_search, ast_grep_replace }
}

View File

@ -1,3 +1 @@
import { glob } from "./tools" export { createGlobTools } from "./tools"
export { glob }

View File

@ -1,9 +1,11 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool" import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
import { runRgFiles } from "./cli" import { runRgFiles } from "./cli"
import { resolveGrepCliWithAutoInstall } from "./constants" import { resolveGrepCliWithAutoInstall } from "./constants"
import { formatGlobResult } from "./utils" import { formatGlobResult } from "./utils"
export const glob: ToolDefinition = tool({ export function createGlobTools(ctx: PluginInput): Record<string, ToolDefinition> {
const glob: ToolDefinition = tool({
description: description:
"Fast file pattern matching tool with safety limits (60s timeout, 100 file limit). " + "Fast file pattern matching tool with safety limits (60s timeout, 100 file limit). " +
"Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\". " + "Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\". " +
@ -23,8 +25,7 @@ export const glob: ToolDefinition = tool({
execute: async (args) => { execute: async (args) => {
try { try {
const cli = await resolveGrepCliWithAutoInstall() const cli = await resolveGrepCliWithAutoInstall()
// Use process.cwd() as the default search path when no path is provided const searchPath = args.path ?? ctx.directory
const searchPath = args.path ?? process.cwd()
const paths = [searchPath] const paths = [searchPath]
const result = await runRgFiles( const result = await runRgFiles(
@ -41,3 +42,6 @@ export const glob: ToolDefinition = tool({
} }
}, },
}) })
return { glob }
}

View File

@ -1,3 +1 @@
import { grep } from "./tools" export { createGrepTools } from "./tools"
export { grep }

View File

@ -1,8 +1,10 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool" import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
import { runRg } from "./cli" import { runRg } from "./cli"
import { formatGrepResult } from "./utils" import { formatGrepResult } from "./utils"
export const grep: ToolDefinition = tool({ export function createGrepTools(ctx: PluginInput): Record<string, ToolDefinition> {
const grep: ToolDefinition = tool({
description: description:
"Fast content search tool with safety limits (60s timeout, 10MB output). " + "Fast content search tool with safety limits (60s timeout, 10MB output). " +
"Searches file contents using regular expressions. " + "Searches file contents using regular expressions. " +
@ -23,8 +25,7 @@ export const grep: ToolDefinition = tool({
execute: async (args) => { execute: async (args) => {
try { try {
const globs = args.include ? [args.include] : undefined const globs = args.include ? [args.include] : undefined
// Use process.cwd() as the default search path when no path is provided const searchPath = args.path ?? ctx.directory
const searchPath = args.path ?? process.cwd()
const paths = [searchPath] const paths = [searchPath]
const result = await runRg({ const result = await runRg({
@ -40,3 +41,6 @@ export const grep: ToolDefinition = tool({
} }
}, },
}) })
return { grep }
}

View File

@ -10,21 +10,11 @@ import {
export { lspManager } export { lspManager }
import { export { createAstGrepTools } from "./ast-grep"
ast_grep_search, export { createGrepTools } from "./grep"
ast_grep_replace, export { createGlobTools } from "./glob"
} from "./ast-grep"
import { grep } from "./grep"
import { glob } from "./glob"
export { createSlashcommandTool, discoverCommandsSync } from "./slashcommand" export { createSlashcommandTool, discoverCommandsSync } from "./slashcommand"
export { createSessionManagerTools } from "./session-manager"
import {
session_list,
session_read,
session_search,
session_info,
} from "./session-manager"
export { sessionExists } from "./session-manager/storage" export { sessionExists } from "./session-manager/storage"
@ -70,12 +60,4 @@ export const builtinTools: Record<string, ToolDefinition> = {
lsp_diagnostics, lsp_diagnostics,
lsp_prepare_rename, lsp_prepare_rename,
lsp_rename, lsp_rename,
ast_grep_search,
ast_grep_replace,
grep,
glob,
session_list,
session_read,
session_search,
session_info,
} }

View File

@ -1,3 +1,3 @@
export * from "./tools" export { createSessionManagerTools } from "./tools"
export * from "./types" export * from "./types"
export * from "./constants" export * from "./constants"

View File

@ -1,3 +1,4 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool" import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
import { import {
SESSION_LIST_DESCRIPTION, SESSION_LIST_DESCRIPTION,
@ -26,7 +27,8 @@ function withTimeout<T>(promise: Promise<T>, ms: number, operation: string): Pro
]) ])
} }
export const session_list: ToolDefinition = tool({ export function createSessionManagerTools(ctx: PluginInput): Record<string, ToolDefinition> {
const session_list: ToolDefinition = tool({
description: SESSION_LIST_DESCRIPTION, description: SESSION_LIST_DESCRIPTION,
args: { args: {
limit: tool.schema.number().optional().describe("Maximum number of sessions to return"), limit: tool.schema.number().optional().describe("Maximum number of sessions to return"),
@ -36,7 +38,7 @@ export const session_list: ToolDefinition = tool({
}, },
execute: async (args: SessionListArgs, _context) => { execute: async (args: SessionListArgs, _context) => {
try { try {
const directory = args.project_path ?? process.cwd() const directory = args.project_path ?? ctx.directory
let sessions = await getMainSessions({ directory }) let sessions = await getMainSessions({ directory })
let sessionIDs = sessions.map((s) => s.id) let sessionIDs = sessions.map((s) => s.id)
@ -55,7 +57,7 @@ export const session_list: ToolDefinition = tool({
}, },
}) })
export const session_read: ToolDefinition = tool({ const session_read: ToolDefinition = tool({
description: SESSION_READ_DESCRIPTION, description: SESSION_READ_DESCRIPTION,
args: { args: {
session_id: tool.schema.string().describe("Session ID to read"), session_id: tool.schema.string().describe("Session ID to read"),
@ -84,7 +86,7 @@ export const session_read: ToolDefinition = tool({
}, },
}) })
export const session_search: ToolDefinition = tool({ const session_search: ToolDefinition = tool({
description: SESSION_SEARCH_DESCRIPTION, description: SESSION_SEARCH_DESCRIPTION,
args: { args: {
query: tool.schema.string().describe("Search query string"), query: tool.schema.string().describe("Search query string"),
@ -125,7 +127,7 @@ export const session_search: ToolDefinition = tool({
}, },
}) })
export const session_info: ToolDefinition = tool({ const session_info: ToolDefinition = tool({
description: SESSION_INFO_DESCRIPTION, description: SESSION_INFO_DESCRIPTION,
args: { args: {
session_id: tool.schema.string().describe("Session ID to inspect"), session_id: tool.schema.string().describe("Session ID to inspect"),
@ -144,3 +146,6 @@ export const session_info: ToolDefinition = tool({
} }
}, },
}) })
return { session_list, session_read, session_search, session_info }
}