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:
parent
8e92704316
commit
476f154ef5
@ -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 } : {}),
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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,80 +35,83 @@ 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> {
|
||||||
description:
|
const ast_grep_search: ToolDefinition = tool({
|
||||||
"Search code patterns across filesystem using AST-aware matching. Supports 25 languages. " +
|
description:
|
||||||
"Use meta-variables: $VAR (single node), $$$ (multiple nodes). " +
|
"Search code patterns across filesystem using AST-aware matching. Supports 25 languages. " +
|
||||||
"IMPORTANT: Patterns must be complete AST nodes (valid code). " +
|
"Use meta-variables: $VAR (single node), $$$ (multiple nodes). " +
|
||||||
"For functions, include params and body: 'export async function $NAME($$$) { $$$ }' not 'export async function $NAME'. " +
|
"IMPORTANT: Patterns must be complete AST nodes (valid code). " +
|
||||||
"Examples: 'console.log($MSG)', 'def $FUNC($$$):', 'async function $NAME($$$)'",
|
"For functions, include params and body: 'export async function $NAME($$$) { $$$ }' not 'export async function $NAME'. " +
|
||||||
args: {
|
"Examples: 'console.log($MSG)', 'def $FUNC($$$):', 'async function $NAME($$$)'",
|
||||||
pattern: tool.schema.string().describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."),
|
args: {
|
||||||
lang: tool.schema.enum(CLI_LANGUAGES).describe("Target language"),
|
pattern: tool.schema.string().describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."),
|
||||||
paths: tool.schema.array(tool.schema.string()).optional().describe("Paths to search (default: ['.'])"),
|
lang: tool.schema.enum(CLI_LANGUAGES).describe("Target language"),
|
||||||
globs: tool.schema.array(tool.schema.string()).optional().describe("Include/exclude globs (prefix ! to exclude)"),
|
paths: tool.schema.array(tool.schema.string()).optional().describe("Paths to search (default: ['.'])"),
|
||||||
context: tool.schema.number().optional().describe("Context lines around match"),
|
globs: tool.schema.array(tool.schema.string()).optional().describe("Include/exclude globs (prefix ! to exclude)"),
|
||||||
},
|
context: tool.schema.number().optional().describe("Context lines around match"),
|
||||||
execute: async (args, context) => {
|
},
|
||||||
try {
|
execute: async (args, context) => {
|
||||||
const result = await runSg({
|
try {
|
||||||
pattern: args.pattern,
|
const result = await runSg({
|
||||||
lang: args.lang as CliLanguage,
|
pattern: args.pattern,
|
||||||
paths: args.paths,
|
lang: args.lang as CliLanguage,
|
||||||
globs: args.globs,
|
paths: args.paths ?? [ctx.directory],
|
||||||
context: args.context,
|
globs: args.globs,
|
||||||
})
|
context: args.context,
|
||||||
|
})
|
||||||
|
|
||||||
let output = formatSearchResult(result)
|
let output = formatSearchResult(result)
|
||||||
|
|
||||||
if (result.matches.length === 0 && !result.error) {
|
if (result.matches.length === 0 && !result.error) {
|
||||||
const hint = getEmptyResultHint(args.pattern, args.lang as CliLanguage)
|
const hint = getEmptyResultHint(args.pattern, args.lang as CliLanguage)
|
||||||
if (hint) {
|
if (hint) {
|
||||||
output += `\n\n${hint}`
|
output += `\n\n${hint}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await showOutputToUser(context, output)
|
||||||
|
return output
|
||||||
|
} catch (e) {
|
||||||
|
const output = `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
|
await showOutputToUser(context, output)
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
await showOutputToUser(context, output)
|
const ast_grep_replace: ToolDefinition = tool({
|
||||||
return output
|
description:
|
||||||
} catch (e) {
|
"Replace code patterns across filesystem with AST-aware rewriting. " +
|
||||||
const output = `Error: ${e instanceof Error ? e.message : String(e)}`
|
"Dry-run by default. Use meta-variables in rewrite to preserve matched content. " +
|
||||||
await showOutputToUser(context, output)
|
"Example: pattern='console.log($MSG)' rewrite='logger.info($MSG)'",
|
||||||
return output
|
args: {
|
||||||
}
|
pattern: tool.schema.string().describe("AST pattern to match"),
|
||||||
},
|
rewrite: tool.schema.string().describe("Replacement pattern (can use $VAR from pattern)"),
|
||||||
})
|
lang: tool.schema.enum(CLI_LANGUAGES).describe("Target language"),
|
||||||
|
paths: tool.schema.array(tool.schema.string()).optional().describe("Paths to search"),
|
||||||
export const ast_grep_replace: ToolDefinition = tool({
|
globs: tool.schema.array(tool.schema.string()).optional().describe("Include/exclude globs"),
|
||||||
description:
|
dryRun: tool.schema.boolean().optional().describe("Preview changes without applying (default: true)"),
|
||||||
"Replace code patterns across filesystem with AST-aware rewriting. " +
|
},
|
||||||
"Dry-run by default. Use meta-variables in rewrite to preserve matched content. " +
|
execute: async (args, context) => {
|
||||||
"Example: pattern='console.log($MSG)' rewrite='logger.info($MSG)'",
|
try {
|
||||||
args: {
|
const result = await runSg({
|
||||||
pattern: tool.schema.string().describe("AST pattern to match"),
|
pattern: args.pattern,
|
||||||
rewrite: tool.schema.string().describe("Replacement pattern (can use $VAR from pattern)"),
|
rewrite: args.rewrite,
|
||||||
lang: tool.schema.enum(CLI_LANGUAGES).describe("Target language"),
|
lang: args.lang as CliLanguage,
|
||||||
paths: tool.schema.array(tool.schema.string()).optional().describe("Paths to search"),
|
paths: args.paths ?? [ctx.directory],
|
||||||
globs: tool.schema.array(tool.schema.string()).optional().describe("Include/exclude globs"),
|
globs: args.globs,
|
||||||
dryRun: tool.schema.boolean().optional().describe("Preview changes without applying (default: true)"),
|
updateAll: args.dryRun === false,
|
||||||
},
|
})
|
||||||
execute: async (args, context) => {
|
const output = formatReplaceResult(result, args.dryRun !== false)
|
||||||
try {
|
await showOutputToUser(context, output)
|
||||||
const result = await runSg({
|
return output
|
||||||
pattern: args.pattern,
|
} catch (e) {
|
||||||
rewrite: args.rewrite,
|
const output = `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
lang: args.lang as CliLanguage,
|
await showOutputToUser(context, output)
|
||||||
paths: args.paths,
|
return output
|
||||||
globs: args.globs,
|
}
|
||||||
updateAll: args.dryRun === false,
|
},
|
||||||
})
|
})
|
||||||
const output = formatReplaceResult(result, args.dryRun !== false)
|
|
||||||
await showOutputToUser(context, output)
|
|
||||||
return output
|
|
||||||
} catch (e) {
|
|
||||||
const output = `Error: ${e instanceof Error ? e.message : String(e)}`
|
|
||||||
await showOutputToUser(context, output)
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
return { ast_grep_search, ast_grep_replace }
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1 @@
|
|||||||
import { glob } from "./tools"
|
export { createGlobTools } from "./tools"
|
||||||
|
|
||||||
export { glob }
|
|
||||||
|
|||||||
@ -1,43 +1,47 @@
|
|||||||
|
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> {
|
||||||
description:
|
const glob: ToolDefinition = tool({
|
||||||
"Fast file pattern matching tool with safety limits (60s timeout, 100 file limit). " +
|
description:
|
||||||
"Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\". " +
|
"Fast file pattern matching tool with safety limits (60s timeout, 100 file limit). " +
|
||||||
"Returns matching file paths sorted by modification time. " +
|
"Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\". " +
|
||||||
"Use this tool when you need to find files by name patterns.",
|
"Returns matching file paths sorted by modification time. " +
|
||||||
args: {
|
"Use this tool when you need to find files by name patterns.",
|
||||||
pattern: tool.schema.string().describe("The glob pattern to match files against"),
|
args: {
|
||||||
path: tool.schema
|
pattern: tool.schema.string().describe("The glob pattern to match files against"),
|
||||||
.string()
|
path: tool.schema
|
||||||
.optional()
|
.string()
|
||||||
.describe(
|
.optional()
|
||||||
"The directory to search in. If not specified, the current working directory will be used. " +
|
.describe(
|
||||||
"IMPORTANT: Omit this field to use the default directory. DO NOT enter \"undefined\" or \"null\" - " +
|
"The directory to search in. If not specified, the current working directory will be used. " +
|
||||||
"simply omit it for the default behavior. Must be a valid directory path if provided."
|
"IMPORTANT: Omit this field to use the default directory. DO NOT enter \"undefined\" or \"null\" - " +
|
||||||
),
|
"simply omit it for the default behavior. Must be a valid directory path if provided."
|
||||||
},
|
),
|
||||||
execute: async (args) => {
|
},
|
||||||
try {
|
execute: async (args) => {
|
||||||
const cli = await resolveGrepCliWithAutoInstall()
|
try {
|
||||||
// Use process.cwd() as the default search path when no path is provided
|
const cli = await resolveGrepCliWithAutoInstall()
|
||||||
const searchPath = args.path ?? process.cwd()
|
const searchPath = args.path ?? ctx.directory
|
||||||
const paths = [searchPath]
|
const paths = [searchPath]
|
||||||
|
|
||||||
const result = await runRgFiles(
|
const result = await runRgFiles(
|
||||||
{
|
{
|
||||||
pattern: args.pattern,
|
pattern: args.pattern,
|
||||||
paths,
|
paths,
|
||||||
},
|
},
|
||||||
cli
|
cli
|
||||||
)
|
)
|
||||||
|
|
||||||
return formatGlobResult(result)
|
return formatGlobResult(result)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { glob }
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1 @@
|
|||||||
import { grep } from "./tools"
|
export { createGrepTools } from "./tools"
|
||||||
|
|
||||||
export { grep }
|
|
||||||
|
|||||||
@ -1,42 +1,46 @@
|
|||||||
|
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> {
|
||||||
description:
|
const grep: ToolDefinition = tool({
|
||||||
"Fast content search tool with safety limits (60s timeout, 10MB output). " +
|
description:
|
||||||
"Searches file contents using regular expressions. " +
|
"Fast content search tool with safety limits (60s timeout, 10MB output). " +
|
||||||
"Supports full regex syntax (eg. \"log.*Error\", \"function\\s+\\w+\", etc.). " +
|
"Searches file contents using regular expressions. " +
|
||||||
"Filter files by pattern with the include parameter (eg. \"*.js\", \"*.{ts,tsx}\"). " +
|
"Supports full regex syntax (eg. \"log.*Error\", \"function\\s+\\w+\", etc.). " +
|
||||||
"Returns file paths with matches sorted by modification time.",
|
"Filter files by pattern with the include parameter (eg. \"*.js\", \"*.{ts,tsx}\"). " +
|
||||||
args: {
|
"Returns file paths with matches sorted by modification time.",
|
||||||
pattern: tool.schema.string().describe("The regex pattern to search for in file contents"),
|
args: {
|
||||||
include: tool.schema
|
pattern: tool.schema.string().describe("The regex pattern to search for in file contents"),
|
||||||
.string()
|
include: tool.schema
|
||||||
.optional()
|
.string()
|
||||||
.describe("File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\")"),
|
.optional()
|
||||||
path: tool.schema
|
.describe("File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\")"),
|
||||||
.string()
|
path: tool.schema
|
||||||
.optional()
|
.string()
|
||||||
.describe("The directory to search in. Defaults to the current working directory."),
|
.optional()
|
||||||
},
|
.describe("The directory to search in. Defaults to the current working directory."),
|
||||||
execute: async (args) => {
|
},
|
||||||
try {
|
execute: async (args) => {
|
||||||
const globs = args.include ? [args.include] : undefined
|
try {
|
||||||
// Use process.cwd() as the default search path when no path is provided
|
const globs = args.include ? [args.include] : undefined
|
||||||
const searchPath = args.path ?? process.cwd()
|
const searchPath = args.path ?? ctx.directory
|
||||||
const paths = [searchPath]
|
const paths = [searchPath]
|
||||||
|
|
||||||
const result = await runRg({
|
const result = await runRg({
|
||||||
pattern: args.pattern,
|
pattern: args.pattern,
|
||||||
paths,
|
paths,
|
||||||
globs,
|
globs,
|
||||||
context: 0,
|
context: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
return formatGrepResult(result)
|
return formatGrepResult(result)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { grep }
|
||||||
|
}
|
||||||
|
|||||||
@ -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,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export * from "./tools"
|
export { createSessionManagerTools } from "./tools"
|
||||||
export * from "./types"
|
export * from "./types"
|
||||||
export * from "./constants"
|
export * from "./constants"
|
||||||
|
|||||||
@ -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,121 +27,125 @@ 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> {
|
||||||
description: SESSION_LIST_DESCRIPTION,
|
const session_list: ToolDefinition = tool({
|
||||||
args: {
|
description: SESSION_LIST_DESCRIPTION,
|
||||||
limit: tool.schema.number().optional().describe("Maximum number of sessions to return"),
|
args: {
|
||||||
from_date: tool.schema.string().optional().describe("Filter sessions from this date (ISO 8601 format)"),
|
limit: tool.schema.number().optional().describe("Maximum number of sessions to return"),
|
||||||
to_date: tool.schema.string().optional().describe("Filter sessions until this date (ISO 8601 format)"),
|
from_date: tool.schema.string().optional().describe("Filter sessions from this date (ISO 8601 format)"),
|
||||||
project_path: tool.schema.string().optional().describe("Filter sessions by project path (default: current working directory)"),
|
to_date: tool.schema.string().optional().describe("Filter sessions until this date (ISO 8601 format)"),
|
||||||
},
|
project_path: tool.schema.string().optional().describe("Filter sessions by project path (default: current working directory)"),
|
||||||
execute: async (args: SessionListArgs, _context) => {
|
},
|
||||||
try {
|
execute: async (args: SessionListArgs, _context) => {
|
||||||
const directory = args.project_path ?? process.cwd()
|
try {
|
||||||
let sessions = await getMainSessions({ directory })
|
const directory = args.project_path ?? ctx.directory
|
||||||
let sessionIDs = sessions.map((s) => s.id)
|
let sessions = await getMainSessions({ directory })
|
||||||
|
let sessionIDs = sessions.map((s) => s.id)
|
||||||
|
|
||||||
if (args.from_date || args.to_date) {
|
if (args.from_date || args.to_date) {
|
||||||
sessionIDs = await filterSessionsByDate(sessionIDs, args.from_date, args.to_date)
|
sessionIDs = await filterSessionsByDate(sessionIDs, args.from_date, args.to_date)
|
||||||
}
|
|
||||||
|
|
||||||
if (args.limit && args.limit > 0) {
|
|
||||||
sessionIDs = sessionIDs.slice(0, args.limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return await formatSessionList(sessionIDs)
|
|
||||||
} catch (e) {
|
|
||||||
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const session_read: ToolDefinition = tool({
|
|
||||||
description: SESSION_READ_DESCRIPTION,
|
|
||||||
args: {
|
|
||||||
session_id: tool.schema.string().describe("Session ID to read"),
|
|
||||||
include_todos: tool.schema.boolean().optional().describe("Include todo list if available (default: false)"),
|
|
||||||
include_transcript: tool.schema.boolean().optional().describe("Include transcript log if available (default: false)"),
|
|
||||||
limit: tool.schema.number().optional().describe("Maximum number of messages to return (default: all)"),
|
|
||||||
},
|
|
||||||
execute: async (args: SessionReadArgs, _context) => {
|
|
||||||
try {
|
|
||||||
if (!sessionExists(args.session_id)) {
|
|
||||||
return `Session not found: ${args.session_id}`
|
|
||||||
}
|
|
||||||
|
|
||||||
let messages = await readSessionMessages(args.session_id)
|
|
||||||
|
|
||||||
if (args.limit && args.limit > 0) {
|
|
||||||
messages = messages.slice(0, args.limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const todos = args.include_todos ? await readSessionTodos(args.session_id) : undefined
|
|
||||||
|
|
||||||
return formatSessionMessages(messages, args.include_todos, todos)
|
|
||||||
} catch (e) {
|
|
||||||
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const session_search: ToolDefinition = tool({
|
|
||||||
description: SESSION_SEARCH_DESCRIPTION,
|
|
||||||
args: {
|
|
||||||
query: tool.schema.string().describe("Search query string"),
|
|
||||||
session_id: tool.schema.string().optional().describe("Search within specific session only (default: all sessions)"),
|
|
||||||
case_sensitive: tool.schema.boolean().optional().describe("Case-sensitive search (default: false)"),
|
|
||||||
limit: tool.schema.number().optional().describe("Maximum number of results to return (default: 20)"),
|
|
||||||
},
|
|
||||||
execute: async (args: SessionSearchArgs, _context) => {
|
|
||||||
try {
|
|
||||||
const resultLimit = args.limit && args.limit > 0 ? args.limit : 20
|
|
||||||
|
|
||||||
const searchOperation = async (): Promise<SearchResult[]> => {
|
|
||||||
if (args.session_id) {
|
|
||||||
return searchInSession(args.session_id, args.query, args.case_sensitive, resultLimit)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const allSessions = await getAllSessions()
|
if (args.limit && args.limit > 0) {
|
||||||
const sessionsToScan = allSessions.slice(0, MAX_SESSIONS_TO_SCAN)
|
sessionIDs = sessionIDs.slice(0, args.limit)
|
||||||
|
|
||||||
const allResults: SearchResult[] = []
|
|
||||||
for (const sid of sessionsToScan) {
|
|
||||||
if (allResults.length >= resultLimit) break
|
|
||||||
|
|
||||||
const remaining = resultLimit - allResults.length
|
|
||||||
const sessionResults = await searchInSession(sid, args.query, args.case_sensitive, remaining)
|
|
||||||
allResults.push(...sessionResults)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return allResults.slice(0, resultLimit)
|
return await formatSessionList(sessionIDs)
|
||||||
|
} catch (e) {
|
||||||
|
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const results = await withTimeout(searchOperation(), SEARCH_TIMEOUT_MS, "Search")
|
const session_read: ToolDefinition = tool({
|
||||||
|
description: SESSION_READ_DESCRIPTION,
|
||||||
|
args: {
|
||||||
|
session_id: tool.schema.string().describe("Session ID to read"),
|
||||||
|
include_todos: tool.schema.boolean().optional().describe("Include todo list if available (default: false)"),
|
||||||
|
include_transcript: tool.schema.boolean().optional().describe("Include transcript log if available (default: false)"),
|
||||||
|
limit: tool.schema.number().optional().describe("Maximum number of messages to return (default: all)"),
|
||||||
|
},
|
||||||
|
execute: async (args: SessionReadArgs, _context) => {
|
||||||
|
try {
|
||||||
|
if (!sessionExists(args.session_id)) {
|
||||||
|
return `Session not found: ${args.session_id}`
|
||||||
|
}
|
||||||
|
|
||||||
return formatSearchResults(results)
|
let messages = await readSessionMessages(args.session_id)
|
||||||
} catch (e) {
|
|
||||||
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const session_info: ToolDefinition = tool({
|
if (args.limit && args.limit > 0) {
|
||||||
description: SESSION_INFO_DESCRIPTION,
|
messages = messages.slice(0, args.limit)
|
||||||
args: {
|
}
|
||||||
session_id: tool.schema.string().describe("Session ID to inspect"),
|
|
||||||
},
|
|
||||||
execute: async (args: SessionInfoArgs, _context) => {
|
|
||||||
try {
|
|
||||||
const info = await getSessionInfo(args.session_id)
|
|
||||||
|
|
||||||
if (!info) {
|
const todos = args.include_todos ? await readSessionTodos(args.session_id) : undefined
|
||||||
return `Session not found: ${args.session_id}`
|
|
||||||
|
return formatSessionMessages(messages, args.include_todos, todos)
|
||||||
|
} catch (e) {
|
||||||
|
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
return formatSessionInfo(info)
|
const session_search: ToolDefinition = tool({
|
||||||
} catch (e) {
|
description: SESSION_SEARCH_DESCRIPTION,
|
||||||
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
args: {
|
||||||
}
|
query: tool.schema.string().describe("Search query string"),
|
||||||
},
|
session_id: tool.schema.string().optional().describe("Search within specific session only (default: all sessions)"),
|
||||||
})
|
case_sensitive: tool.schema.boolean().optional().describe("Case-sensitive search (default: false)"),
|
||||||
|
limit: tool.schema.number().optional().describe("Maximum number of results to return (default: 20)"),
|
||||||
|
},
|
||||||
|
execute: async (args: SessionSearchArgs, _context) => {
|
||||||
|
try {
|
||||||
|
const resultLimit = args.limit && args.limit > 0 ? args.limit : 20
|
||||||
|
|
||||||
|
const searchOperation = async (): Promise<SearchResult[]> => {
|
||||||
|
if (args.session_id) {
|
||||||
|
return searchInSession(args.session_id, args.query, args.case_sensitive, resultLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
const allSessions = await getAllSessions()
|
||||||
|
const sessionsToScan = allSessions.slice(0, MAX_SESSIONS_TO_SCAN)
|
||||||
|
|
||||||
|
const allResults: SearchResult[] = []
|
||||||
|
for (const sid of sessionsToScan) {
|
||||||
|
if (allResults.length >= resultLimit) break
|
||||||
|
|
||||||
|
const remaining = resultLimit - allResults.length
|
||||||
|
const sessionResults = await searchInSession(sid, args.query, args.case_sensitive, remaining)
|
||||||
|
allResults.push(...sessionResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allResults.slice(0, resultLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await withTimeout(searchOperation(), SEARCH_TIMEOUT_MS, "Search")
|
||||||
|
|
||||||
|
return formatSearchResults(results)
|
||||||
|
} catch (e) {
|
||||||
|
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const session_info: ToolDefinition = tool({
|
||||||
|
description: SESSION_INFO_DESCRIPTION,
|
||||||
|
args: {
|
||||||
|
session_id: tool.schema.string().describe("Session ID to inspect"),
|
||||||
|
},
|
||||||
|
execute: async (args: SessionInfoArgs, _context) => {
|
||||||
|
try {
|
||||||
|
const info = await getSessionInfo(args.session_id)
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
return `Session not found: ${args.session_id}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatSessionInfo(info)
|
||||||
|
} catch (e) {
|
||||||
|
return `Error: ${e instanceof Error ? e.message : String(e)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return { session_list, session_read, session_search, session_info }
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user