diff --git a/agents/__tests__/base3.test.ts b/agents/__tests__/base3.test.ts index b72d425b42..417753c159 100644 --- a/agents/__tests__/base3.test.ts +++ b/agents/__tests__/base3.test.ts @@ -181,8 +181,7 @@ describe('base3 CLI roots', () => { test('brands Freebuff roots as Freebuff, and Codebuff roots as Codebuff', () => { expect(base3FreeDeepseek.systemPrompt).toContain('Freebuff') - expect(base3FreeDeepseek.systemPrompt).not.toContain('/usage') - // Codebuff's paid modes explain credits; Freebuff has none to explain. - expect(base3.systemPrompt).toContain('/usage') + expect(base3FreeDeepseek.systemPrompt).not.toContain('You are Codebuff.') + expect(base3.systemPrompt).toContain('You are Codebuff.') }) }) diff --git a/agents/base3.ts b/agents/base3.ts index 73005a7c6e..abb352ccff 100644 --- a/agents/base3.ts +++ b/agents/base3.ts @@ -1,11 +1,8 @@ import { compactionPolicyForModel } from '@codebuff/common/constants/compaction-policy' import { - FOLLOWUP_STYLE_GUIDANCE, - gravityIndexGuidance, OPUS_MODEL, publisher, - SKILL_DISCOVERY_GUIDANCE, } from './constants' import { PLACEHOLDER, @@ -48,15 +45,10 @@ export function createBase3( 'write_todos', ], - systemPrompt: `You are Buffy, the coding agent behind Codebuff. You help users with software engineering tasks: fixing bugs, adding functionality, refactoring, and explaining code. + systemPrompt: `You are Buffy, the coding agent behind Codebuff. Current date: ${PLACEHOLDER.CURRENT_DATE}. -- Match the project's existing conventions. Verify a library is already used in the project before employing it. -- Prefer editing existing files over creating new ones. Make the fewest changes that address the request. -- Verify non-trivial changes by running the project's typecheck and relevant tests. -- Use write_todos to plan and track multi-step tasks. -- Your responses are displayed in a terminal. Keep them short and concise. - Don't run destructive or hard-to-undo commands (git push, resets, deploys) unless the user asks for them. ${PLACEHOLDER.KNOWLEDGE_FILES_CONTENTS} @@ -128,7 +120,7 @@ export function createBase3CliRoot( 'skill', ], systemPrompt: `${base3.systemPrompt} -${buildCliAppendix({ isFreebuff, model, noAskUser })}`, +${buildCliAppendix({ isFreebuff, noAskUser })}`, } if (!noAskUser) return root @@ -146,39 +138,21 @@ const HUMAN_TOOL_NAMES: ReadonlySet = new Set([ function buildCliAppendix({ isFreebuff, - model, noAskUser = false, }: { isFreebuff: boolean - model: SecretAgentDefinition['model'] noAskUser?: boolean }): string { return ` -# Working with the user ${ noAskUser ? '' : ` -- **Ask about important decisions:** Use the ask_user tool to collaborate with the user on non-obvious choices — alternate implementation strategies, ambiguous requirements. Gather context first, and skip it when the answer is obvious or the detail can be changed later. -- **Suggest next steps:** At the end of your turn, use the suggest_followups tool to suggest ~3 next steps the user might want to take. ${FOLLOWUP_STYLE_GUIDANCE}` +- Use ask_user when an important decision needs the user. +- Use suggest_followups at the end of your turn to suggest next steps.` } -${gravityIndexGuidance()} -${SKILL_DISCOVERY_GUIDANCE} -# ${isFreebuff ? 'Freebuff' : 'Codebuff'} Meta-information - -You are running on the ${model} model. - -${ - isFreebuff - ? 'You are the AI agent behind Freebuff, a tool where users can chat with you to code with AI for free. See freebuff.com for more information about the product.' - : [ - 'Users send prompts to you in one of a few user-selected modes, like DEFAULT, LITE, MAX, or PLAN.', - "Every prompt sent consumes the user's credits, which is calculated based on the API cost of the models used.", - 'The user can use the "/usage" command to see how many credits they have used and have left, so you can tell them to check their usage this way.', - 'For other questions, you can direct them to codebuff.com, or especially codebuff.com/docs for detailed information about the product.', - ].join('\n') -} +You are ${isFreebuff ? 'Freebuff' : 'Codebuff'}. ${PLACEHOLDER.SYSTEM_INFO_PROMPT} ` diff --git a/common/src/tools/params/tool/__tests__/run-terminal-command-attribution.test.ts b/common/src/tools/params/tool/__tests__/run-terminal-command-attribution.test.ts index 4cb886bea6..4dc25c3a4d 100644 --- a/common/src/tools/params/tool/__tests__/run-terminal-command-attribution.test.ts +++ b/common/src/tools/params/tool/__tests__/run-terminal-command-attribution.test.ts @@ -87,11 +87,12 @@ describe('run_terminal_command commit attribution', () => { // Everything a normal run relies on is still there: the two variants differ // in the step-4 block and the second example and nowhere else. for (const shared of [ - 'Stick to these use cases:', - 'DO NOT do any of the following:', + 'Commands run in bash on every OS', '### Using git to commit changes', - 'Never alter the git config.', - 'Do not create an empty commit if there are no changes.', + "Don't push", + "never alter git config", + "don't use interactive flags", + "don't create empty commits", String.raw`echo \"hello world\"`, ]) { expect(runTerminalCommandParams.description).toContain(shared) diff --git a/common/src/tools/params/tool/ask-user.ts b/common/src/tools/params/tool/ask-user.ts index 56948e4364..99d5f0c3e1 100644 --- a/common/src/tools/params/tool/ask-user.ts +++ b/common/src/tools/params/tool/ask-user.ts @@ -110,17 +110,11 @@ const outputSchema = z.object({ }) const description = ` -Ask the user multiple choice questions and pause execution until they respond. Supports both single-select (radio) and multi-select (checkbox) modes. +Ask the user multiple choice questions. Execution pauses until they respond. -The user can either: -- Select one option (single-select mode, default) -- Select multiple options (multi-select mode, set multiSelect: true) -- Type a custom answer in the "Other" text field -- Skip the questions to provide different instructions instead +IMPORTANT: Do NOT include options like "Custom", "Other", "None of the above", or similar catch-all options. The UI already provides a "Custom" text field. -IMPORTANT: Do NOT include options like "Custom", "Other", "None of the above", or similar catch-all options. The UI automatically provides a "Custom" text input field for users to type their own answer. Including such options would be redundant and confusing. - -Single-select example: +Example: ${$getNativeToolCallExampleString({ toolName, inputSchema, @@ -130,41 +124,10 @@ ${$getNativeToolCallExampleString({ question: 'Which authentication method should we use?', header: 'Auth method', options: [ - { - label: 'JWT tokens', - description: 'Stateless tokens stored in localStorage', - }, - { - label: 'Session cookies', - description: 'Server-side sessions with httpOnly cookies', - }, - { - label: 'OAuth2', - description: 'Third-party authentication (Google, GitHub, etc.)', - }, - ], - }, - ], - }, - endsAgentStep, -})} - -Multi-select example: -${$getNativeToolCallExampleString({ - toolName, - inputSchema, - input: { - questions: [ - { - question: 'Which features should we implement?', - header: 'Features', - options: [ - { label: 'Rate limiting' }, - { label: 'Caching' }, - { label: 'Logging' }, - { label: 'Monitoring' }, + { label: 'JWT tokens' }, + { label: 'Session cookies' }, + { label: 'OAuth2' }, ], - multiSelect: true, }, ], }, diff --git a/common/src/tools/params/tool/code-search.ts b/common/src/tools/params/tool/code-search.ts index bf902e24e9..f42d4c853a 100644 --- a/common/src/tools/params/tool/code-search.ts +++ b/common/src/tools/params/tool/code-search.ts @@ -41,52 +41,8 @@ const legacyInputSchema = inputSchema.describe( `Search for string patterns in the project's files. This tool uses ripgrep (rg), a fast line-oriented search tool. Use this tool only when read_files is not sufficient to find the files you need.`, ) const buildDescription = (guidance: string) => ` -Purpose: Search through code files to find files with specific text patterns, function names, variable names, and more. - ${guidance} -Use cases: -1. Finding all references to a function, class, or variable name across the codebase -2. Searching for specific code patterns or implementations -3. Looking up where certain strings or text appear -4. Finding files that contain specific imports or dependencies -5. Locating configuration settings or environment variables - -The pattern supports regular expressions and will search recursively through all files in the project by default. Some tips: -- Be as constraining in the pattern as possible to limit the number of files returned, e.g. if searching for the definition of a function, use "(function foo|const foo)" or "def foo" instead of merely "foo". -- Use Rust-style regex, not grep-style, PCRE, RE2 or JavaScript regex - you must always escape special characters like { and } -- Be as constraining as possible to limit results, e.g. use "(function foo|const foo)" or "def foo" instead of merely "foo" -- Add context to your search with surrounding terms (e.g., "function handleAuth" rather than just "handleAuth") -- Use word boundaries (\\b) to match whole words only -- Use the cwd parameter to narrow your search to specific directories -- For case-sensitive searches like constants (e.g., ERROR vs error), omit the "-i" flag -- Searches file content and filenames -- Automatically ignores binary files, hidden files, and files in .gitignore - - -Advanced ripgrep flags (use the flags parameter): - -- Case sensitivity: "-i" for case-insensitive search -- File type filtering: "-t ts -t js" (TypeScript and JavaScript), "-t py" (Python), etc. -- Exclude file types: "--type-not py" to exclude Python files -- Context lines: "-A 3" (3 lines after), "-B 2" (2 lines before), "-C 2" (2 lines before and after) -- Line numbers: "-n" to show line numbers -- Count matches: "-c" to count matches per file -- Only filenames: "-l" to show only filenames with matches -- Invert match: "-v" to show lines that don't match -- Word boundaries: "-w" to match whole words only -- Fixed strings: "-F" to treat pattern as literal string (not regex) - -Note: Do not use the end_turn tool after this tool! You will want to see the output of this tool before ending your turn. - -RESULT LIMITING: - -- The maxResults parameter limits the number of results shown per file (default: 15) -- There is also a global limit of 250 total results across all files -- These limits allow you to see results across multiple files without being overwhelmed by matches in a single file -- If a file has more matches than maxResults, you'll see a truncation notice indicating how many results were found -- If the global limit is reached, remaining files will be skipped - Examples: ${$getNativeToolCallExampleString({ toolName, @@ -106,31 +62,13 @@ ${$getNativeToolCallExampleString({ input: { pattern: 'import.*foo', cwd: 'src' }, endsAgentStep, })} -${$getNativeToolCallExampleString({ - toolName, - inputSchema, - input: { pattern: 'function.*authenticate', flags: '-i -t ts -t js' }, - endsAgentStep, -})} -${$getNativeToolCallExampleString({ - toolName, - inputSchema, - input: { pattern: 'TODO', flags: '-n --type-not py' }, - endsAgentStep, -})} -${$getNativeToolCallExampleString({ - toolName, - inputSchema, - input: { pattern: 'getUserData', maxResults: 10 }, - endsAgentStep, -})} `.trim() const legacyDescription = buildDescription( - 'Prefer to use read_files instead of code_search unless you need to search for a specific pattern in multiple files.', + 'Prefer read_files over code_search unless you need to search for a specific pattern across files.', ) const description = buildDescription( - 'Matches are returned with their line numbers, so in a large file you can search first and then read a window around a match with read_files { path, offset, limit } instead of reading the whole file.', + 'Matches come with line numbers, so in a large file you can search first and then read a window around a match with read_files.', ) export const codeSearchDisplayVariants = { diff --git a/common/src/tools/params/tool/glob.ts b/common/src/tools/params/tool/glob.ts index a3f40c890f..748768017a 100644 --- a/common/src/tools/params/tool/glob.ts +++ b/common/src/tools/params/tool/glob.ts @@ -31,7 +31,6 @@ const inputSchema = z `Search for files matching a glob pattern. Returns matching file paths sorted by modification time.`, ) const description = ` -Example: ${$getNativeToolCallExampleString({ toolName, inputSchema, @@ -40,22 +39,6 @@ ${$getNativeToolCallExampleString({ }, endsAgentStep, })} - -Purpose: Search for files matching a glob pattern to discover files by name patterns rather than content. -Use cases: -- Find all files with a specific extension (e.g., "*.js", "*.test.ts") -- Locate files in specific directories (e.g., "src/**/*.ts") -- Find files with specific naming patterns (e.g., "**/test_*.go", "**/*-config.json") -- Discover test files, configuration files, or other files with predictable naming - -Glob patterns support: -- * matches any characters except / -- ** matches any characters including / -- ? matches a single character -- [abc] matches one of the characters in brackets -- {a,b} matches one of the comma-separated patterns - -This tool is fast and works well for discovering files by name patterns. `.trim() export const globParams = { diff --git a/common/src/tools/params/tool/list-directory.ts b/common/src/tools/params/tool/list-directory.ts index d70590f375..610fcf873c 100644 --- a/common/src/tools/params/tool/list-directory.ts +++ b/common/src/tools/params/tool/list-directory.ts @@ -16,9 +16,6 @@ const inputSchema = z 'List files and directories in the specified path. Returns separate arrays of file names and directory names.', ) const description = ` -Lists all files and directories in the specified path. Useful for exploring directory structure and finding files. - -Example: ${$getNativeToolCallExampleString({ toolName, inputSchema, @@ -27,15 +24,6 @@ ${$getNativeToolCallExampleString({ }, endsAgentStep, })} - -${$getNativeToolCallExampleString({ - toolName, - inputSchema, - input: { - path: '.', - }, - endsAgentStep, -})} `.trim() export const listDirectoryParams = { diff --git a/common/src/tools/params/tool/run-terminal-command.ts b/common/src/tools/params/tool/run-terminal-command.ts index c38cf940fa..2ffadfd12f 100644 --- a/common/src/tools/params/tool/run-terminal-command.ts +++ b/common/src/tools/params/tool/run-terminal-command.ts @@ -121,41 +121,10 @@ const GIT_COMMIT_PLAIN_STEP = `4. **Create the commit.** Do NOT add any trailer, const GIT_COMMIT_GUIDE_HEAD = ` ### Using git to commit changes -When the user requests a new git commit, please follow these steps closely: - -1. **Run two run_terminal_command tool calls:** - - Run \`git diff\` to review both staged and unstaged modifications. - - Run \`git log\` to check recent commit messages, ensuring consistency with this repository's style. - -2. **Select relevant files to include in the commit:** - Use the git context established at the start of this conversation to decide which files are pertinent to the changes. Stage any new untracked files that are relevant, but avoid committing previously modified files (from the beginning of the conversation) unless they directly relate to this commit. - -3. **Analyze the staged changes and compose a commit message:** - Enclose your analysis in tags. Within these tags, you should: - - Note which files have been altered or added. - - Categorize the nature of the changes (e.g., new feature, fix, refactor, documentation, etc.). - - Consider the purpose or motivation behind the alterations. - - Refrain from using tools to inspect code beyond what is presented in the git context. - - Evaluate the overall impact on the project. - - Check for sensitive details that should not be committed. - - Draft a concise, one- to two-sentence commit message focusing on the “why” rather than the “what.” - - Use precise, straightforward language that accurately represents the changes. - - Ensure the message provides clarity—avoid generic or vague terms like “Update” or “Fix” without context. - - Revisit your draft to confirm it truly reflects the changes and their intention. - +Run \`git diff\` to review changes and \`git log\` to match the repo's commit style before committing. Don't push, never alter git config, don't use interactive flags, don't create empty commits. ` -const GIT_COMMIT_GUIDE_TAIL = ` - -**Important details** - -- When feasible, use a single \`git commit -am\` command to add and commit together, but do not accidentally stage unrelated files. -- Never alter the git config. -- Do not push to the remote repository. -- Avoid using interactive flags (e.g., \`-i\`) that require unsupported interactive input. -- Do not create an empty commit if there are no changes. -- Make sure your commit message is concise yet descriptive, focusing on the intention behind the changes rather than merely describing them. -` +const GIT_COMMIT_GUIDE_TAIL = `` /** The default guidance. Byte-identical to what shipped before it was split. */ export const gitCommitGuidePrompt = buildGitCommitGuidePrompt({ @@ -198,30 +167,7 @@ const inputSchema = z `Execute a CLI command from the **project root** (different from the user's cwd).`, ) const buildDescription = (options: { attribution: boolean }) => ` -Stick to these use cases: -1. Typechecking the project or running build (e.g., "npm run build"). Reading the output can help you edit code to fix build errors. If possible, use an option that performs checks but doesn't emit files, e.g. \`tsc --noEmit\`. -2. Running tests (e.g., "npm test"). Reading the output can help you edit code to fix failing tests. Or, you could write new unit tests and then run them. -3. Moving, renaming, or deleting files and directories. These actions can be vital for refactoring requests. Use \`mv\` or \`rm\` (commands run in bash on every OS, including Windows — do not use \`move\`/\`del\`). - -Most likely, you should ask for permission for any other type of command you want to run. If asking for permission, show the user the command you want to run using \`\`\` tags and *do not* use the tool call format, e.g.: -\`\`\`bash -git branch -D foo -\`\`\` - -DO NOT do any of the following: -1. Run commands that can modify files outside of the project directory, install packages globally, install virtual environments, or have significant side effects outside of the project directory, unless you have explicit permission from the user. Treat anything outside of the project directory as read-only. -2. Run \`git push\` because it can break production (!) if the user was not expecting it. Don't run \`git commit\`, \`git rebase\`, or related commands unless you get explicit permission. If a user asks to commit changes, you can do so, but you should not invoke any further git commands beyond the git commit command. -3. Run scripts without asking. Especially don't run scripts that could run against the production environment or have permanent effects without explicit permission from the user. -4. Be careful with any command that has big or irreversible effects. Anything that touches a production environment, servers, the database, or other systems that could be affected by a command should be run with explicit permission from the user. -5. Use the run_terminal_command tool to create or edit files. Do not use \`cat\` or \`echo\` to create or edit files. You should instead use other tools for creating or editing files. -6. Use the wrong package manager for the project. For example, if the project uses \`pnpm\` or \`bun\` or \`yarn\`, you should not use \`npm\`. Similarly not everyone uses \`pip\` for python, etc. - -Do: -- If there's an opportunity to use "-y" or "--yes" flags, use them. Any command that prompts for confirmation will hang if you don't use the flags. - -Notes: -- If the user references a specific file, it could be either from their cwd or from the project root. You **must** determine which they are referring to (either infer or ask). Then, you must specify the path relative to the project root (or use the cwd parameter) -- Commands can succeed without giving any output, e.g. if no type errors were found. +Commands run in bash on every OS. Use POSIX syntax (\`mv\`/\`rm\`, not \`move\`/\`del\`). ${buildGitCommitGuidePrompt(options)} diff --git a/common/src/tools/params/tool/suggest-followups.ts b/common/src/tools/params/tool/suggest-followups.ts index b5cde7f39c..6445159c31 100644 --- a/common/src/tools/params/tool/suggest-followups.ts +++ b/common/src/tools/params/tool/suggest-followups.ts @@ -45,43 +45,17 @@ const outputSchema = z.object({ }) const description = ` -Suggest clickable followup prompts to the user. When the user clicks a suggestion, it sends that prompt as a new user message. +Suggest clickable followup prompts. Each followup becomes a card the user can click to send that prompt. -Use this tool after completing a task to suggest what the user might want to do next. Good suggestions include: -- Alternatives to the latest implementation like "Cache the data in local storage instead" -- Related features like "Show the state data in a hover card" -- Cleanup opportunities like "Split app.ts into focused modules" -- Testing suggestions like "Add unit tests for this change" -- Verification you can run yourself like "Check the login flow in the browser and fix what breaks" -- "Continue with the next step" - when there are more steps in a plan - -Keep every suggestion short and goal-oriented: one sentence naming the outcome you want, not the steps to get there. Whoever picks up the prompt does better work when free to choose the approach, so leave out file paths, function names, ordering, and design decisions unless one of those is the actual point of the request. A prompt still has to stand on its own — name the target clearly, just don't narrate a plan. - -Don't include suggestions like: -- "Commit these changes" -- Anything the user would have to carry out themselves, like "Ask your designer whether this matches the spec" — as opposed to "Add test coverage for the login flow", which is a goal the assistant can own. Judge by the tools you actually have: work you can do yourself is a goal, not a chore. - -Try to make different suggestions than you did in past steps. That's because users can still click previous suggestions if they want to. - -Aim for around 3 suggestions. The suggestions persist and remain clickable, with clicked ones visually updated to show they were used. +Aim for ~3 suggestions. Keep each short and goal-oriented — name the outcome, not the steps. Skip work the user would have to do themselves. ${$getNativeToolCallExampleString({ toolName, inputSchema, input: { followups: [ - { - prompt: 'Continue with the next step', - label: 'Continue', - }, - { - prompt: 'Add unit tests for UserService', - label: 'Add tests', - }, - { - prompt: 'Pull the auth logic out of the request handler', - label: 'Refactor auth', - }, + { prompt: 'Add unit tests for this change', label: 'Add tests' }, + { prompt: 'Continue with the next step', label: 'Continue' }, ], }, endsAgentStep, diff --git a/common/src/tools/params/tool/web-search.ts b/common/src/tools/params/tool/web-search.ts index ba705295c0..04f7b82ea6 100644 --- a/common/src/tools/params/tool/web-search.ts +++ b/common/src/tools/params/tool/web-search.ts @@ -22,18 +22,6 @@ const inputSchema = z }) .describe(`Search the web for current information using Serper API.`) const description = ` -Purpose: Search the web for current, up-to-date information on any topic. This tool uses Serper's Google Search API to find relevant content from across the internet. - -Use cases: -- Finding current information about technologies, libraries, or frameworks -- Researching best practices and solutions -- Getting up-to-date news or documentation -- Finding examples and tutorials -- Checking current status of services or APIs - -The tool will return JSON search results with titles, URLs, content snippets, and other available SERP fields such as answer boxes or related questions. - -Example: ${$getNativeToolCallExampleString({ toolName, inputSchema, @@ -43,16 +31,6 @@ ${$getNativeToolCallExampleString({ }, endsAgentStep, })} - -${$getNativeToolCallExampleString({ - toolName, - inputSchema, - input: { - query: 'React Server Components tutorial', - depth: 'deep', - }, - endsAgentStep, -})} `.trim() export const webSearchParams = { diff --git a/common/src/tools/params/tool/write-todos.ts b/common/src/tools/params/tool/write-todos.ts index ba0f4a34e3..0ac8ccae00 100644 --- a/common/src/tools/params/tool/write-todos.ts +++ b/common/src/tools/params/tool/write-todos.ts @@ -18,33 +18,21 @@ const inputSchema = z }), ), ) - .describe( - "List of todos with their completion status. Add ALL of the applicable tasks to the list, so you don't forget to do anything. Try to order the todos the same way you will complete them. Do not mark todos as completed if you have not completed them yet!", - ), + .describe( + 'List of todos with their completion status.', + ), }) .describe( - 'Write a todo list to track tasks for multi-step implementations. Use this frequently to maintain an updated step-by-step plan.', + 'Track multi-step work with a todo list.', ) const description = ` -Use this tool to track your objectives through an ordered step-by-step plan. Call this tool after you have gathered context on the user's request to plan out the implementation steps for the user's request. - -After completing each todo step, call this tool again to update the list and mark that task as completed. Note that each time you call this tool, rewrite ALL todos with their current status. - -Use this tool frequently as you work through tasks to update the list of todos with their current status. Doing this is extremely useful because it helps you stay on track and complete all the requirements of the user's request. It also helps inform the user of your plans and the current progress, which they want to know at all times. - -Example: ${$getNativeToolCallExampleString({ toolName, inputSchema, input: { todos: [ - { task: 'Create new implementation in foo.ts', completed: true }, - { task: 'Update bar.ts to use the new implementation', completed: false }, - { task: 'Write tests for the new implementation', completed: false }, - { - task: 'Run the tests to verify the new implementation', - completed: false, - }, + { task: 'Edit foo.ts', completed: true }, + { task: 'Run tests', completed: false }, ], }, endsAgentStep, diff --git a/packages/agent-runtime/src/system-prompt/prompts.ts b/packages/agent-runtime/src/system-prompt/prompts.ts index 1f7e151e2b..ee6071407a 100644 --- a/packages/agent-runtime/src/system-prompt/prompts.ts +++ b/packages/agent-runtime/src/system-prompt/prompts.ts @@ -10,55 +10,6 @@ import { truncateFileTreeBasedOnTokenBudget } from './truncate-file-tree' import type { Logger } from '@codebuff/common/types/contracts/logger' import type { ProjectFileContext } from '@codebuff/common/util/file' -export const knowledgeFilesPrompt = ` -# Knowledge files - -Knowledge files are your guide to the project. Knowledge files (files ending in "knowledge.md", "AGENTS.md", or "CLAUDE.md") within a directory capture knowledge about that portion of the codebase. They are another way to take notes in this "Memento"-style environment. - -Knowledge files were created by previous engineers working on the codebase, and they were given these same instructions. They contain key concepts or helpful tips that are not obvious from the code. e.g., let's say I want to use a package manager aside from the default. That is hard to find in the codebase and would therefore be an appropriate piece of information to add to a knowledge file. - -Each knowledge file should develop over time into a concise but rich repository of knowledge about the files within the directory, subdirectories, or the specific file it's associated with. - -There is a special class of user knowledge files that are stored in the user's home directory, e.g. \`~/.knowledge.md\`, \`~/.AGENTS.md\`, or \`~/.CLAUDE.md\`. These files are available to be read, but you cannot edit them because they are outside of the project directory. Do not try to edit them. - -When should you update a knowledge file? -- If the user gives broad advice to "always do x", that is a good candidate for updating a knowledge file with a concise rule to follow or bit of advice so you won't make the mistake again. -- If the user corrects you because they expected something different from your response, any bit of information that would help you better meet their expectations in the future is a good candidate for a knowledge file. - -What to include in knowledge files: -- The mission of the project. Goals, purpose, and a high-level overview of the project. -- Explanations of how different parts of the codebase work or interact. -- Examples of how to do common tasks with a short explanation. -- Anti-examples of what should be avoided. -- Anything the user has said to do. -- Anything you can infer that the user wants you to do going forward. -- Tips and tricks. -- Style preferences for the codebase. -- Technical goals that are in progress. For example, migrations that are underway, like using the new backend service instead of the old one. -- Links to reference pages that are helpful. For example, the url of documentation for an api you are using. -- Anything else that would be helpful for you or an inexperienced coder to know - -What *not* to include in knowledge files: -- Documentation of a single file. -- Restated code or interfaces in natural language. -- Anything obvious from reading the codebase. -- Lots of detail about a minor change. -- An explanation of the code you just wrote, unless there's something very unintuitive. - -Again, DO NOT include details from your recent change that are not relevant more broadly. - -Guidelines for updating knowledge files: -- Be concise and focused on the most important aspects of the project. -- Integrate new knowledge into existing sections when possible. -- Avoid overemphasizing recent changes or the aspect you're currently working on. Your current change is less important than you think. -- Remove as many words as possible while keeping the meaning. Use command verbs. Use sentence fragments. -- Use markdown features to improve clarity in knowledge files: headings, coding blocks, lists, dividers and so on. - -Once again: BE CONCISE! - -If the user sends you the url to a page that is helpful now or could be helpful in the future (e.g. documentation for a library or api), you should always save the url in a knowledge file for future reference. Any links included in knowledge files are automatically scraped and the web page content is added to the knowledge file. -`.trim() - const compactPrompt = ` User has typed "compact". Summarize the current conversation and prepare it to replace the existing message history. diff --git a/packages/agent-runtime/src/tools/prompts.ts b/packages/agent-runtime/src/tools/prompts.ts index d3d9110665..dd1354e30b 100644 --- a/packages/agent-runtime/src/tools/prompts.ts +++ b/packages/agent-runtime/src/tools/prompts.ts @@ -14,10 +14,7 @@ import { convertJsonSchemaToZod } from 'zod-from-json-schema' import type { ToolName } from '@codebuff/common/tools/constants' import type { SkillsMap } from '@codebuff/common/types/skill' -import type { - CustomToolDefinitions, - customToolDefinitionsSchema, -} from '@codebuff/common/util/file' +import type { CustomToolDefinitions } from '@codebuff/common/util/file' import type { ToolSet } from 'ai' /** @@ -149,206 +146,6 @@ export const toolDescriptions = Object.fromEntries( ]), ) as Record -function buildShortToolDescription(params: { - toolName: string - schema: z.ZodType - endsAgentStep: boolean -}): string { - const { toolName, schema, endsAgentStep } = params - return `${toolName}:\n${paramsSection({ schema, endsAgentStep })}` -} - -export const getToolsInstructions = ( - tools: readonly string[], - additionalToolDefinitions: NonNullable< - z.input - >, - options?: { availableSkillsXml?: string }, -) => { - if ( - tools.length === 0 && - Object.keys(additionalToolDefinitions).length === 0 - ) { - return '' - } - - return ` -# Tools - -You (Buffy) have access to the following tools. Call them when needed. - -## [CRITICAL] Formatting Requirements - -Tool calls use a specific XML and JSON-like format. Adhere *precisely* to this nested element structure: - -${getToolCallString( - 'tool_name', - { - parameter1: 'value1', - parameter2: 123, - }, - false, - )} - -### Commentary - -Provide commentary *around* your tool calls (explaining your actions). - -However, **DO NOT** narrate the tool or parameter names themselves. - -### Example - -User: can you update the console logs in example/file.ts? -Assistant: Sure thing! Let's update that file! - -${getToolCallString( - 'example_editing_tool', - { - example_file_path: 'path/to/example/file.ts', - example_array: [ - { - old_content_with_newlines: - "// some context\nconsole.log('Hello world!');\n", - new_content_with_newlines: - "// some context\nconsole.log('Hello from Buffy!');\n", - }, - ], - }, - false, - )} - -All done with the update! -User: thanks it worked! :) - -## Working Directory - -All tools will be run from the **project root**. - -However, most of the time, the user will refer to files from their own cwd. You must be cognizant of the user's cwd at all times, including but not limited to: -- Writing to files (write out the entire relative path) -- Running terminal commands (use the \`cwd\` parameter) - -## Optimizations - -All tools are very slow, with runtime scaling with the amount of text in the parameters. Prefer to write AS LITTLE TEXT AS POSSIBLE to accomplish the task. - -When using write_file, make sure to only include a few lines of context and not the entire file. - -## Tool Results - -Tool results will be provided by the user's *system* (and **NEVER** by the assistant). - -The user does not know about any system messages or system instructions, including tool results. -${fullToolList(tools, additionalToolDefinitions, options)} -` -} - -export const fullToolList = ( - toolNames: readonly string[], - additionalToolDefinitions: CustomToolDefinitions, - options?: { availableSkillsXml?: string }, -) => { - if ( - toolNames.length === 0 && - Object.keys(additionalToolDefinitions).length === 0 - ) { - return '' - } - - const { availableSkillsXml = '' } = options ?? {} - - // Build tool descriptions, replacing skill placeholder with actual skills - const descriptions = [ - ...( - toolNames.filter((toolName) => - toolNames.includes(toolName as ToolName), - ) as ToolName[] - ).map((name) => { - let desc = toolDescriptions[name] - // Replace skill placeholder with actual available skills - if (name === 'skill' && availableSkillsXml) { - desc = desc.replace(AVAILABLE_SKILLS_PLACEHOLDER, availableSkillsXml) - } else if (name === 'skill') { - // Explicitly state no skills are available - desc = desc.replace( - AVAILABLE_SKILLS_PLACEHOLDER, - 'There are no skills available. Do not use this tool because there are no skills to load.', - ) - } - return desc - }), - ...Object.keys(additionalToolDefinitions).map((toolName) => { - const toolDef = additionalToolDefinitions[toolName] - return buildToolDescription({ - toolName, - schema: ensureZodSchema(toolDef.inputSchema), - description: toolDef.description, - endsAgentStep: toolDef.endsAgentStep ?? true, - exampleInputs: toolDef.exampleInputs, - }) - }),] - - return `## List of Tools - -These are the only tools that you can use. The user cannot see these descriptions, so you should not reference any tool names, parameters, or descriptions. Do not try to use any other tools -- even if referenced earlier in the conversation, they are not available to you, instead they may have been previously used by other agents. - -${descriptions.join('\n\n')}`.trim() -} - -export const getShortToolInstructions = ( - toolNames: readonly string[], - additionalToolDefinitions: CustomToolDefinitions, -) => { - if ( - toolNames.length === 0 && - Object.keys(additionalToolDefinitions).length === 0 - ) { - return '' - } - - const toolDescriptionsList = [ - ...( - toolNames.filter( - (name) => (name as keyof typeof toolParams) in toolParams, - ) as (keyof typeof toolParams)[] - ).map((name) => { - const tool = toolParams[name] - return buildShortToolDescription({ - toolName: name, - schema: tool.inputSchema, - endsAgentStep: tool.endsAgentStep, - }) - }), - ...Object.keys(additionalToolDefinitions).map((name) => { - const { inputSchema, endsAgentStep } = additionalToolDefinitions[name] - return buildShortToolDescription({ - toolName: name, - schema: ensureZodSchema(inputSchema), - endsAgentStep: endsAgentStep ?? true, - }) - }), - ] - - return `## Tools -Use the tools below to complete the user request, if applicable. - -Tool calls use a specific XML and JSON-like format. Adhere *precisely* to this nested element structure: - -${getToolCallString( - 'tool_name', - { - parameter1: 'value1', - parameter2: 123, - }, - false, - )} - -Important: You only have access to the tools below. Do not use any other tools -- they are not available to you, instead they may have been previously used by other agents. - -${toolDescriptionsList.join('\n\n')} -`.trim() -} - const readStyleDisplayVariants: Partial< Record > = {