import type { TextBlock } from '@anthropic-ai/sdk/resources/index.mjs' import { Box } from 'ink' import * as React from 'react' import { z } from 'zod' import type { Tool } from '../../Tool' import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage' import { HighlightedCode } from '../../components/HighlightedCode' import { getContext } from '../../context' import { Message, query } from '../../query' import { lastX } from '../../utils/generators' import { createUserMessage } from '../../utils/messages' import { BashTool } from '../BashTool/BashTool' import { FileReadTool } from '../FileReadTool/FileReadTool' import { FileWriteTool } from '../FileWriteTool/FileWriteTool' import { GlobTool } from '../GlobTool/GlobTool' import { GrepTool } from '../GrepTool/GrepTool' import { LSTool } from '../lsTool/lsTool' import { ARCHITECT_SYSTEM_PROMPT, DESCRIPTION } from './prompt' const FS_EXPLORATION_TOOLS: Tool[] = [ BashTool, LSTool, FileReadTool, FileWriteTool, GlobTool, GrepTool, ] const inputSchema = z.strictObject({ prompt: z .string() .describe('The technical request or coding task to analyze'), context: z .string() .describe('Optional context from previous conversation or system state') .optional(), }) export const ArchitectTool = { name: 'Architect', async description() { return DESCRIPTION }, inputSchema, isReadOnly() { return true }, isConcurrencySafe() { return true // ArchitectTool is read-only, safe for concurrent execution }, userFacingName() { return 'Architect' }, async isEnabled() { return false }, needsPermissions() { return false }, async *call({ prompt, context }, toolUseContext, canUseTool) { const content = context ? `${context}\n\n${prompt}` : prompt const userMessage = createUserMessage(content) const messages: Message[] = [userMessage] // We only allow the file exploration tools to be used in the architect tool const allowedTools = (toolUseContext.options.tools ?? []).filter(_ => FS_EXPLORATION_TOOLS.map(_ => _.name).includes(_.name), ) const lastResponse = await lastX( query( messages, [ARCHITECT_SYSTEM_PROMPT], await getContext(), canUseTool, { ...toolUseContext, options: { ...toolUseContext.options, tools: allowedTools }, }, ), ) if (lastResponse.type !== 'assistant') { throw new Error(`Invalid response from API`) } const data = lastResponse.message.content.filter(_ => _.type === 'text') yield { type: 'result', data, resultForAssistant: this.renderResultForAssistant(data), } }, async prompt() { return DESCRIPTION }, renderResultForAssistant(data) { return data }, renderToolUseMessage(input) { return Object.entries(input) .map(([key, value]) => `${key}: ${JSON.stringify(value)}`) .join(', ') }, renderToolResultMessage(content) { return ( _.text).join('\n')} language="markdown" /> ) }, renderToolUseRejectedMessage() { return }, } satisfies Tool