mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 10:23:14 +00:00
[open-swe] feat: Add find_instances_of tool for comprehensive string search (#350)
* Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * Apply patch * cr * cr * fix * cr --------- Co-authored-by: open-swe-dev[bot] <open-swe-dev@users.noreply.github.com> Co-authored-by: bracesproul <braceasproul@gmail.com>
This commit is contained in:
parent
23f72d0669
commit
77fe0e9a05
8 changed files with 434 additions and 19 deletions
|
|
@ -22,6 +22,7 @@ import { getTaskPlanFromIssue } from "../../../../utils/github/issue-task.js";
|
|||
import { createRgTool } from "../../../../tools/rg.js";
|
||||
import { formatCustomRulesPrompt } from "../../../../utils/custom-rules.js";
|
||||
import { createPlannerNotesTool } from "../../../../tools/planner-notes.js";
|
||||
import { createFindInstancesOfTool } from "../../../../tools/find-instances-of.js";
|
||||
import { getMcpTools } from "../../../../utils/mcp-client.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "GeneratePlanningMessageNode");
|
||||
|
|
@ -56,6 +57,7 @@ export async function generateAction(
|
|||
const tools = [
|
||||
createRgTool(state),
|
||||
createShellTool(state),
|
||||
createFindInstancesOfTool(state),
|
||||
createPlannerNotesTool(),
|
||||
createGetURLContentTool(),
|
||||
...mcpTools,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
getChangedFilesStatus,
|
||||
stashAndClearChanges,
|
||||
} from "../../../utils/github/git.js";
|
||||
import { createFindInstancesOfTool } from "../../../tools/find-instances-of.js";
|
||||
import { getRepoAbsolutePath } from "@open-swe/shared/git";
|
||||
import { daytonaClient } from "../../../utils/sandbox.js";
|
||||
import { createPlannerNotesTool } from "../../../tools/planner-notes.js";
|
||||
|
|
@ -40,8 +41,8 @@ export async function takeActions(
|
|||
const shellTool = createShellTool(state);
|
||||
const rgTool = createRgTool(state);
|
||||
const plannerNotesTool = createPlannerNotesTool();
|
||||
const findInstancesOfTool = createFindInstancesOfTool(state);
|
||||
const getURLContentTool = createGetURLContentTool();
|
||||
|
||||
const mcpTools = await getMcpTools(config);
|
||||
|
||||
const allTools = [
|
||||
|
|
@ -49,6 +50,7 @@ export async function takeActions(
|
|||
rgTool,
|
||||
plannerNotesTool,
|
||||
getURLContentTool,
|
||||
findInstancesOfTool,
|
||||
...mcpTools,
|
||||
];
|
||||
const toolsMap = Object.fromEntries(
|
||||
|
|
|
|||
154
apps/open-swe/src/tools/find-instances-of.ts
Normal file
154
apps/open-swe/src/tools/find-instances-of.ts
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
import { tool } from "@langchain/core/tools";
|
||||
import { Sandbox } from "@daytonaio/sdk";
|
||||
import { GraphState } from "@open-swe/shared/open-swe/types";
|
||||
import { getCurrentTaskInput } from "@langchain/langgraph";
|
||||
import { getSandboxErrorFields } from "../utils/sandbox-error-fields.js";
|
||||
import { createLogger, LogLevel } from "../utils/logger.js";
|
||||
import { daytonaClient } from "../utils/sandbox.js";
|
||||
import { TIMEOUT_SEC } from "@open-swe/shared/constants";
|
||||
import { createFindInstancesOfToolFields } from "@open-swe/shared/open-swe/tools";
|
||||
import { getRepoAbsolutePath } from "@open-swe/shared/git";
|
||||
import { z } from "zod";
|
||||
import { wrapScript } from "../utils/wrap-script.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "FindInstancesOfTool");
|
||||
|
||||
const DEFAULT_ENV = {
|
||||
// Prevents corepack from showing a y/n download prompt which causes the command to hang
|
||||
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
|
||||
};
|
||||
|
||||
export function createFindInstancesOfTool(
|
||||
state: Pick<GraphState, "sandboxSessionId" | "targetRepository">,
|
||||
) {
|
||||
const findInstancesOfFields = createFindInstancesOfToolFields(
|
||||
state.targetRepository,
|
||||
);
|
||||
const formatFindInstancesOfCommand = (
|
||||
input: z.infer<typeof findInstancesOfFields.schema>,
|
||||
): string[] => {
|
||||
const args = ["rg"];
|
||||
|
||||
// Always include these flags for consistent output
|
||||
args.push("--color", "never", "--line-number", "--heading");
|
||||
|
||||
// Add context lines (3 above and 3 below)
|
||||
args.push("-A", "3", "-B", "3");
|
||||
|
||||
// Handle case sensitivity
|
||||
if (!input.case_sensitive) {
|
||||
args.push("-i");
|
||||
}
|
||||
|
||||
// Handle word matching
|
||||
if (input.match_word) {
|
||||
args.push("--word-regexp");
|
||||
}
|
||||
|
||||
// Handle file inclusion/exclusion patterns
|
||||
if (input.exclude_files) {
|
||||
args.push("-g", `!${input.exclude_files}`);
|
||||
}
|
||||
|
||||
if (input.include_files) {
|
||||
args.push("-g", input.include_files);
|
||||
}
|
||||
|
||||
// For literal string matching (not regex)
|
||||
args.push("--fixed-strings");
|
||||
|
||||
// Add the search query as the last argument (ensure it's properly quoted)
|
||||
const formattedQuery = `'${input.query.replace(/^'|'$/g, "")}'`;
|
||||
args.push(formattedQuery);
|
||||
|
||||
return args;
|
||||
};
|
||||
|
||||
const findInstancesOfTool = tool(
|
||||
async (
|
||||
input: z.infer<typeof findInstancesOfFields.schema>,
|
||||
): Promise<{ result: string; status: "success" | "error" }> => {
|
||||
let sandbox: Sandbox | undefined;
|
||||
try {
|
||||
const state = getCurrentTaskInput<GraphState>();
|
||||
const { sandboxSessionId } = state;
|
||||
if (!sandboxSessionId) {
|
||||
logger.error(
|
||||
"FAILED TO RUN COMMAND: No sandbox session ID provided",
|
||||
{
|
||||
input,
|
||||
},
|
||||
);
|
||||
throw new Error(
|
||||
"FAILED TO RUN COMMAND: No sandbox session ID provided",
|
||||
);
|
||||
}
|
||||
|
||||
const repoRoot = getRepoAbsolutePath(state.targetRepository);
|
||||
|
||||
sandbox = await daytonaClient().get(sandboxSessionId);
|
||||
const command = formatFindInstancesOfCommand(input);
|
||||
logger.info("Running find_instances_of command", {
|
||||
command: command.join(" "),
|
||||
repoRoot,
|
||||
});
|
||||
|
||||
const response = await sandbox.process.executeCommand(
|
||||
wrapScript(command.join(" ")),
|
||||
repoRoot,
|
||||
DEFAULT_ENV,
|
||||
TIMEOUT_SEC,
|
||||
);
|
||||
|
||||
let successResult = response.result;
|
||||
|
||||
if (
|
||||
response.exitCode === 1 ||
|
||||
(response.exitCode === 127 && response.result.startsWith("sh: 1: "))
|
||||
) {
|
||||
logger.info("Exit code 1. no results found", {
|
||||
...response,
|
||||
});
|
||||
successResult = `Exit code 1. No results found.\n\n${response.result}`;
|
||||
} else if (response.exitCode > 1) {
|
||||
logger.error("Failed to run find_instances_of command", {
|
||||
error: response.result,
|
||||
error_result: response,
|
||||
input,
|
||||
});
|
||||
throw new Error("Command failed. Exit code: " + response.exitCode);
|
||||
}
|
||||
|
||||
return {
|
||||
result: successResult,
|
||||
status: "success",
|
||||
};
|
||||
} catch (e) {
|
||||
const errorFields = getSandboxErrorFields(e);
|
||||
if (errorFields) {
|
||||
logger.error("Failed to run find_instances_of command", {
|
||||
input,
|
||||
error: errorFields,
|
||||
});
|
||||
throw new Error("Command failed. Exit code: " + errorFields.exitCode);
|
||||
}
|
||||
|
||||
logger.error(
|
||||
"Failed to run find_instances_of command: " +
|
||||
(e instanceof Error ? e.message : "Unknown error"),
|
||||
{
|
||||
error: e,
|
||||
input,
|
||||
},
|
||||
);
|
||||
throw new Error(
|
||||
"FAILED TO RUN FIND_INSTANCES_OF COMMAND: " +
|
||||
(e instanceof Error ? e.message : "Unknown error"),
|
||||
);
|
||||
}
|
||||
},
|
||||
findInstancesOfFields,
|
||||
);
|
||||
|
||||
return findInstancesOfTool;
|
||||
}
|
||||
|
|
@ -11,13 +11,7 @@ import {
|
|||
formatRgCommand,
|
||||
} from "@open-swe/shared/open-swe/tools";
|
||||
import { getRepoAbsolutePath } from "@open-swe/shared/git";
|
||||
|
||||
const wrapScript = (command: string): string => {
|
||||
return `script --return --quiet -c "$(cat <<'OPEN_SWE_X'
|
||||
${command}
|
||||
OPEN_SWE_X
|
||||
)" /dev/null`;
|
||||
};
|
||||
import { wrapScript } from "../utils/wrap-script.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "RgTool");
|
||||
|
||||
|
|
|
|||
6
apps/open-swe/src/utils/wrap-script.ts
Normal file
6
apps/open-swe/src/utils/wrap-script.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export function wrapScript(command: string): string {
|
||||
return `script --return --quiet -c "$(cat <<'OPEN_SWE_X'
|
||||
${command}
|
||||
OPEN_SWE_X
|
||||
)" /dev/null`;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { JSX, useState } from "react";
|
||||
import {
|
||||
Terminal,
|
||||
FileCode,
|
||||
|
|
@ -14,6 +14,7 @@ import {
|
|||
CloudDownload,
|
||||
Search,
|
||||
Globe,
|
||||
Hash,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
createApplyPatchToolFields,
|
||||
|
|
@ -21,10 +22,18 @@ import {
|
|||
createInstallDependenciesToolFields,
|
||||
createTakePlannerNotesFields,
|
||||
createGetURLContentToolFields,
|
||||
createFindInstancesOfToolFields,
|
||||
formatRgCommand,
|
||||
RipgrepCommand,
|
||||
} from "@open-swe/shared/open-swe/tools";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "../ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// Used only for Zod type inference.
|
||||
const dummyRepo = { owner: "dummy", repo: "dummy" };
|
||||
|
|
@ -40,6 +49,8 @@ const plannerNotesTool = createTakePlannerNotesFields();
|
|||
type PlannerNotesToolArgs = z.infer<typeof plannerNotesTool.schema>;
|
||||
const getURLContentTool = createGetURLContentToolFields();
|
||||
type GetURLContentToolArgs = z.infer<typeof getURLContentTool.schema>;
|
||||
const findInstancesOfTool = createFindInstancesOfToolFields(dummyRepo);
|
||||
type FindInstancesOfToolArgs = z.infer<typeof findInstancesOfTool.schema>;
|
||||
|
||||
// Common props for all action types
|
||||
type BaseActionProps = {
|
||||
|
|
@ -91,6 +102,13 @@ type GetURLContentActionProps = BaseActionProps &
|
|||
output?: string;
|
||||
};
|
||||
|
||||
type FindInstancesOfActionProps = BaseActionProps &
|
||||
Partial<FindInstancesOfToolArgs> & {
|
||||
actionType: "find_instances_of";
|
||||
output?: string;
|
||||
errorCode?: number;
|
||||
};
|
||||
|
||||
export type ActionItemProps =
|
||||
| (BaseActionProps & { status: "loading" })
|
||||
| ShellActionProps
|
||||
|
|
@ -98,7 +116,8 @@ export type ActionItemProps =
|
|||
| RgActionProps
|
||||
| InstallDependenciesActionProps
|
||||
| PlannerNotesActionProps
|
||||
| GetURLContentActionProps;
|
||||
| GetURLContentActionProps
|
||||
| FindInstancesOfActionProps;
|
||||
|
||||
export type ActionStepProps = {
|
||||
actions: ActionItemProps[];
|
||||
|
|
@ -113,8 +132,88 @@ const ACTION_GENERATING_TEXT_MAP = {
|
|||
[installDependenciesTool.name]: "Installing dependencies...",
|
||||
[plannerNotesTool.name]: "Saving notes...",
|
||||
[getURLContentTool.name]: "Fetching URL content...",
|
||||
[findInstancesOfTool.name]: "Finding instances...",
|
||||
};
|
||||
|
||||
function ToolIconWithTooltip({
|
||||
toolNamePretty,
|
||||
icon,
|
||||
}: {
|
||||
toolNamePretty: string;
|
||||
icon: JSX.Element;
|
||||
}) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{icon}</TooltipTrigger>
|
||||
<TooltipContent>{toolNamePretty}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function MatchCaseIcon({ matchCase }: { matchCase: boolean }) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
className={cn(
|
||||
"rounded-sm border border-gray-300 px-1 py-[2px] text-xs dark:border-gray-600",
|
||||
matchCase
|
||||
? "border-blue-500 bg-blue-500/80 text-white"
|
||||
: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200",
|
||||
)}
|
||||
>
|
||||
<p className="font-mono">Aa</p>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Match case {matchCase ? "on" : "off"}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function MatchWholeWordIcon({ matchWholeWord }: { matchWholeWord: boolean }) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
className={cn(
|
||||
"rounded-sm border border-gray-300 px-1 py-[2px] text-xs dark:border-gray-600",
|
||||
matchWholeWord
|
||||
? "border-blue-500 bg-blue-500/80 text-white"
|
||||
: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200",
|
||||
)}
|
||||
>
|
||||
<span className="relative inline-block px-0.5">
|
||||
<p className="relative z-10 m-0 text-center font-mono">ab</p>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute bottom-0 left-0 h-1/5 w-[1px]",
|
||||
matchWholeWord ? "bg-white" : "bg-gray-600 dark:bg-gray-300",
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute right-0 bottom-0 left-0 h-[1px] w-full",
|
||||
matchWholeWord ? "bg-white" : "bg-gray-600 dark:bg-gray-300",
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute right-0 bottom-0 h-1/5 w-[1px]",
|
||||
matchWholeWord ? "bg-white" : "bg-gray-600 dark:bg-gray-300",
|
||||
)}
|
||||
></div>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Match whole word {matchWholeWord ? "on" : "off"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function ActionItem(props: ActionItemProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
|
|
@ -159,6 +258,8 @@ function ActionItem(props: ActionItemProps) {
|
|||
return props.success
|
||||
? "URL content fetched"
|
||||
: "Failed to fetch URL content";
|
||||
} else if (props.actionType === "find_instances_of") {
|
||||
return props.success ? "Search completed" : "Search failed";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +274,8 @@ function ActionItem(props: ActionItemProps) {
|
|||
props.actionType === "shell" ||
|
||||
props.actionType === "rg" ||
|
||||
props.actionType === "install_dependencies" ||
|
||||
props.actionType === "get_url_content"
|
||||
props.actionType === "get_url_content" ||
|
||||
props.actionType === "find_instances_of"
|
||||
) {
|
||||
return !!props.output;
|
||||
} else if (props.actionType === "apply-patch") {
|
||||
|
|
@ -187,23 +289,66 @@ function ActionItem(props: ActionItemProps) {
|
|||
|
||||
// Render the header icon based on action type
|
||||
const renderHeaderIcon = () => {
|
||||
const defaultIconStyling = "text-muted-foreground mr-2 size-3.5";
|
||||
if (props.status === "loading" || !("actionType" in props)) {
|
||||
// In loading state, we don't know the type yet, use a generic icon
|
||||
return <Loader2 className="text-muted-foreground mr-2 size-3.5" />;
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Loading"
|
||||
icon={<Loader2 className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.actionType === "planner_notes") {
|
||||
return <FileText className="text-muted-foreground mr-2 size-3.5" />;
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Planner Notes"
|
||||
icon={<FileText className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
} else if (props.actionType === "install_dependencies") {
|
||||
return <CloudDownload className="text-muted-foreground mr-2 size-3.5" />;
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Install Dependencies"
|
||||
icon={<CloudDownload className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
} else if (props.actionType === "apply-patch") {
|
||||
return <FileCode className="text-muted-foreground mr-2 size-3.5" />;
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Apply Patch"
|
||||
icon={<FileCode className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
} else if (props.actionType === "rg") {
|
||||
return <Search className="text-muted-foreground mr-2 size-3.5" />;
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Ripgrep (rg)"
|
||||
icon={<Search className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
} else if (props.actionType === "get_url_content") {
|
||||
return <Globe className="text-muted-foreground mr-2 size-3.5" />;
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Get URL Contents"
|
||||
icon={<Globe className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
} else if (props.actionType === "find_instances_of") {
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Find Instances of"
|
||||
icon={<Hash className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <Terminal className="text-muted-foreground mr-2 size-3.5" />;
|
||||
return (
|
||||
<ToolIconWithTooltip
|
||||
toolNamePretty="Tool Call"
|
||||
icon={<Terminal className={cn(defaultIconStyling)} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -237,6 +382,32 @@ function ActionItem(props: ActionItemProps) {
|
|||
);
|
||||
}
|
||||
|
||||
if (props.actionType === "find_instances_of") {
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="text-foreground/80 text-xs font-normal">
|
||||
{props.query}
|
||||
</code>
|
||||
<div className="bg-border h-4 w-[1px] dark:bg-white"></div>
|
||||
<MatchCaseIcon matchCase={!!props.case_sensitive} />
|
||||
<MatchWholeWordIcon matchWholeWord={!!props.match_word} />
|
||||
</div>
|
||||
{(props.include_files || props.exclude_files) && (
|
||||
<div className="text-muted-foreground mt-0.5 text-xs font-normal">
|
||||
{props.include_files && (
|
||||
<span>Include: {props.include_files}</span>
|
||||
)}
|
||||
{props.include_files && props.exclude_files && <span> | </span>}
|
||||
{props.exclude_files && (
|
||||
<span>Exclude: {props.exclude_files}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
props.actionType === "shell" ||
|
||||
props.actionType === "install_dependencies"
|
||||
|
|
@ -305,6 +476,7 @@ function ActionItem(props: ActionItemProps) {
|
|||
if (
|
||||
(props.actionType === "shell" ||
|
||||
props.actionType === "rg" ||
|
||||
props.actionType === "find_instances_of" ||
|
||||
props.actionType === "install_dependencies") &&
|
||||
props.output
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
createTakePlannerNotesFields,
|
||||
createDiagnoseErrorToolFields,
|
||||
createGetURLContentToolFields,
|
||||
createFindInstancesOfToolFields,
|
||||
} from "@open-swe/shared/open-swe/tools";
|
||||
import { z } from "zod";
|
||||
import { isAIMessageSDK, isToolMessageSDK } from "@/lib/langchain-messages";
|
||||
|
|
@ -63,6 +64,9 @@ type DiagnoseErrorToolArgs = z.infer<typeof diagnoseErrorTool.schema>;
|
|||
const getURLContentTool = createGetURLContentToolFields();
|
||||
type GetURLContentToolArgs = z.infer<typeof getURLContentTool.schema>;
|
||||
|
||||
const findInstancesOfTool = createFindInstancesOfToolFields(dummyRepo);
|
||||
type FindInstancesOfToolArgs = z.infer<typeof findInstancesOfTool.schema>;
|
||||
|
||||
function CustomComponent({
|
||||
message,
|
||||
thread,
|
||||
|
|
@ -199,6 +203,24 @@ export function mapToolMessageToActionStepProps(
|
|||
output: getContentString(message.content),
|
||||
reasoningText,
|
||||
};
|
||||
} else if (toolCall?.name === findInstancesOfTool.name) {
|
||||
const args = toolCall.args as FindInstancesOfToolArgs;
|
||||
// case_sensitive and match_word both default to true.
|
||||
const caseSensitive =
|
||||
args.case_sensitive === undefined ? true : args.case_sensitive;
|
||||
const matchWord = args.match_word === undefined ? true : args.match_word;
|
||||
return {
|
||||
actionType: "find_instances_of",
|
||||
status,
|
||||
success,
|
||||
query: args.query || "",
|
||||
case_sensitive: caseSensitive,
|
||||
match_word: matchWord,
|
||||
include_files: args.include_files,
|
||||
exclude_files: args.exclude_files,
|
||||
output: getContentString(message.content),
|
||||
reasoningText,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: "loading",
|
||||
|
|
@ -264,7 +286,8 @@ export function AssistantMessage({
|
|||
tc.name === rgTool.name ||
|
||||
tc.name === installDependenciesTool.name ||
|
||||
tc.name === plannerNotesTool.name ||
|
||||
tc.name === getURLContentTool.name,
|
||||
tc.name === getURLContentTool.name ||
|
||||
tc.name === findInstancesOfTool.name,
|
||||
)
|
||||
: [];
|
||||
|
||||
|
|
@ -420,6 +443,23 @@ export function AssistantMessage({
|
|||
url: args?.url || "",
|
||||
output: "",
|
||||
} as ActionItemProps;
|
||||
} else if (toolCall.name === findInstancesOfTool.name) {
|
||||
const args = toolCall.args as FindInstancesOfToolArgs;
|
||||
// case_sensitive and match_word both default to true.
|
||||
const caseSensitive =
|
||||
args.case_sensitive === undefined ? true : args.case_sensitive;
|
||||
const matchWord =
|
||||
args.match_word === undefined ? true : args.match_word;
|
||||
return {
|
||||
actionType: "find_instances_of",
|
||||
status: "generating",
|
||||
query: args?.query || "",
|
||||
case_sensitive: caseSensitive,
|
||||
match_word: matchWord,
|
||||
include_files: args?.include_files,
|
||||
exclude_files: args?.exclude_files,
|
||||
output: "",
|
||||
} as ActionItemProps;
|
||||
} else {
|
||||
if (isShellTool) {
|
||||
const args = toolCall.args as ShellToolArgs;
|
||||
|
|
|
|||
|
|
@ -180,6 +180,51 @@ export function formatRgCommand(cmd: RipgrepCommand): string[] {
|
|||
return args;
|
||||
}
|
||||
|
||||
export function createFindInstancesOfToolFields(
|
||||
targetRepository: TargetRepository,
|
||||
) {
|
||||
const repoRoot = getRepoAbsolutePath(targetRepository);
|
||||
const findInstancesOfSchema = z.object({
|
||||
query: z
|
||||
.string()
|
||||
.describe(
|
||||
"The query/keyword to search for. This should be a literal string, not a regex.",
|
||||
),
|
||||
|
||||
case_sensitive: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(true)
|
||||
.describe(
|
||||
"Whether or not to make the query search case sensitive. Defaults to true",
|
||||
),
|
||||
|
||||
match_word: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(true)
|
||||
.describe(
|
||||
"Whether or not to only show results which match the exact keyword. Defaults to true",
|
||||
),
|
||||
|
||||
exclude_files: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Glob pattern of files to exclude"),
|
||||
|
||||
include_files: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Glob pattern of files to include"),
|
||||
});
|
||||
|
||||
return {
|
||||
name: "find_instances_of",
|
||||
schema: findInstancesOfSchema,
|
||||
description: `Find all instances of a string in the repository. Returns results with 3 lines of context above and below each match, absolute file paths, and total result count. The working directory this command will be executed in is \`${repoRoot}\`.`,
|
||||
};
|
||||
}
|
||||
|
||||
export function createSetTaskStatusToolFields() {
|
||||
const setTaskStatusToolSchema = z.object({
|
||||
reasoning: z
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue