mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 13:53:15 +00:00
feat: Custom rules (#304)
This commit is contained in:
parent
3d7267914b
commit
7a281ad4c1
14 changed files with 318 additions and 62 deletions
|
|
@ -17,6 +17,7 @@ import { getMissingMessages } from "../../../../utils/github/issue-messages.js";
|
|||
import { filterHiddenMessages } from "../../../../utils/message/filter-hidden.js";
|
||||
import { getTaskPlanFromIssue } from "../../../../utils/github/issue-task.js";
|
||||
import { createRgTool } from "../../../../tools/rg.js";
|
||||
import { formatCustomRulesPrompt } from "../../../../utils/custom-rules.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "GeneratePlanningMessageNode");
|
||||
|
||||
|
|
@ -36,7 +37,8 @@ function formatSystemPrompt(state: PlannerGraphState): string {
|
|||
.replaceAll(
|
||||
"{CURRENT_WORKING_DIRECTORY}",
|
||||
getRepoAbsolutePath(state.targetRepository),
|
||||
);
|
||||
)
|
||||
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(state.customRules));
|
||||
}
|
||||
|
||||
export async function generateAction(
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ Generated via: \`git ls-files | tree --fromfile -L 3\`
|
|||
</codebase_tree>
|
||||
</workspace_information>
|
||||
|
||||
{CUSTOM_RULES}
|
||||
|
||||
<task_context>
|
||||
The user's request appears as the first message in the conversation below. Your context gathering should specifically target information needed to address this request effectively.
|
||||
</task_context>`;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { stopSandbox } from "../../../utils/sandbox.js";
|
||||
import { filterHiddenMessages } from "../../../utils/message/filter-hidden.js";
|
||||
import { z } from "zod";
|
||||
import { formatCustomRulesPrompt } from "../../../utils/custom-rules.js";
|
||||
|
||||
const systemPrompt = `You are a terminal-based agentic coding assistant built by LangChain, designed to enable natural language interaction with local codebases through wrapped LLM models.
|
||||
|
||||
|
|
@ -62,6 +63,8 @@ Structure your plan items as clear directives, for example:
|
|||
- "Modify the authentication middleware in /src/auth.js to add rate limiting using the Express rate-limit package"
|
||||
</output_format>
|
||||
|
||||
{CUSTOM_RULES}
|
||||
|
||||
Remember: Your goal is to create a focused, executable plan that efficiently accomplishes the user's request using the context you've already gathered.`;
|
||||
|
||||
function formatSystemPrompt(state: PlannerGraphState): string {
|
||||
|
|
@ -78,7 +81,8 @@ function formatSystemPrompt(state: PlannerGraphState): string {
|
|||
"\n\n"
|
||||
: "",
|
||||
)
|
||||
.replace("{USER_REQUEST}", userRequest);
|
||||
.replace("{USER_REQUEST}", userRequest)
|
||||
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(state.customRules));
|
||||
}
|
||||
|
||||
export async function generatePlan(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { z } from "zod";
|
||||
import { GraphConfig } from "@open-swe/shared/open-swe/types";
|
||||
import { CustomRules, GraphConfig } from "@open-swe/shared/open-swe/types";
|
||||
import {
|
||||
PlannerGraphState,
|
||||
PlannerGraphUpdate,
|
||||
|
|
@ -8,6 +8,7 @@ import { loadModel, Task } from "../../../utils/load-model.js";
|
|||
import { getMessageString } from "../../../utils/message/content.js";
|
||||
import { getUserRequest } from "../../../utils/user-request.js";
|
||||
import { BaseMessage } from "@langchain/core/messages";
|
||||
import { formatCustomRulesPrompt } from "../../../utils/custom-rules.js";
|
||||
|
||||
const systemPrompt = `You are operating as a terminal-based agentic coding assistant built by LangChain. It wraps LLM models to enable natural language interaction with a local codebase. You are expected to be precise, safe, and helpful.
|
||||
|
||||
|
|
@ -23,6 +24,8 @@ You MUST adhere to the following criteria when generating your notes:
|
|||
- Do not retain any full file contents.
|
||||
- Only take notes on the context provided below, and do not make up, or attempt to infer any information/context which is not explicitly provided.
|
||||
|
||||
{CUSTOM_RULES}
|
||||
|
||||
Here is the user's request
|
||||
## User request:
|
||||
{USER_REQUEST}
|
||||
|
|
@ -42,6 +45,7 @@ const formatPrompt = (
|
|||
userRequest: string,
|
||||
conversationHistory: BaseMessage[],
|
||||
proposedPlan: string[],
|
||||
customRules?: CustomRules,
|
||||
): string =>
|
||||
systemPrompt
|
||||
.replace("{USER_REQUEST}", userRequest)
|
||||
|
|
@ -49,7 +53,8 @@ const formatPrompt = (
|
|||
"{CONVERSATION_HISTORY}",
|
||||
conversationHistory.map(getMessageString).join("\n"),
|
||||
)
|
||||
.replace("{PROPOSED_PLAN}", ` - ${proposedPlan.join("\n - ")}`);
|
||||
.replace("{PROPOSED_PLAN}", ` - ${proposedPlan.join("\n - ")}`)
|
||||
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(customRules));
|
||||
|
||||
const condenseContextToolSchema = z.object({
|
||||
notes: z
|
||||
|
|
@ -85,6 +90,7 @@ ${state.messages.map(getMessageString).join("\n")}`;
|
|||
userRequest || "No user request provided.",
|
||||
state.messages,
|
||||
state.proposedPlan,
|
||||
state.customRules,
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { getMissingMessages } from "../../../../utils/github/issue-messages.js";
|
|||
import { getTaskPlanFromIssue } from "../../../../utils/github/issue-task.js";
|
||||
import { createRgTool } from "../../../../tools/rg.js";
|
||||
import { createInstallDependenciesTool } from "../../../../tools/install-dependencies.js";
|
||||
import { formatCustomRulesPrompt } from "../../../../utils/custom-rules.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "GenerateMessageNode");
|
||||
|
||||
|
|
@ -61,7 +62,8 @@ const formatPrompt = (state: GraphState): string => {
|
|||
!state.dependenciesInstalled
|
||||
? INSTALL_DEPENDENCIES_TOOL_PROMPT
|
||||
: DEPENDENCIES_INSTALLED_PROMPT,
|
||||
);
|
||||
)
|
||||
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(state.customRules));
|
||||
};
|
||||
|
||||
export async function generateAction(
|
||||
|
|
|
|||
|
|
@ -87,4 +87,6 @@ Generated via: \`git ls-files | tree --fromfile -L 3\`
|
|||
Location: {REPO_DIRECTORY}
|
||||
|
||||
{CODEBASE_TREE}
|
||||
</codebase_structure>`;
|
||||
</codebase_structure>
|
||||
|
||||
{CUSTOM_RULES}`;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {
|
|||
GraphConfig,
|
||||
PlanItem,
|
||||
GraphUpdate,
|
||||
CustomRules,
|
||||
} from "@open-swe/shared/open-swe/types";
|
||||
import { loadModel, Task } from "../../../utils/load-model.js";
|
||||
import { z } from "zod";
|
||||
|
|
@ -19,6 +20,7 @@ import { getMessageString } from "../../../utils/message/content.js";
|
|||
import { formatPlanPrompt } from "../../../utils/plan-prompt.js";
|
||||
import { createLogger, LogLevel } from "../../../utils/logger.js";
|
||||
import { createUpdatePlanToolFields } from "@open-swe/shared/open-swe/tools";
|
||||
import { formatCustomRulesPrompt } from "../../../utils/custom-rules.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "UpdatePlanNode");
|
||||
|
||||
|
|
@ -50,6 +52,8 @@ You MUST adhere to the following criteria when generating the plan:
|
|||
- You should call the \`update_plan\` tool, passing in each plan item in the order they should be executed in.
|
||||
- To remove an item from the plan, you should not include it in the \`update_plan\` tool call.
|
||||
|
||||
{CUSTOM_RULES}
|
||||
|
||||
With all of this in mind, please call the \`update_plan\` tool with the updated plan.
|
||||
`;
|
||||
|
||||
|
|
@ -72,11 +76,13 @@ const formatSystemPrompt = (
|
|||
userRequest: string,
|
||||
reasoning: string,
|
||||
planItems: PlanItem[],
|
||||
customRules?: CustomRules,
|
||||
) => {
|
||||
return systemPrompt
|
||||
.replace("{USER_REQUEST}", userRequest)
|
||||
.replace("{PLAN}", formatPlanPrompt(planItems, { includeSummaries: true }))
|
||||
.replace("{REASONING}", reasoning);
|
||||
.replace("{REASONING}", reasoning)
|
||||
.replaceAll("{CUSTOM_RULES}", formatCustomRulesPrompt(customRules));
|
||||
};
|
||||
|
||||
const formatUserMessage = (messages: BaseMessage[]): string => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { v4 as uuidv4 } from "uuid";
|
||||
import { getRepoAbsolutePath } from "@open-swe/shared/git";
|
||||
import { getGitHubTokensFromConfig } from "../../utils/github-tokens.js";
|
||||
import { GraphConfig, TargetRepository } from "@open-swe/shared/open-swe/types";
|
||||
import {
|
||||
CustomRules,
|
||||
GraphConfig,
|
||||
TargetRepository,
|
||||
} from "@open-swe/shared/open-swe/types";
|
||||
import { createLogger, LogLevel } from "../../utils/logger.js";
|
||||
import { daytonaClient } from "../../utils/sandbox.js";
|
||||
import {
|
||||
|
|
@ -19,6 +23,7 @@ import {
|
|||
import { Sandbox } from "@daytonaio/sdk";
|
||||
import { AIMessage, BaseMessage } from "@langchain/core/messages";
|
||||
import { DEFAULT_SANDBOX_CREATE_PARAMS } from "../../constants.js";
|
||||
import { getCustomRules } from "../../utils/custom-rules.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "InitializeSandbox");
|
||||
|
||||
|
|
@ -29,6 +34,7 @@ type InitializeSandboxState = {
|
|||
codebaseTree?: string;
|
||||
messages?: BaseMessage[];
|
||||
dependenciesInstalled?: boolean;
|
||||
customRules?: CustomRules;
|
||||
};
|
||||
|
||||
export async function initializeSandbox(
|
||||
|
|
@ -166,10 +172,12 @@ export async function initializeSandbox(
|
|||
try {
|
||||
const codebaseTree = await getCodebaseTree(existingSandbox.id);
|
||||
emitStepEvent(baseGenerateCodebaseTreeAction, "success");
|
||||
|
||||
return {
|
||||
sandboxSessionId: existingSandbox.id,
|
||||
codebaseTree,
|
||||
messages: createEventsMessage(),
|
||||
customRules: await getCustomRules(existingSandbox, absoluteRepoDir),
|
||||
};
|
||||
} catch {
|
||||
emitStepEvent(
|
||||
|
|
@ -332,5 +340,6 @@ export async function initializeSandbox(
|
|||
codebaseTree,
|
||||
messages: createEventsMessage(),
|
||||
dependenciesInstalled: false,
|
||||
customRules: await getCustomRules(sandbox, absoluteRepoDir),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
164
apps/open-swe/src/utils/custom-rules.ts
Normal file
164
apps/open-swe/src/utils/custom-rules.ts
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
import { CustomRules } from "@open-swe/shared/open-swe/types";
|
||||
import { Sandbox } from "@daytonaio/sdk";
|
||||
import { createLogger, LogLevel } from "./logger.js";
|
||||
import { getSandboxErrorFields } from "./sandbox-error-fields.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "CustomRules");
|
||||
|
||||
const GENERAL_RULES_OPEN_TAG = "<general_rules>";
|
||||
const GENERAL_RULES_CLOSE_TAG = "</general_rules>";
|
||||
const REPOSITORY_STRUCTURE_OPEN_TAG = "<repository_structure>";
|
||||
const REPOSITORY_STRUCTURE_CLOSE_TAG = "</repository_structure>";
|
||||
const DEPENDENCIES_AND_INSTALLATION_OPEN_TAG =
|
||||
"<dependencies_and_installation>";
|
||||
const DEPENDENCIES_AND_INSTALLATION_CLOSE_TAG =
|
||||
"</dependencies_and_installation>";
|
||||
const TESTING_INSTRUCTIONS_OPEN_TAG = "<testing_instructions>";
|
||||
const TESTING_INSTRUCTIONS_CLOSE_TAG = "</testing_instructions>";
|
||||
const ALL_TAGS = [
|
||||
GENERAL_RULES_OPEN_TAG,
|
||||
GENERAL_RULES_CLOSE_TAG,
|
||||
REPOSITORY_STRUCTURE_OPEN_TAG,
|
||||
REPOSITORY_STRUCTURE_CLOSE_TAG,
|
||||
DEPENDENCIES_AND_INSTALLATION_OPEN_TAG,
|
||||
DEPENDENCIES_AND_INSTALLATION_CLOSE_TAG,
|
||||
TESTING_INSTRUCTIONS_OPEN_TAG,
|
||||
TESTING_INSTRUCTIONS_CLOSE_TAG,
|
||||
];
|
||||
|
||||
export function parseCustomRulesFromString(contents: string): CustomRules {
|
||||
if (ALL_TAGS.every((tag) => !contents.includes(tag))) {
|
||||
// Text file has no custom rules. Return all as general rules
|
||||
return {
|
||||
generalRules: contents,
|
||||
};
|
||||
}
|
||||
let generalRules = "";
|
||||
let repositoryStructure = "";
|
||||
let dependenciesAndInstallation = "";
|
||||
let testingInstructions = "";
|
||||
|
||||
if (
|
||||
contents.includes(GENERAL_RULES_OPEN_TAG) &&
|
||||
contents.includes(GENERAL_RULES_CLOSE_TAG)
|
||||
) {
|
||||
generalRules = contents.substring(
|
||||
contents.indexOf(GENERAL_RULES_OPEN_TAG) + GENERAL_RULES_OPEN_TAG.length,
|
||||
contents.indexOf(GENERAL_RULES_CLOSE_TAG),
|
||||
);
|
||||
}
|
||||
if (
|
||||
contents.includes(REPOSITORY_STRUCTURE_OPEN_TAG) &&
|
||||
contents.includes(REPOSITORY_STRUCTURE_CLOSE_TAG)
|
||||
) {
|
||||
repositoryStructure = contents.substring(
|
||||
contents.indexOf(REPOSITORY_STRUCTURE_OPEN_TAG) +
|
||||
REPOSITORY_STRUCTURE_OPEN_TAG.length,
|
||||
contents.indexOf(REPOSITORY_STRUCTURE_CLOSE_TAG),
|
||||
);
|
||||
}
|
||||
if (
|
||||
contents.includes(DEPENDENCIES_AND_INSTALLATION_OPEN_TAG) &&
|
||||
contents.includes(DEPENDENCIES_AND_INSTALLATION_CLOSE_TAG)
|
||||
) {
|
||||
dependenciesAndInstallation = contents.substring(
|
||||
contents.indexOf(DEPENDENCIES_AND_INSTALLATION_OPEN_TAG) +
|
||||
DEPENDENCIES_AND_INSTALLATION_OPEN_TAG.length,
|
||||
contents.indexOf(DEPENDENCIES_AND_INSTALLATION_CLOSE_TAG),
|
||||
);
|
||||
}
|
||||
if (
|
||||
contents.includes(TESTING_INSTRUCTIONS_OPEN_TAG) &&
|
||||
contents.includes(TESTING_INSTRUCTIONS_CLOSE_TAG)
|
||||
) {
|
||||
testingInstructions = contents.substring(
|
||||
contents.indexOf(TESTING_INSTRUCTIONS_OPEN_TAG) +
|
||||
TESTING_INSTRUCTIONS_OPEN_TAG.length,
|
||||
contents.indexOf(TESTING_INSTRUCTIONS_CLOSE_TAG),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
generalRules,
|
||||
repositoryStructure,
|
||||
dependenciesAndInstallation,
|
||||
testingInstructions,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCustomRules(
|
||||
sandbox: Sandbox,
|
||||
rootDir: string,
|
||||
): Promise<CustomRules | undefined> {
|
||||
try {
|
||||
const catAgentsMdFileCommand = ["cat", "AGENTS.md"];
|
||||
const agentsMdRes = await sandbox.process.executeCommand(
|
||||
catAgentsMdFileCommand.join(" "),
|
||||
rootDir,
|
||||
);
|
||||
if (agentsMdRes.exitCode === 0 && agentsMdRes.result?.length > 0) {
|
||||
return parseCustomRulesFromString(agentsMdRes.result);
|
||||
}
|
||||
|
||||
const catAgentMdFileCommand = ["cat", "AGENT.md"];
|
||||
const catClaudeMdFileCommand = ["cat", "CLAUDE.md"];
|
||||
const catCursorMdFileCommand = ["cat", "CURSOR.md"];
|
||||
const [agentMdRes, claudeMdRes, cursorMdRes] = await Promise.all([
|
||||
sandbox.process.executeCommand(catAgentMdFileCommand.join(" "), rootDir),
|
||||
sandbox.process.executeCommand(catClaudeMdFileCommand.join(" "), rootDir),
|
||||
sandbox.process.executeCommand(catCursorMdFileCommand.join(" "), rootDir),
|
||||
]);
|
||||
if (agentMdRes.exitCode === 0 && agentMdRes.result?.length > 0) {
|
||||
return parseCustomRulesFromString(agentMdRes.result);
|
||||
}
|
||||
if (claudeMdRes.exitCode === 0 && claudeMdRes.result?.length > 0) {
|
||||
return parseCustomRulesFromString(claudeMdRes.result);
|
||||
}
|
||||
if (cursorMdRes.exitCode === 0 && cursorMdRes.result?.length > 0) {
|
||||
return parseCustomRulesFromString(cursorMdRes.result);
|
||||
}
|
||||
} catch (error) {
|
||||
const sandboxErrorFields = getSandboxErrorFields(error);
|
||||
logger.error("Failed to get custom rules", {
|
||||
...(sandboxErrorFields ? { ...sandboxErrorFields } : { error }),
|
||||
});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const CUSTOM_RULES_PROMPT = `<custom_rules>
|
||||
The following are custom rules provided by the user.
|
||||
{GENERAL_RULES}
|
||||
{REPOSITORY_STRUCTURE}
|
||||
{TESTING_INSTRUCTIONS}
|
||||
{DEPENDENCIES_AND_INSTALLATION}
|
||||
</custom_rules>`;
|
||||
|
||||
export function formatCustomRulesPrompt(customRules?: CustomRules): string {
|
||||
if (!customRules) return "";
|
||||
return CUSTOM_RULES_PROMPT.replace(
|
||||
"{GENERAL_RULES}",
|
||||
customRules.generalRules
|
||||
? `<general_rules>\n${customRules.generalRules}\n</general_rules>`
|
||||
: "",
|
||||
)
|
||||
.replace(
|
||||
"{REPOSITORY_STRUCTURE}",
|
||||
customRules.repositoryStructure
|
||||
? `<repository_structure>\n${customRules.repositoryStructure}\n</repository_structure>`
|
||||
: "",
|
||||
)
|
||||
.replace(
|
||||
"{TESTING_INSTRUCTIONS}",
|
||||
customRules.testingInstructions
|
||||
? `<testing_instructions>\n${customRules.testingInstructions}\n</testing_instructions>`
|
||||
: "",
|
||||
)
|
||||
.replace(
|
||||
"{DEPENDENCIES_AND_INSTALLATION}",
|
||||
customRules.dependenciesAndInstallation
|
||||
? `<dependencies_and_installation>\n${customRules.dependenciesAndInstallation}\n</dependencies_and_installation>`
|
||||
: "",
|
||||
);
|
||||
}
|
||||
|
|
@ -32,9 +32,10 @@ import {
|
|||
} from "../ui/tooltip";
|
||||
import { Label } from "../ui/label";
|
||||
import { ContentBlocksPreview } from "../thread/ContentBlocksPreview";
|
||||
import { TooltipIconButton } from "../ui/tooltip-icon-button";
|
||||
import { ThemeToggle } from "../theme-toggle";
|
||||
import { ThreadCard, ThreadCardLoading } from "./thread-card";
|
||||
import { QuickActions } from "./quick-actions";
|
||||
import { useState } from "react";
|
||||
|
||||
interface DefaultViewProps {
|
||||
threads: ThreadDisplayInfo[];
|
||||
|
|
@ -43,6 +44,7 @@ interface DefaultViewProps {
|
|||
|
||||
export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
||||
const router = useRouter();
|
||||
const [quickActionPrompt, setQuickActionPrompt] = useState("");
|
||||
const apiUrl: string | undefined = process.env.NEXT_PUBLIC_API_URL ?? "";
|
||||
const assistantId: string | undefined =
|
||||
process.env.NEXT_PUBLIC_MANAGER_ASSISTANT_ID ?? "";
|
||||
|
|
@ -115,6 +117,8 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
|||
contentBlocks={contentBlocks}
|
||||
setContentBlocks={setContentBlocks}
|
||||
onPaste={handlePaste}
|
||||
quickActionPrompt={quickActionPrompt}
|
||||
setQuickActionPrompt={setQuickActionPrompt}
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<TooltipProvider>
|
||||
|
|
@ -177,45 +181,7 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div>
|
||||
<h2 className="text-foreground mb-3 text-base font-semibold">
|
||||
Quick Actions
|
||||
</h2>
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
<Card className="border-border bg-card hover:bg-muted cursor-pointer py-3 transition-shadow hover:shadow-lg dark:bg-gray-950">
|
||||
<CardHeader className="px-3">
|
||||
<CardTitle className="text-foreground text-sm">
|
||||
Debug Code
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground text-xs">
|
||||
Find and fix issues in your codebase
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
<Card className="border-border bg-card hover:bg-muted cursor-pointer py-3 transition-shadow hover:shadow-lg dark:bg-gray-950">
|
||||
<CardHeader className="px-3">
|
||||
<CardTitle className="text-foreground text-sm">
|
||||
Add Feature
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground text-xs">
|
||||
Implement new functionality
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
<Card className="border-border bg-card hover:bg-muted cursor-pointer py-3 transition-shadow hover:shadow-lg dark:bg-gray-950">
|
||||
<CardHeader className="px-3">
|
||||
<CardTitle className="text-foreground text-sm">
|
||||
Refactor Code
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground text-xs">
|
||||
Improve code structure and performance
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<QuickActions setQuickActionPrompt={setQuickActionPrompt} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
54
apps/web/src/components/v2/quick-actions.tsx
Normal file
54
apps/web/src/components/v2/quick-actions.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { Dispatch, SetStateAction } from "react";
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "../ui/card";
|
||||
|
||||
const GENERATE_RULES_PROMPT = `You're given a task to write a collection of rules, context and guidelines on the repository you're provided. Please gather context on the following categories, then write an \`AGENTS.md\` file in the root of the repository.
|
||||
|
||||
- **General Rules**: These are general rules any developer/agent should follow when writing code. It should contain rules such as "When creating new XYZ functions, always first search in the XYZ/ directory to see if one exists, and if not, create it and place it in an existing or new file". Additionally, here is where you'd include context about scripts which are commonly executed, such as linter and formatter scripts.
|
||||
- **Repository Structure**: This section is where you'll include high level context about how the repository is laid out, any highly useful and not overly obvious information about how the directories/files are structured, different apps/packages/services inside the repository, etc. Do not include every last detail about the repository contents, only a high level technical overview on the repository structure.
|
||||
- **Dependencies and Installation**: This section should include high level context about how to install dependencies, where to install them, package managers, etc. Do not include overly verbose context in this section as most repositories typically have straightforward dependency management.
|
||||
- **Testing Instructions**: A general guide on testing in this repository. It should include context such as testing frameworks, roughly what types of modules should be tested, how to run tests, and any other context a developer wouldn't be able to infer by looking at test files on how to write & run tests. Do not include every last detail about testing in this section, only the most used/important context necessary to run tests.
|
||||
|
||||
Ensure each category is properly wrapped in opening and closing XML tags. The tags to use are as follows:
|
||||
<general_rules>
|
||||
</general_rules>
|
||||
<repository_structure>
|
||||
</repository_structure>
|
||||
<dependencies_and_installation>
|
||||
</dependencies_and_installation>
|
||||
<testing_instructions>
|
||||
</testing_instructions>
|
||||
|
||||
It is incredibly important that you ALWAYS wrap your sections in the opening and closing XML tags. Failure to do so will result in an invalid file and will not be accepted.
|
||||
|
||||
The file should not contain any other content besides those tags, and the rules/context/instructions they contain. Ensure your rules are thoughtful, concise and actually useful to a developer who's never contributed to the repository before. You can think of it as a more structured and directed \`CONTRIBUTING.md\` file.
|
||||
|
||||
With all of this in mind, please explore the repository and write this single \`AGENTS.md\` file with the rules/context/instructions gathered!`;
|
||||
|
||||
interface QuickActionsProps {
|
||||
setQuickActionPrompt: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export function QuickActions({ setQuickActionPrompt }: QuickActionsProps) {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-foreground mb-3 text-base font-semibold">
|
||||
Quick Actions
|
||||
</h2>
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
<Card
|
||||
onClick={() => setQuickActionPrompt(GENERATE_RULES_PROMPT)}
|
||||
className="border-border bg-card hover:bg-muted cursor-pointer py-3 transition-shadow hover:shadow-lg dark:bg-gray-950"
|
||||
>
|
||||
<CardHeader className="px-3">
|
||||
<CardTitle className="text-foreground text-sm">
|
||||
Generate Agent Rules
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground text-xs">
|
||||
Generate an AGENTS.md file for the repository.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import type React from "react";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Loader2, Send } from "lucide-react";
|
||||
import { RepositoryBranchSelectors } from "../github/repo-branch-selectors";
|
||||
|
|
@ -23,6 +23,8 @@ interface TerminalInputProps {
|
|||
contentBlocks: Base64ContentBlock[];
|
||||
setContentBlocks: Dispatch<SetStateAction<Base64ContentBlock[]>>;
|
||||
onPaste?: (e: React.ClipboardEvent<HTMLTextAreaElement>) => void;
|
||||
quickActionPrompt?: string;
|
||||
setQuickActionPrompt?: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export function TerminalInput({
|
||||
|
|
@ -33,6 +35,8 @@ export function TerminalInput({
|
|||
contentBlocks,
|
||||
setContentBlocks,
|
||||
onPaste,
|
||||
quickActionPrompt,
|
||||
setQuickActionPrompt,
|
||||
}: TerminalInputProps) {
|
||||
const { push } = useRouter();
|
||||
const [message, setMessage] = useState("");
|
||||
|
|
@ -113,6 +117,14 @@ export function TerminalInput({
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (quickActionPrompt && message !== quickActionPrompt) {
|
||||
setMessage(quickActionPrompt);
|
||||
// Clear quick action prompt
|
||||
setQuickActionPrompt?.("");
|
||||
}
|
||||
}, [quickActionPrompt]);
|
||||
|
||||
return (
|
||||
<div className="border-border bg-muted rounded-md border p-2 font-mono text-xs dark:bg-black">
|
||||
<div className="text-foreground flex items-start gap-1">
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
import "@langchain/langgraph/zod";
|
||||
import { z } from "zod";
|
||||
import { MessagesZodState } from "@langchain/langgraph";
|
||||
import { AgentSession, TargetRepository, TaskPlan } from "../types.js";
|
||||
import {
|
||||
AgentSession,
|
||||
CustomRules,
|
||||
TargetRepository,
|
||||
TaskPlan,
|
||||
} from "../types.js";
|
||||
import { withLangGraph } from "@langchain/langgraph/zod";
|
||||
|
||||
export const PlannerGraphStateObj = MessagesZodState.extend({
|
||||
sandboxSessionId: withLangGraph(z.custom<string>(), {
|
||||
sandboxSessionId: withLangGraph(z.string(), {
|
||||
reducer: {
|
||||
schema: z.custom<string>(),
|
||||
schema: z.string(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
|
|
@ -23,9 +28,9 @@ export const PlannerGraphStateObj = MessagesZodState.extend({
|
|||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
codebaseTree: withLangGraph(z.custom<string>(), {
|
||||
codebaseTree: withLangGraph(z.string(), {
|
||||
reducer: {
|
||||
schema: z.custom<string>(),
|
||||
schema: z.string(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
|
|
@ -42,22 +47,22 @@ export const PlannerGraphStateObj = MessagesZodState.extend({
|
|||
},
|
||||
default: (): string[] => [],
|
||||
}),
|
||||
contextGatheringNotes: withLangGraph(z.custom<string>(), {
|
||||
contextGatheringNotes: withLangGraph(z.string(), {
|
||||
reducer: {
|
||||
schema: z.custom<string>(),
|
||||
schema: z.string(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
default: () => "",
|
||||
}),
|
||||
branchName: withLangGraph(z.custom<string>(), {
|
||||
branchName: withLangGraph(z.string(), {
|
||||
reducer: {
|
||||
schema: z.custom<string>(),
|
||||
schema: z.string(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
planChangeRequest: withLangGraph(z.custom<string>(), {
|
||||
planChangeRequest: withLangGraph(z.string(), {
|
||||
reducer: {
|
||||
schema: z.custom<string>(),
|
||||
schema: z.string(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
|
|
@ -67,13 +72,19 @@ export const PlannerGraphStateObj = MessagesZodState.extend({
|
|||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
proposedPlanTitle: withLangGraph(z.custom<string>(), {
|
||||
proposedPlanTitle: withLangGraph(z.string(), {
|
||||
reducer: {
|
||||
schema: z.custom<string>(),
|
||||
schema: z.string(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
default: () => "",
|
||||
}),
|
||||
customRules: withLangGraph(z.custom<CustomRules>().optional(), {
|
||||
reducer: {
|
||||
schema: z.custom<CustomRules>().optional(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export type PlannerGraphState = z.infer<typeof PlannerGraphStateObj>;
|
||||
|
|
|
|||
|
|
@ -126,6 +126,13 @@ export type TargetRepository = {
|
|||
baseCommit?: string;
|
||||
};
|
||||
|
||||
export type CustomRules = {
|
||||
generalRules?: string;
|
||||
repositoryStructure?: string;
|
||||
dependenciesAndInstallation?: string;
|
||||
testingInstructions?: string;
|
||||
};
|
||||
|
||||
export const GraphAnnotation = MessagesZodState.extend({
|
||||
/**
|
||||
* The internal messages. These are the messages which are
|
||||
|
|
@ -219,6 +226,15 @@ export const GraphAnnotation = MessagesZodState.extend({
|
|||
},
|
||||
default: () => false,
|
||||
}),
|
||||
/**
|
||||
* User defined rules.
|
||||
*/
|
||||
customRules: withLangGraph(z.custom<CustomRules>().optional(), {
|
||||
reducer: {
|
||||
schema: z.custom<CustomRules>().optional(),
|
||||
fn: (_state, update) => update,
|
||||
},
|
||||
}),
|
||||
|
||||
// ---NOT USED---
|
||||
ui: z
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue