@within-7/minto 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -95,19 +95,20 @@ ${prompt}` : prompt;
95
95
  return DESCRIPTION;
96
96
  },
97
97
  renderResultForAssistant(data) {
98
- return data.map((block) => block.text).join("\n");
98
+ if (!data || !Array.isArray(data)) {
99
+ return "Analysis completed";
100
+ }
101
+ return data.filter((block) => block != null).map((block) => block.text || "").join("\n");
99
102
  },
100
103
  renderToolUseMessage(input) {
101
- return Object.entries(input).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(", ");
104
+ return Object.entries(input || {}).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(", ");
102
105
  },
103
106
  renderToolResultMessage(content) {
104
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(
105
- HighlightedCode,
106
- {
107
- code: content.map((_) => _.text).join("\n"),
108
- language: "markdown"
109
- }
110
- ));
107
+ if (!content || !Array.isArray(content)) {
108
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(HighlightedCode, { code: "Analysis completed", language: "markdown" }));
109
+ }
110
+ const code = content.filter((block) => block != null).map((block) => block.text || "").join("\n");
111
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React.createElement(HighlightedCode, { code: code || "Analysis completed", language: "markdown" }));
111
112
  },
112
113
  renderToolUseRejectedMessage() {
113
114
  return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/ArchitectTool/ArchitectTool.tsx"],
4
- "sourcesContent": ["import type { TextBlock } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { Box } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport type { Tool } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { getContext } from '@context'\nimport { Message, query } from '@query'\nimport { lastX } from '@utils/generators'\nimport { createUserMessage } from '@utils/messages'\nimport { BashTool } from '@tools/BashTool/BashTool'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { FileWriteTool } from '@tools/FileWriteTool/FileWriteTool'\nimport { GlobTool } from '@tools/GlobTool/GlobTool'\nimport { GrepTool } from '@tools/GrepTool/GrepTool'\nimport { LSTool } from '@tools/lsTool/lsTool'\nimport { ARCHITECT_SYSTEM_PROMPT, DESCRIPTION } from './prompt'\n\nconst FS_EXPLORATION_TOOLS: Tool[] = [\n BashTool,\n LSTool,\n FileReadTool,\n FileWriteTool,\n GlobTool,\n GrepTool,\n]\n\nconst inputSchema = z.strictObject({\n prompt: z\n .string()\n .describe('The technical request or coding task to analyze'),\n context: z\n .string()\n .describe('Optional context from previous conversation or system state')\n .optional(),\n})\n\nexport const ArchitectTool = {\n name: 'Architect',\n async description() {\n return DESCRIPTION\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // ArchitectTool is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Architect'\n },\n async isEnabled() {\n return false\n },\n needsPermissions() {\n return false\n },\n async *call({ prompt, context }, toolUseContext) {\n const content = context\n ? `<context>${context}</context>\\n\\n${prompt}`\n : prompt\n\n const userMessage = createUserMessage(content)\n\n const messages: Message[] = [userMessage]\n\n // We only allow the file exploration tools to be used in the architect tool\n const allowedTools = (toolUseContext.options?.tools ?? []).filter(_ =>\n FS_EXPLORATION_TOOLS.map(_ => _.name).includes(_.name),\n )\n\n // Create a dummy canUseTool function since this tool controls its own tool usage\n const canUseTool = async () => ({ result: true as const })\n\n const lastResponse = await lastX(\n query(\n messages,\n [ARCHITECT_SYSTEM_PROMPT],\n await getContext(),\n canUseTool,\n {\n ...toolUseContext,\n setToolJSX: () => {}, // Dummy function since ArchitectTool doesn't use UI\n options: {\n commands: toolUseContext.options?.commands || [],\n forkNumber: toolUseContext.options?.forkNumber || 0,\n messageLogName: toolUseContext.options?.messageLogName || 'default',\n verbose: toolUseContext.options?.verbose || false,\n safeMode: toolUseContext.options?.safeMode || false,\n maxThinkingTokens: toolUseContext.options?.maxThinkingTokens || 0,\n ...toolUseContext.options,\n tools: allowedTools,\n },\n },\n ),\n )\n\n if (lastResponse.type !== 'assistant') {\n throw new Error(`Invalid response from API`)\n }\n\n const data = lastResponse.message.content.filter(_ => _.type === 'text')\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n async prompt() {\n return DESCRIPTION\n },\n renderResultForAssistant(data: TextBlock[]): string {\n return data.map(block => block.text).join('\\n')\n },\n renderToolUseMessage(input) {\n return Object.entries(input)\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(content) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <HighlightedCode\n code={content.map(_ => _.text).join('\\n')}\n language=\"markdown\"\n />\n </Box>\n )\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n} satisfies Tool<typeof inputSchema, TextBlock[]>\n"],
5
- "mappings": "AACA,SAAS,WAAW;AACpB,YAAY,WAAW;AACvB,SAAS,SAAS;AAElB,SAAS,sCAAsC;AAC/C,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAC3B,SAAkB,aAAa;AAC/B,SAAS,aAAa;AACtB,SAAS,yBAAyB;AAClC,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,yBAAyB,mBAAmB;AAErD,MAAM,uBAA+B;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,QAAQ,EACL,OAAO,EACP,SAAS,iDAAiD;AAAA,EAC7D,SAAS,EACN,OAAO,EACP,SAAS,6DAA6D,EACtE,SAAS;AACd,CAAC;AAEM,MAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EACA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EACA,OAAO,KAAK,EAAE,QAAQ,QAAQ,GAAG,gBAAgB;AAC/C,UAAM,UAAU,UACZ,YAAY,OAAO;AAAA;AAAA,EAAiB,MAAM,KAC1C;AAEJ,UAAM,cAAc,kBAAkB,OAAO;AAE7C,UAAM,WAAsB,CAAC,WAAW;AAGxC,UAAM,gBAAgB,eAAe,SAAS,SAAS,CAAC,GAAG;AAAA,MAAO,OAChE,qBAAqB,IAAI,CAAAA,OAAKA,GAAE,IAAI,EAAE,SAAS,EAAE,IAAI;AAAA,IACvD;AAGA,UAAM,aAAa,aAAa,EAAE,QAAQ,KAAc;AAExD,UAAM,eAAe,MAAM;AAAA,MACzB;AAAA,QACE;AAAA,QACA,CAAC,uBAAuB;AAAA,QACxB,MAAM,WAAW;AAAA,QACjB;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,YAAY,MAAM;AAAA,UAAC;AAAA;AAAA,UACnB,SAAS;AAAA,YACP,UAAU,eAAe,SAAS,YAAY,CAAC;AAAA,YAC/C,YAAY,eAAe,SAAS,cAAc;AAAA,YAClD,gBAAgB,eAAe,SAAS,kBAAkB;AAAA,YAC1D,SAAS,eAAe,SAAS,WAAW;AAAA,YAC5C,UAAU,eAAe,SAAS,YAAY;AAAA,YAC9C,mBAAmB,eAAe,SAAS,qBAAqB;AAAA,YAChE,GAAG,eAAe;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,aAAa,SAAS,aAAa;AACrC,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,UAAM,OAAO,aAAa,QAAQ,QAAQ,OAAO,OAAK,EAAE,SAAS,MAAM;AACvE,UAAM;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA,oBAAoB,KAAK,yBAAyB,IAAI;AAAA,IACxD;AAAA,EACF;AAAA,EACA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA,yBAAyB,MAA2B;AAClD,WAAO,KAAK,IAAI,WAAS,MAAM,IAAI,EAAE,KAAK,IAAI;AAAA,EAChD;AAAA,EACA,qBAAqB,OAAO;AAC1B,WAAO,OAAO,QAAQ,KAAK,EACxB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE,EACxD,KAAK,IAAI;AAAA,EACd;AAAA,EACA,wBAAwB,SAAS;AAC/B,WACE,oCAAC,OAAI,eAAc,UAAS,KAAK,KAC/B;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,QAAQ,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI;AAAA,QACxC,UAAS;AAAA;AAAA,IACX,CACF;AAAA,EAEJ;AAAA,EACA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AACF;",
4
+ "sourcesContent": ["import type { TextBlock } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { Box } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport type { Tool } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { getContext } from '@context'\nimport { Message, query } from '@query'\nimport { lastX } from '@utils/generators'\nimport { createUserMessage } from '@utils/messages'\nimport { BashTool } from '@tools/BashTool/BashTool'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { FileWriteTool } from '@tools/FileWriteTool/FileWriteTool'\nimport { GlobTool } from '@tools/GlobTool/GlobTool'\nimport { GrepTool } from '@tools/GrepTool/GrepTool'\nimport { LSTool } from '@tools/lsTool/lsTool'\nimport { ARCHITECT_SYSTEM_PROMPT, DESCRIPTION } from './prompt'\n\nconst FS_EXPLORATION_TOOLS: Tool[] = [\n BashTool,\n LSTool,\n FileReadTool,\n FileWriteTool,\n GlobTool,\n GrepTool,\n]\n\nconst inputSchema = z.strictObject({\n prompt: z\n .string()\n .describe('The technical request or coding task to analyze'),\n context: z\n .string()\n .describe('Optional context from previous conversation or system state')\n .optional(),\n})\n\nexport const ArchitectTool = {\n name: 'Architect',\n async description() {\n return DESCRIPTION\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // ArchitectTool is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Architect'\n },\n async isEnabled() {\n return false\n },\n needsPermissions() {\n return false\n },\n async *call({ prompt, context }, toolUseContext) {\n const content = context\n ? `<context>${context}</context>\\n\\n${prompt}`\n : prompt\n\n const userMessage = createUserMessage(content)\n\n const messages: Message[] = [userMessage]\n\n // We only allow the file exploration tools to be used in the architect tool\n const allowedTools = (toolUseContext.options?.tools ?? []).filter(_ =>\n FS_EXPLORATION_TOOLS.map(_ => _.name).includes(_.name),\n )\n\n // Create a dummy canUseTool function since this tool controls its own tool usage\n const canUseTool = async () => ({ result: true as const })\n\n const lastResponse = await lastX(\n query(\n messages,\n [ARCHITECT_SYSTEM_PROMPT],\n await getContext(),\n canUseTool,\n {\n ...toolUseContext,\n setToolJSX: () => {}, // Dummy function since ArchitectTool doesn't use UI\n options: {\n commands: toolUseContext.options?.commands || [],\n forkNumber: toolUseContext.options?.forkNumber || 0,\n messageLogName: toolUseContext.options?.messageLogName || 'default',\n verbose: toolUseContext.options?.verbose || false,\n safeMode: toolUseContext.options?.safeMode || false,\n maxThinkingTokens: toolUseContext.options?.maxThinkingTokens || 0,\n ...toolUseContext.options,\n tools: allowedTools,\n },\n },\n ),\n )\n\n if (lastResponse.type !== 'assistant') {\n throw new Error(`Invalid response from API`)\n }\n\n const data = lastResponse.message.content.filter(_ => _.type === 'text')\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n async prompt() {\n return DESCRIPTION\n },\n renderResultForAssistant(data: TextBlock[]): string {\n // Guard against undefined or null data\n if (!data || !Array.isArray(data)) {\n return 'Analysis completed'\n }\n return data\n .filter(block => block != null)\n .map(block => block.text || '')\n .join('\\n')\n },\n renderToolUseMessage(input) {\n return Object.entries(input || {})\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(content) {\n // Guard against undefined or null content\n if (!content || !Array.isArray(content)) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <HighlightedCode code=\"Analysis completed\" language=\"markdown\" />\n </Box>\n )\n }\n\n const code = content\n .filter(block => block != null)\n .map(block => block.text || '')\n .join('\\n')\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <HighlightedCode code={code || 'Analysis completed'} language=\"markdown\" />\n </Box>\n )\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n} satisfies Tool<typeof inputSchema, TextBlock[]>\n"],
5
+ "mappings": "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",
6
6
  "names": ["_"]
7
7
  }
@@ -64,7 +64,14 @@ ${formattedAnswers}`;
64
64
  return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
65
65
  },
66
66
  renderToolResultMessage(output) {
67
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: "#10B981" }, " ", "\u23BF "), /* @__PURE__ */ React.createElement(Text, null, "User provided answers")), output.answers.map((answer, index) => /* @__PURE__ */ React.createElement(Box, { key: index, flexDirection: "row", paddingLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: "#6B7280" }, "\u2022 "), answer.customInput ? /* @__PURE__ */ React.createElement(Text, null, "Q", answer.questionIndex + 1, ': "', answer.customInput, '"') : answer.selectedOptions ? /* @__PURE__ */ React.createElement(Text, null, "Q", answer.questionIndex + 1, ": Option", " ", answer.selectedOptions.map((i) => i + 1).join(", ")) : /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Q", answer.questionIndex + 1, ": No answer"))));
67
+ if (!output) {
68
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: "#10B981" }, " ", "\u23BF "), /* @__PURE__ */ React.createElement(Text, null, "Question completed")));
69
+ }
70
+ const answers = output.answers || [];
71
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: "#10B981" }, " ", "\u23BF "), /* @__PURE__ */ React.createElement(Text, null, "User provided answers")), answers.map((answer, index) => {
72
+ if (!answer) return null;
73
+ return /* @__PURE__ */ React.createElement(Box, { key: index, flexDirection: "row", paddingLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: "#6B7280" }, "\u2022 "), answer.customInput ? /* @__PURE__ */ React.createElement(Text, null, "Q", (answer.questionIndex ?? index) + 1, ': "', answer.customInput, '"') : answer.selectedOptions ? /* @__PURE__ */ React.createElement(Text, null, "Q", (answer.questionIndex ?? index) + 1, ": Option", " ", answer.selectedOptions.map((i) => i + 1).join(", ")) : /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Q", (answer.questionIndex ?? index) + 1, ": No answer"));
74
+ }));
68
75
  },
69
76
  async *call({ questions }, context) {
70
77
  if (!context.askUser) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { Tool, type ExtendedToolUseContext } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport type { UserAnswer } from '@minto-types/askUserQuestion'\n\nconst inputSchema = z.strictObject({\n questions: z\n .array(\n z.object({\n question: z.string().describe('The question to ask the user'),\n header: z\n .string()\n .max(12)\n .optional()\n .describe('Short category label (max 12 chars)'),\n options: z\n .array(\n z.object({\n label: z.string().describe('Option display text'),\n description: z.string().optional().describe('Option explanation'),\n }),\n )\n .min(2)\n .max(4)\n .optional()\n .describe('List of options (2-4 recommended)'),\n multiSelect: z\n .boolean()\n .optional()\n .default(false)\n .describe('Allow multiple selections'),\n }),\n )\n .min(1)\n .max(4)\n .describe('Questions to ask (1-4 questions)'),\n})\n\nexport type AskUserQuestionOutput = {\n answers: UserAnswer[]\n timestamp: number\n}\n\nexport const AskUserQuestionTool = {\n name: 'AskUserQuestion',\n\n async description() {\n return DESCRIPTION\n },\n\n async prompt() {\n return PROMPT\n },\n\n inputSchema,\n\n userFacingName() {\n return 'Ask User Question'\n },\n\n async isEnabled() {\n return true\n },\n\n isReadOnly() {\n return true // Doesn't modify files\n },\n\n isConcurrencySafe() {\n return false // User interaction is not concurrent-safe\n },\n\n needsPermissions() {\n return false // No special permissions needed\n },\n\n renderResultForAssistant(output: AskUserQuestionOutput) {\n const formattedAnswers = output.answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')\n\n return `User answered:\\n${formattedAnswers}`\n },\n\n renderToolUseMessage(input: z.infer<typeof inputSchema>) {\n const questionCount = input.questions.length\n const questionText =\n questionCount === 1\n ? input.questions[0]!.question\n : `${questionCount} questions`\n return `Asking user: ${questionText}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(output: AskUserQuestionOutput) {\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>User provided answers</Text>\n </Box>\n {output.answers.map((answer, index) => (\n <Box key={index} flexDirection=\"row\" paddingLeft={4}>\n <Text color=\"#6B7280\">\u2022 </Text>\n {answer.customInput ? (\n <Text>\n Q{answer.questionIndex + 1}: \"{answer.customInput}\"\n </Text>\n ) : answer.selectedOptions ? (\n <Text>\n Q{answer.questionIndex + 1}: Option{' '}\n {answer.selectedOptions.map(i => i + 1).join(', ')}\n </Text>\n ) : (\n <Text dimColor>Q{answer.questionIndex + 1}: No answer</Text>\n )}\n </Box>\n ))}\n </Box>\n )\n },\n\n async *call(\n { questions }: z.infer<typeof inputSchema>,\n context: ExtendedToolUseContext,\n ) {\n // Check if askUser function is available\n if (!context.askUser) {\n throw new Error(\n 'AskUserQuestion tool requires askUser function in context',\n )\n }\n\n try {\n // Convert parsed questions to UserQuestion type\n const userQuestions = questions.map(q => ({\n question: q.question,\n header: q.header,\n options: q.options?.map(opt => ({\n label: opt.label,\n description: opt.description,\n })),\n multiSelect: q.multiSelect ?? false,\n }))\n\n // Call askUser and wait for user response\n const answers = await context.askUser(userQuestions)\n\n // Return the result\n yield {\n type: 'result',\n data: {\n answers,\n timestamp: Date.now(),\n },\n resultForAssistant: `User answered:\\n${answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n } catch (error) {\n // User cancelled or error occurred\n throw new Error(\n `Failed to get user response: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n },\n} satisfies Tool<typeof inputSchema, AskUserQuestionOutput>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { Tool, type ExtendedToolUseContext } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport type { UserAnswer } from '@minto-types/askUserQuestion'\n\nconst inputSchema = z.strictObject({\n questions: z\n .array(\n z.object({\n question: z.string().describe('The question to ask the user'),\n header: z\n .string()\n .max(12)\n .optional()\n .describe('Short category label (max 12 chars)'),\n options: z\n .array(\n z.object({\n label: z.string().describe('Option display text'),\n description: z.string().optional().describe('Option explanation'),\n }),\n )\n .min(2)\n .max(4)\n .optional()\n .describe('List of options (2-4 recommended)'),\n multiSelect: z\n .boolean()\n .optional()\n .default(false)\n .describe('Allow multiple selections'),\n }),\n )\n .min(1)\n .max(4)\n .describe('Questions to ask (1-4 questions)'),\n})\n\nexport type AskUserQuestionOutput = {\n answers: UserAnswer[]\n timestamp: number\n}\n\nexport const AskUserQuestionTool = {\n name: 'AskUserQuestion',\n\n async description() {\n return DESCRIPTION\n },\n\n async prompt() {\n return PROMPT\n },\n\n inputSchema,\n\n userFacingName() {\n return 'Ask User Question'\n },\n\n async isEnabled() {\n return true\n },\n\n isReadOnly() {\n return true // Doesn't modify files\n },\n\n isConcurrencySafe() {\n return false // User interaction is not concurrent-safe\n },\n\n needsPermissions() {\n return false // No special permissions needed\n },\n\n renderResultForAssistant(output: AskUserQuestionOutput) {\n const formattedAnswers = output.answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')\n\n return `User answered:\\n${formattedAnswers}`\n },\n\n renderToolUseMessage(input: z.infer<typeof inputSchema>) {\n const questionCount = input.questions.length\n const questionText =\n questionCount === 1\n ? input.questions[0]!.question\n : `${questionCount} questions`\n return `Asking user: ${questionText}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(output: AskUserQuestionOutput) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>Question completed</Text>\n </Box>\n </Box>\n )\n }\n\n const answers = output.answers || []\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>User provided answers</Text>\n </Box>\n {answers.map((answer, index) => {\n // Guard against null/undefined answers\n if (!answer) return null\n return (\n <Box key={index} flexDirection=\"row\" paddingLeft={4}>\n <Text color=\"#6B7280\">\u2022 </Text>\n {answer.customInput ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: \"{answer.customInput}\"\n </Text>\n ) : answer.selectedOptions ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: Option{' '}\n {answer.selectedOptions.map(i => i + 1).join(', ')}\n </Text>\n ) : (\n <Text dimColor>Q{(answer.questionIndex ?? index) + 1}: No answer</Text>\n )}\n </Box>\n )\n })}\n </Box>\n )\n },\n\n async *call(\n { questions }: z.infer<typeof inputSchema>,\n context: ExtendedToolUseContext,\n ) {\n // Check if askUser function is available\n if (!context.askUser) {\n throw new Error(\n 'AskUserQuestion tool requires askUser function in context',\n )\n }\n\n try {\n // Convert parsed questions to UserQuestion type\n const userQuestions = questions.map(q => ({\n question: q.question,\n header: q.header,\n options: q.options?.map(opt => ({\n label: opt.label,\n description: opt.description,\n })),\n multiSelect: q.multiSelect ?? false,\n }))\n\n // Call askUser and wait for user response\n const answers = await context.askUser(userQuestions)\n\n // Return the result\n yield {\n type: 'result',\n data: {\n answers,\n timestamp: Date.now(),\n },\n resultForAssistant: `User answered:\\n${answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n } catch (error) {\n // User cancelled or error occurred\n throw new Error(\n `Failed to get user response: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n },\n} satisfies Tool<typeof inputSchema, AskUserQuestionOutput>\n"],
5
+ "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAS,SAAS;AAElB,SAAS,sCAAsC;AAC/C,SAAS,aAAa,cAAc;AAGpC,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,WAAW,EACR;AAAA,IACC,EAAE,OAAO;AAAA,MACP,UAAU,EAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,MAC5D,QAAQ,EACL,OAAO,EACP,IAAI,EAAE,EACN,SAAS,EACT,SAAS,qCAAqC;AAAA,MACjD,SAAS,EACN;AAAA,QACC,EAAE,OAAO;AAAA,UACP,OAAO,EAAE,OAAO,EAAE,SAAS,qBAAqB;AAAA,UAChD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,QAClE,CAAC;AAAA,MACH,EACC,IAAI,CAAC,EACL,IAAI,CAAC,EACL,SAAS,EACT,SAAS,mCAAmC;AAAA,MAC/C,aAAa,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,KAAK,EACb,SAAS,2BAA2B;AAAA,IACzC,CAAC;AAAA,EACH,EACC,IAAI,CAAC,EACL,IAAI,CAAC,EACL,SAAS,kCAAkC;AAChD,CAAC;AAOM,MAAM,sBAAsB;AAAA,EACjC,MAAM;AAAA,EAEN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EAEA;AAAA,EAEA,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EAEA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EAEA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,yBAAyB,QAA+B;AACtD,UAAM,mBAAmB,OAAO,QAC7B,IAAI,YAAU;AACb,UAAI,OAAO,aAAa;AACtB,eAAO,IAAI,OAAO,gBAAgB,CAAC,MAAM,OAAO,WAAW;AAAA,MAC7D;AACA,UAAI,OAAO,iBAAiB;AAC1B,eAAO,IAAI,OAAO,gBAAgB,CAAC,YAAY,OAAO,gBAAgB,IAAI,OAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAClG;AACA,aAAO,IAAI,OAAO,gBAAgB,CAAC;AAAA,IACrC,CAAC,EACA,KAAK,IAAI;AAEZ,WAAO;AAAA,EAAmB,gBAAgB;AAAA,EAC5C;AAAA,EAEA,qBAAqB,OAAoC;AACvD,UAAM,gBAAgB,MAAM,UAAU;AACtC,UAAM,eACJ,kBAAkB,IACd,MAAM,UAAU,CAAC,EAAG,WACpB,GAAG,aAAa;AACtB,WAAO,gBAAgB,YAAY;AAAA,EACrC;AAAA,EAEA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EAEA,wBAAwB,QAA+B;AAErD,QAAI,CAAC,QAAQ;AACX,aACE,oCAAC,OAAI,eAAc,UAAS,aAAa,KACvC,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAM,aAAW,MAAK,SAAE,GAC9B,oCAAC,YAAK,oBAAkB,CAC1B,CACF;AAAA,IAEJ;AAEA,UAAM,UAAU,OAAO,WAAW,CAAC;AAEnC,WACE,oCAAC,OAAI,eAAc,UAAS,aAAa,KACvC,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAM,aAAW,MAAK,SAAE,GAC9B,oCAAC,YAAK,uBAAqB,CAC7B,GACC,QAAQ,IAAI,CAAC,QAAQ,UAAU;AAE9B,UAAI,CAAC,OAAQ,QAAO;AACpB,aACE,oCAAC,OAAI,KAAK,OAAO,eAAc,OAAM,aAAa,KAChD,oCAAC,QAAK,OAAM,aAAU,SAAE,GACvB,OAAO,cACN,oCAAC,YAAK,MACD,OAAO,iBAAiB,SAAS,GAAE,OAAI,OAAO,aAAY,GAC/D,IACE,OAAO,kBACT,oCAAC,YAAK,MACD,OAAO,iBAAiB,SAAS,GAAE,YAAS,KAC9C,OAAO,gBAAgB,IAAI,OAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CACnD,IAEA,oCAAC,QAAK,UAAQ,QAAC,MAAG,OAAO,iBAAiB,SAAS,GAAE,aAAW,CAEpE;AAAA,IAEJ,CAAC,CACH;AAAA,EAEJ;AAAA,EAEA,OAAO,KACL,EAAE,UAAU,GACZ,SACA;AAEA,QAAI,CAAC,QAAQ,SAAS;AACpB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AAEF,YAAM,gBAAgB,UAAU,IAAI,QAAM;AAAA,QACxC,UAAU,EAAE;AAAA,QACZ,QAAQ,EAAE;AAAA,QACV,SAAS,EAAE,SAAS,IAAI,UAAQ;AAAA,UAC9B,OAAO,IAAI;AAAA,UACX,aAAa,IAAI;AAAA,QACnB,EAAE;AAAA,QACF,aAAa,EAAE,eAAe;AAAA,MAChC,EAAE;AAGF,YAAM,UAAU,MAAM,QAAQ,QAAQ,aAAa;AAGnD,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB;AAAA,QACA,oBAAoB;AAAA,EAAmB,QACpC,IAAI,YAAU;AACb,cAAI,OAAO,aAAa;AACtB,mBAAO,IAAI,OAAO,gBAAgB,CAAC,MAAM,OAAO,WAAW;AAAA,UAC7D;AACA,cAAI,OAAO,iBAAiB;AAC1B,mBAAO,IAAI,OAAO,gBAAgB,CAAC,YAAY,OAAO,gBAAgB,IAAI,OAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,UAClG;AACA,iBAAO,IAAI,OAAO,gBAAgB,CAAC;AAAA,QACrC,CAAC,EACA,KAAK,IAAI,CAAC;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AAEd,YAAM,IAAI;AAAA,QACR,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MAC1F;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -78,21 +78,29 @@ const FileReadTool = {
78
78
  return entries.map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(", ");
79
79
  },
80
80
  renderToolResultMessage(output) {
81
+ if (!output || !output.type) {
82
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, null, "File read completed")));
83
+ }
81
84
  const verbose = false;
82
85
  switch (output.type) {
83
86
  case "image":
84
87
  return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, null, "Read image")));
85
88
  case "text": {
89
+ if (!output.file) {
90
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, null, "File read completed")));
91
+ }
86
92
  const { filePath, content, numLines } = output.file;
87
93
  const contentWithFallback = content || "(No content)";
88
94
  return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(
89
95
  HighlightedCode,
90
96
  {
91
97
  code: verbose ? contentWithFallback : contentWithFallback.split("\n").slice(0, MAX_LINES_TO_RENDER).filter((_) => _.trim() !== "").join("\n"),
92
- language: extname(filePath).slice(1)
98
+ language: extname(filePath || "").slice(1)
93
99
  }
94
- ), !verbose && numLines > MAX_LINES_TO_RENDER && /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "... (+", numLines - MAX_LINES_TO_RENDER, " lines)"))));
100
+ ), !verbose && (numLines || 0) > MAX_LINES_TO_RENDER && /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "... (+", (numLines || 0) - MAX_LINES_TO_RENDER, " lines)"))));
95
101
  }
102
+ default:
103
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, null, "File read completed")));
96
104
  }
97
105
  },
98
106
  renderToolUseRejectedMessage() {
@@ -179,20 +187,31 @@ const FileReadTool = {
179
187
  };
180
188
  },
181
189
  renderResultForAssistant(data) {
190
+ if (!data || !data.type) {
191
+ return "File read completed";
192
+ }
182
193
  switch (data.type) {
183
194
  case "image":
195
+ if (!data.file) {
196
+ return "Image read completed";
197
+ }
184
198
  return [
185
199
  {
186
200
  type: "image",
187
201
  source: {
188
202
  type: "base64",
189
- data: data.file.base64,
190
- media_type: data.file.type
203
+ data: data.file.base64 || "",
204
+ media_type: data.file.type || "image/png"
191
205
  }
192
206
  }
193
207
  ];
194
208
  case "text":
209
+ if (!data.file) {
210
+ return "File read completed";
211
+ }
195
212
  return addLineNumbers(data.file);
213
+ default:
214
+ return "File read completed";
196
215
  }
197
216
  }
198
217
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/FileReadTool/FileReadTool.tsx"],
4
- "sourcesContent": ["import { ImageBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { statSync } from 'node:fs'\nimport { Box, Text } from 'ink'\nimport * as path from 'node:path'\nimport { extname, relative } from 'node:path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport type { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport {\n addLineNumbers,\n findSimilarFile,\n normalizeFilePath,\n readTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport {\n recordFileRead,\n generateFileModificationReminder,\n} from '@services/fileFreshness'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\nimport { secureFileService } from '@utils/secureFile'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_OUTPUT_SIZE = 0.25 * 1024 * 1024 // 0.25MB in bytes\n\n// Common image extensions\nconst IMAGE_EXTENSIONS = new Set([\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.webp',\n])\n\n// Maximum dimensions for images\nconst MAX_WIDTH = 2000\nconst MAX_HEIGHT = 2000\nconst MAX_IMAGE_SIZE = 3.75 * 1024 * 1024 // 5MB in bytes, with base64 encoding\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once',\n ),\n limit: z\n .number()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once.',\n ),\n})\n\nexport const FileReadTool = {\n name: 'View',\n async description() {\n return DESCRIPTION\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // FileRead is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Read'\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ file_path }) {\n return !hasReadPermission(file_path || getCwd())\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, ...rest } = input\n const entries = [\n ['file_path', verbose ? file_path : relative(getCwd(), file_path)],\n ...Object.entries(rest),\n ]\n return entries\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(output) {\n const verbose = false // Set default value for verbose\n // TODO: Render recursively\n switch (output.type) {\n case 'image':\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>Read image</Text>\n </Box>\n </Box>\n )\n case 'text': {\n const { filePath, content, numLines } = output.file\n const contentWithFallback = content || '(No content)'\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Box flexDirection=\"column\">\n <HighlightedCode\n code={\n verbose\n ? contentWithFallback\n : contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')\n }\n language={extname(filePath).slice(1)}\n />\n {!verbose && numLines > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{numLines - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n )\n }\n }\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n async validateInput({ file_path, offset, limit }) {\n const fullFilePath = normalizeFilePath(file_path)\n\n // Use secure file service to check if file exists and get file info\n const fileCheck = secureFileService.safeGetFileInfo(fullFilePath)\n if (!fileCheck.success) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n const stats = fileCheck.stats!\n const fileSize = stats.size\n const ext = path.extname(fullFilePath).toLowerCase()\n\n // Skip size check for image files - they have their own size limits\n if (!IMAGE_EXTENSIONS.has(ext)) {\n // If file is too large and no offset/limit provided\n if (fileSize > MAX_OUTPUT_SIZE && !offset && !limit) {\n return {\n result: false,\n message: formatFileSizeError(fileSize),\n meta: { fileSize },\n }\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, offset = 1, limit = undefined },\n { readFileTimestamps },\n ) {\n const ext = path.extname(file_path).toLowerCase()\n const fullFilePath = normalizeFilePath(file_path)\n\n // Record file read for freshness tracking\n recordFileRead(fullFilePath)\n\n // Emit file read event for system reminders\n emitReminderEvent('file:read', {\n filePath: fullFilePath,\n extension: ext,\n timestamp: Date.now(),\n })\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = Date.now()\n\n // Check for file modifications and generate reminder if needed\n const modificationReminder = generateFileModificationReminder(fullFilePath)\n if (modificationReminder) {\n emitReminderEvent('file:modified', {\n filePath: fullFilePath,\n reminder: modificationReminder,\n timestamp: Date.now(),\n })\n }\n\n // If it's an image file, process and return base64 encoded contents\n if (IMAGE_EXTENSIONS.has(ext)) {\n const data = await readImage(fullFilePath, ext)\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n // Handle offset properly - if offset is 0, don't subtract 1\n const lineOffset = offset === 0 ? 0 : offset - 1\n const { content, lineCount, totalLines } = readTextContent(\n fullFilePath,\n lineOffset,\n limit,\n )\n\n // Add size validation after reading for non-image files\n if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {\n throw new Error(formatFileSizeError(content.length))\n }\n\n const data = {\n type: 'text' as const,\n file: {\n filePath: file_path,\n content: content,\n numLines: lineCount,\n startLine: offset,\n totalLines,\n },\n }\n\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant(data) {\n switch (data.type) {\n case 'image':\n return [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: data.file.base64,\n media_type: data.file.type,\n },\n },\n ]\n case 'text':\n return addLineNumbers(data.file)\n }\n },\n} satisfies Tool<\n typeof inputSchema,\n | {\n type: 'text'\n file: {\n filePath: string\n content: string\n numLines: number\n startLine: number\n totalLines: number\n }\n }\n | {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n }\n>\n\nconst formatFileSizeError = (sizeInBytes: number) =>\n `File content (${Math.round(sizeInBytes / 1024)}KB) exceeds maximum allowed size (${Math.round(MAX_OUTPUT_SIZE / 1024)}KB). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.`\n\nfunction createImageResponse(\n buffer: Buffer,\n ext: string,\n): {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n} {\n return {\n type: 'image',\n file: {\n base64: buffer.toString('base64'),\n type: `image/${ext.slice(1)}` as ImageBlockParam.Source['media_type'],\n },\n }\n}\n\nasync function readImage(\n filePath: string,\n ext: string,\n): Promise<{\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n}> {\n try {\n const stats = statSync(filePath)\n const sharp = (\n (await import('sharp')) as unknown as { default: typeof import('sharp') }\n ).default\n\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n const image = sharp(fileReadResult.content as Buffer)\n const metadata = await image.metadata()\n\n if (!metadata.width || !metadata.height) {\n if (stats.size > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n }\n\n // Calculate dimensions while maintaining aspect ratio\n let width = metadata.width || 0\n let height = metadata.height || 0\n\n // Check if the original file just works\n if (\n stats.size <= MAX_IMAGE_SIZE &&\n width <= MAX_WIDTH &&\n height <= MAX_HEIGHT\n ) {\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n\n if (width > MAX_WIDTH) {\n height = Math.round((height * MAX_WIDTH) / width)\n width = MAX_WIDTH\n }\n\n if (height > MAX_HEIGHT) {\n width = Math.round((width * MAX_HEIGHT) / height)\n height = MAX_HEIGHT\n }\n\n // Resize image and convert to buffer\n const resizedImageBuffer = await image\n .resize(width, height, {\n fit: 'inside',\n withoutEnlargement: true,\n })\n .toBuffer()\n\n // If still too large after resize, compress quality\n if (resizedImageBuffer.length > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n\n return createImageResponse(resizedImageBuffer, ext)\n } catch (e) {\n logError(e)\n // If any error occurs during processing, return original image\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { ImageBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { statSync } from 'node:fs'\nimport { Box, Text } from 'ink'\nimport * as path from 'node:path'\nimport { extname, relative } from 'node:path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport type { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport {\n addLineNumbers,\n findSimilarFile,\n normalizeFilePath,\n readTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport {\n recordFileRead,\n generateFileModificationReminder,\n} from '@services/fileFreshness'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\nimport { secureFileService } from '@utils/secureFile'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_OUTPUT_SIZE = 0.25 * 1024 * 1024 // 0.25MB in bytes\n\n// Common image extensions\nconst IMAGE_EXTENSIONS = new Set([\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.webp',\n])\n\n// Maximum dimensions for images\nconst MAX_WIDTH = 2000\nconst MAX_HEIGHT = 2000\nconst MAX_IMAGE_SIZE = 3.75 * 1024 * 1024 // 5MB in bytes, with base64 encoding\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once',\n ),\n limit: z\n .number()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once.',\n ),\n})\n\nexport const FileReadTool = {\n name: 'View',\n async description() {\n return DESCRIPTION\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // FileRead is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Read'\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ file_path }) {\n return !hasReadPermission(file_path || getCwd())\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, ...rest } = input\n const entries = [\n ['file_path', verbose ? file_path : relative(getCwd(), file_path)],\n ...Object.entries(rest),\n ]\n return entries\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output || !output.type) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n\n const verbose = false // Set default value for verbose\n // TODO: Render recursively\n switch (output.type) {\n case 'image':\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>Read image</Text>\n </Box>\n </Box>\n )\n case 'text': {\n // Guard against missing file property\n if (!output.file) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n const { filePath, content, numLines } = output.file\n const contentWithFallback = content || '(No content)'\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Box flexDirection=\"column\">\n <HighlightedCode\n code={\n verbose\n ? contentWithFallback\n : contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')\n }\n language={extname(filePath || '').slice(1)}\n />\n {!verbose && (numLines || 0) > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{(numLines || 0) - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n )\n }\n default:\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n async validateInput({ file_path, offset, limit }) {\n const fullFilePath = normalizeFilePath(file_path)\n\n // Use secure file service to check if file exists and get file info\n const fileCheck = secureFileService.safeGetFileInfo(fullFilePath)\n if (!fileCheck.success) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n const stats = fileCheck.stats!\n const fileSize = stats.size\n const ext = path.extname(fullFilePath).toLowerCase()\n\n // Skip size check for image files - they have their own size limits\n if (!IMAGE_EXTENSIONS.has(ext)) {\n // If file is too large and no offset/limit provided\n if (fileSize > MAX_OUTPUT_SIZE && !offset && !limit) {\n return {\n result: false,\n message: formatFileSizeError(fileSize),\n meta: { fileSize },\n }\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, offset = 1, limit = undefined },\n { readFileTimestamps },\n ) {\n const ext = path.extname(file_path).toLowerCase()\n const fullFilePath = normalizeFilePath(file_path)\n\n // Record file read for freshness tracking\n recordFileRead(fullFilePath)\n\n // Emit file read event for system reminders\n emitReminderEvent('file:read', {\n filePath: fullFilePath,\n extension: ext,\n timestamp: Date.now(),\n })\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = Date.now()\n\n // Check for file modifications and generate reminder if needed\n const modificationReminder = generateFileModificationReminder(fullFilePath)\n if (modificationReminder) {\n emitReminderEvent('file:modified', {\n filePath: fullFilePath,\n reminder: modificationReminder,\n timestamp: Date.now(),\n })\n }\n\n // If it's an image file, process and return base64 encoded contents\n if (IMAGE_EXTENSIONS.has(ext)) {\n const data = await readImage(fullFilePath, ext)\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n // Handle offset properly - if offset is 0, don't subtract 1\n const lineOffset = offset === 0 ? 0 : offset - 1\n const { content, lineCount, totalLines } = readTextContent(\n fullFilePath,\n lineOffset,\n limit,\n )\n\n // Add size validation after reading for non-image files\n if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {\n throw new Error(formatFileSizeError(content.length))\n }\n\n const data = {\n type: 'text' as const,\n file: {\n filePath: file_path,\n content: content,\n numLines: lineCount,\n startLine: offset,\n totalLines,\n },\n }\n\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant(data) {\n // Guard against undefined or null data\n if (!data || !data.type) {\n return 'File read completed'\n }\n\n switch (data.type) {\n case 'image':\n // Guard against missing file property\n if (!data.file) {\n return 'Image read completed'\n }\n return [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: data.file.base64 || '',\n media_type: data.file.type || 'image/png',\n },\n },\n ]\n case 'text':\n // Guard against missing file property\n if (!data.file) {\n return 'File read completed'\n }\n return addLineNumbers(data.file)\n default:\n return 'File read completed'\n }\n },\n} satisfies Tool<\n typeof inputSchema,\n | {\n type: 'text'\n file: {\n filePath: string\n content: string\n numLines: number\n startLine: number\n totalLines: number\n }\n }\n | {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n }\n>\n\nconst formatFileSizeError = (sizeInBytes: number) =>\n `File content (${Math.round(sizeInBytes / 1024)}KB) exceeds maximum allowed size (${Math.round(MAX_OUTPUT_SIZE / 1024)}KB). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.`\n\nfunction createImageResponse(\n buffer: Buffer,\n ext: string,\n): {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n} {\n return {\n type: 'image',\n file: {\n base64: buffer.toString('base64'),\n type: `image/${ext.slice(1)}` as ImageBlockParam.Source['media_type'],\n },\n }\n}\n\nasync function readImage(\n filePath: string,\n ext: string,\n): Promise<{\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n}> {\n try {\n const stats = statSync(filePath)\n const sharp = (\n (await import('sharp')) as unknown as { default: typeof import('sharp') }\n ).default\n\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n const image = sharp(fileReadResult.content as Buffer)\n const metadata = await image.metadata()\n\n if (!metadata.width || !metadata.height) {\n if (stats.size > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n }\n\n // Calculate dimensions while maintaining aspect ratio\n let width = metadata.width || 0\n let height = metadata.height || 0\n\n // Check if the original file just works\n if (\n stats.size <= MAX_IMAGE_SIZE &&\n width <= MAX_WIDTH &&\n height <= MAX_HEIGHT\n ) {\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n\n if (width > MAX_WIDTH) {\n height = Math.round((height * MAX_WIDTH) / width)\n width = MAX_WIDTH\n }\n\n if (height > MAX_HEIGHT) {\n width = Math.round((width * MAX_HEIGHT) / height)\n height = MAX_HEIGHT\n }\n\n // Resize image and convert to buffer\n const resizedImageBuffer = await image\n .resize(width, height, {\n fit: 'inside',\n withoutEnlargement: true,\n })\n .toBuffer()\n\n // If still too large after resize, compress quality\n if (resizedImageBuffer.length > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n\n return createImageResponse(resizedImageBuffer, ext)\n } catch (e) {\n logError(e)\n // If any error occurs during processing, return original image\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["data", "fileReadResult"]
7
7
  }
@@ -47,10 +47,19 @@ const GlobTool = {
47
47
  return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
48
48
  },
49
49
  renderToolResultMessage(output) {
50
+ if (!output) {
51
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Search completed")));
52
+ }
50
53
  if (typeof output === "string") {
51
- output = JSON.parse(output);
54
+ try {
55
+ output = JSON.parse(output);
56
+ } catch {
57
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Search completed")));
58
+ }
52
59
  }
53
- return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Found "), /* @__PURE__ */ React.createElement(Text, { bold: true }, output.numFiles, " "), /* @__PURE__ */ React.createElement(Text, null, output.numFiles === 0 || output.numFiles > 1 ? "files" : "file")), /* @__PURE__ */ React.createElement(Cost, { costUSD: 0, durationMs: output.durationMs, debug: false }));
60
+ const numFiles = output?.numFiles ?? 0;
61
+ const durationMs = output?.durationMs ?? 0;
62
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Found "), /* @__PURE__ */ React.createElement(Text, { bold: true }, numFiles, " "), /* @__PURE__ */ React.createElement(Text, null, numFiles === 0 || numFiles > 1 ? "files" : "file")), /* @__PURE__ */ React.createElement(Cost, { costUSD: 0, durationMs, debug: false }));
54
63
  },
55
64
  async *call({ pattern, path }, { abortController }) {
56
65
  const start = Date.now();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/GlobTool/GlobTool.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n output = JSON.parse(output) as Output\n }\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{output.numFiles} </Text>\n <Text>\n {output.numFiles === 0 || output.numFiles > 1 ? 'files' : 'file'}\n </Text>\n </Box>\n <Cost costUSD={0} durationMs={output.durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ pattern, path }, { abortController }) {\n const start = Date.now()\n const { files, truncated } = await glob(\n pattern,\n path ?? getCwd(),\n { limit: 100, offset: 0 },\n abortController.signal,\n )\n const output: Output = {\n filenames: files,\n durationMs: Date.now() - start,\n numFiles: files.length,\n truncated,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n let result = output.filenames.join('\\n')\n if (output.filenames.length === 0) {\n result = 'No files found'\n }\n // Only add truncation message if results were actually truncated\n else if (output.truncated) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n} satisfies Tool<typeof inputSchema, Output>\n"],
5
- "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,SAAS;AAClB,SAAS,YAAY;AACrB,SAAS,sCAAsC;AAE/C,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,aAAa,4BAA4B;AAClD,SAAS,YAAY,UAAU,eAAe;AAC9C,SAAS,yBAAyB;AAElC,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,SAAS,EAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,EACtE,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AASM,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EACA;AAAA,EACA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB,EAAE,KAAK,GAAG;AACzB,WAAO,CAAC,kBAAkB,QAAQ,OAAO,CAAC;AAAA,EAC5C;AAAA,EACA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,EAAE,SAAS,KAAK,GAAG,EAAE,QAAQ,GAAG;AACnD,UAAM,eAAe,OACjB,WAAW,IAAI,IACb,OACA,QAAQ,OAAO,GAAG,IAAI,IACxB;AACJ,UAAM,eAAe,eACjB,SAAS,OAAO,GAAG,YAAY,IAC/B;AACJ,WAAO,aAAa,OAAO,IAAI,gBAAgB,UAAU,YAAY,UAAU,eAAe,YAAY,MAAM,EAAE;AAAA,EACpH;AAAA,EACA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ;AAE9B,QAAI,OAAO,WAAW,UAAU;AAC9B,eAAS,KAAK,MAAM,MAAM;AAAA,IAC5B;AAEA,WACE,oCAAC,OAAI,gBAAe,iBAAgB,OAAM,UACxC,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,2BAA0B,GAChC,oCAAC,QAAK,MAAI,QAAE,OAAO,UAAS,GAAC,GAC7B,oCAAC,YACE,OAAO,aAAa,KAAK,OAAO,WAAW,IAAI,UAAU,MAC5D,CACF,GACA,oCAAC,QAAK,SAAS,GAAG,YAAY,OAAO,YAAY,OAAO,OAAO,CACjE;AAAA,EAEJ;AAAA,EACA,OAAO,KAAK,EAAE,SAAS,KAAK,GAAG,EAAE,gBAAgB,GAAG;AAClD,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,EAAE,OAAO,UAAU,IAAI,MAAM;AAAA,MACjC;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,EAAE,OAAO,KAAK,QAAQ,EAAE;AAAA,MACxB,gBAAgB;AAAA,IAClB;AACA,UAAM,SAAiB;AAAA,MACrB,WAAW;AAAA,MACX,YAAY,KAAK,IAAI,IAAI;AAAA,MACzB,UAAU,MAAM;AAAA,MAChB;AAAA,IACF;AACA,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,oBAAoB,KAAK,yBAAyB,MAAM;AAAA,MACxD,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EACA,yBAAyB,QAAQ;AAC/B,QAAI,SAAS,OAAO,UAAU,KAAK,IAAI;AACvC,QAAI,OAAO,UAAU,WAAW,GAAG;AACjC,eAAS;AAAA,IACX,WAES,OAAO,WAAW;AACzB,gBACE;AAAA,IACJ;AACA,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numFiles = output?.numFiles ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ pattern, path }, { abortController }) {\n const start = Date.now()\n const { files, truncated } = await glob(\n pattern,\n path ?? getCwd(),\n { limit: 100, offset: 0 },\n abortController.signal,\n )\n const output: Output = {\n filenames: files,\n durationMs: Date.now() - start,\n numFiles: files.length,\n truncated,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n let result = output.filenames.join('\\n')\n if (output.filenames.length === 0) {\n result = 'No files found'\n }\n // Only add truncation message if results were actually truncated\n else if (output.truncated) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n} satisfies Tool<typeof inputSchema, Output>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -51,10 +51,15 @@ const GrepTool = {
51
51
  return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
52
52
  },
53
53
  renderToolResultMessage(output) {
54
+ if (!output) {
55
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Search completed")));
56
+ }
54
57
  if (typeof output === "string") {
55
58
  output = output;
56
59
  }
57
- return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Found "), /* @__PURE__ */ React.createElement(Text, { bold: true }, output.numFiles, " "), /* @__PURE__ */ React.createElement(Text, null, output.numFiles === 0 || output.numFiles > 1 ? "files" : "file")), /* @__PURE__ */ React.createElement(Cost, { costUSD: 0, durationMs: output.durationMs, debug: false }));
60
+ const numFiles = output?.numFiles ?? 0;
61
+ const durationMs = output?.durationMs ?? 0;
62
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Found "), /* @__PURE__ */ React.createElement(Text, { bold: true }, numFiles, " "), /* @__PURE__ */ React.createElement(Text, null, numFiles === 0 || numFiles > 1 ? "files" : "file")), /* @__PURE__ */ React.createElement(Cost, { costUSD: 0, durationMs, debug: false }));
58
63
  },
59
64
  renderResultForAssistant({ numFiles, filenames }) {
60
65
  if (numFiles === 0) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/GrepTool/GrepTool.tsx"],
4
- "sourcesContent": ["import { stat } from 'fs/promises'\nimport { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { getAbsolutePath, getAbsoluteAndRelativePaths } from '@utils/file'\nimport { ripGrep } from '@utils/ripgrep'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n include: z\n .string()\n .optional()\n .describe(\n 'File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\")',\n ),\n})\n\nconst MAX_RESULTS = 100\n\ntype Input = typeof inputSchema\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n}\n\nexport const GrepTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GrepTool is read-only, safe for concurrent execution\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path, include }, { verbose }) {\n const { absolutePath, relativePath } = getAbsoluteAndRelativePaths(path)\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}${include ? `, include: \"${include}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n // Convert string to Output type using tmpDeserializeOldLogResult if needed\n output = output as unknown as Output\n }\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{output.numFiles} </Text>\n <Text>\n {output.numFiles === 0 || output.numFiles > 1 ? 'files' : 'file'}\n </Text>\n </Box>\n <Cost costUSD={0} durationMs={output.durationMs} debug={false} />\n </Box>\n )\n },\n renderResultForAssistant({ numFiles, filenames }) {\n if (numFiles === 0) {\n return 'No files found'\n }\n let result = `Found ${numFiles} file${numFiles === 1 ? '' : 's'}\\n${filenames.slice(0, MAX_RESULTS).join('\\n')}`\n if (numFiles > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n async *call({ pattern, path, include }, { abortController }) {\n const start = Date.now()\n const absolutePath = getAbsolutePath(path) || getCwd()\n\n const args = ['-li', pattern]\n if (include) {\n args.push('--glob', include)\n }\n\n const results = await ripGrep(args, absolutePath, abortController.signal)\n\n const stats = await Promise.all(results.map(_ => stat(_)))\n const matches = results\n // Sort by modification time\n .map((_, i) => [_, stats[i]!] as const)\n .sort((a, b) => {\n if (process.env.NODE_ENV === 'test') {\n // In tests, we always want to sort by filename, so that results are deterministic\n return a[0].localeCompare(b[0])\n }\n const timeComparison = (b[1].mtimeMs ?? 0) - (a[1].mtimeMs ?? 0)\n if (timeComparison === 0) {\n // Sort by filename as a tiebreaker\n return a[0].localeCompare(b[0])\n }\n return timeComparison\n })\n .map(_ => _[0])\n\n const output = {\n filenames: matches,\n durationMs: Date.now() - start,\n numFiles: matches.length,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n} satisfies Tool<Input, Output>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { stat } from 'fs/promises'\nimport { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { getAbsolutePath, getAbsoluteAndRelativePaths } from '@utils/file'\nimport { ripGrep } from '@utils/ripgrep'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n include: z\n .string()\n .optional()\n .describe(\n 'File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\")',\n ),\n})\n\nconst MAX_RESULTS = 100\n\ntype Input = typeof inputSchema\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n}\n\nexport const GrepTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GrepTool is read-only, safe for concurrent execution\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path, include }, { verbose }) {\n const { absolutePath, relativePath } = getAbsoluteAndRelativePaths(path)\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}${include ? `, include: \"${include}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n // Convert string to Output type using tmpDeserializeOldLogResult if needed\n output = output as unknown as Output\n }\n\n const numFiles = output?.numFiles ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n renderResultForAssistant({ numFiles, filenames }) {\n if (numFiles === 0) {\n return 'No files found'\n }\n let result = `Found ${numFiles} file${numFiles === 1 ? '' : 's'}\\n${filenames.slice(0, MAX_RESULTS).join('\\n')}`\n if (numFiles > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n async *call({ pattern, path, include }, { abortController }) {\n const start = Date.now()\n const absolutePath = getAbsolutePath(path) || getCwd()\n\n const args = ['-li', pattern]\n if (include) {\n args.push('--glob', include)\n }\n\n const results = await ripGrep(args, absolutePath, abortController.signal)\n\n const stats = await Promise.all(results.map(_ => stat(_)))\n const matches = results\n // Sort by modification time\n .map((_, i) => [_, stats[i]!] as const)\n .sort((a, b) => {\n if (process.env.NODE_ENV === 'test') {\n // In tests, we always want to sort by filename, so that results are deterministic\n return a[0].localeCompare(b[0])\n }\n const timeComparison = (b[1].mtimeMs ?? 0) - (a[1].mtimeMs ?? 0)\n if (timeComparison === 0) {\n // Sort by filename as a tiebreaker\n return a[0].localeCompare(b[0])\n }\n return timeComparison\n })\n .map(_ => _[0])\n\n const output = {\n filenames: matches,\n durationMs: Date.now() - start,\n numFiles: matches.length,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n} satisfies Tool<Input, Output>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -48,8 +48,14 @@ const MCPTool = {
48
48
  },
49
49
  renderToolResultMessage(output) {
50
50
  const verbose = false;
51
+ if (!output) {
52
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "(No content)")));
53
+ }
51
54
  if (Array.isArray(output)) {
52
55
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, output.map((item, i) => {
56
+ if (!item) {
57
+ return null;
58
+ }
53
59
  if (item.type === "image") {
54
60
  return /* @__PURE__ */ React.createElement(
55
61
  Box,
@@ -62,20 +68,13 @@ const MCPTool = {
62
68
  /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, null, "[Image]"))
63
69
  );
64
70
  }
65
- const lines2 = item.text.split("\n").length;
66
- return /* @__PURE__ */ React.createElement(
67
- OutputLine,
68
- {
69
- key: i,
70
- content: item.text,
71
- lines: lines2,
72
- verbose
73
- }
74
- );
71
+ const text = item.text || "";
72
+ const lines2 = text.split("\n").length;
73
+ return /* @__PURE__ */ React.createElement(OutputLine, { key: i, content: text, lines: lines2, verbose });
75
74
  }));
76
75
  }
77
- if (!output) {
78
- return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "(No content)")));
76
+ if (typeof output !== "string") {
77
+ return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "MCP operation completed")));
79
78
  }
80
79
  const lines = output.split("\n").length;
81
80
  return /* @__PURE__ */ React.createElement(OutputLine, { content: output, lines, verbose });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/MCPTool/MCPTool.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { type Tool } from '@tool'\nimport { getTheme } from '@utils/theme'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { OutputLine } from '@tools/BashTool/OutputLine'\n\n// Allow any input object since MCP tools define their own schemas\nconst inputSchema = z.object({}).passthrough()\n\nexport const MCPTool = {\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // MCPTool can modify state through MCP calls, not safe for concurrent execution\n },\n // Overridden in mcpClient.ts\n name: 'mcp',\n // Overridden in mcpClient.ts\n async description() {\n return DESCRIPTION\n },\n // Overridden in mcpClient.ts\n async prompt() {\n return PROMPT\n },\n inputSchema,\n // Overridden in mcpClient.ts\n async *call() {\n yield {\n type: 'result',\n data: '',\n resultForAssistant: '',\n }\n },\n needsPermissions() {\n return true\n },\n renderToolUseMessage(input) {\n return Object.entries(input)\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n // Overridden in mcpClient.ts\n userFacingName: () => 'mcp',\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n const verbose = false // Set default value for verbose\n if (Array.isArray(output)) {\n return (\n <Box flexDirection=\"column\">\n {output.map((item, i) => {\n if (item.type === 'image') {\n return (\n <Box\n key={i}\n justifyContent=\"space-between\"\n overflowX=\"hidden\"\n width=\"100%\"\n >\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>[Image]</Text>\n </Box>\n </Box>\n )\n }\n const lines = item.text.split('\\n').length\n return (\n <OutputLine\n key={i}\n content={item.text}\n lines={lines}\n verbose={verbose}\n />\n )\n })}\n </Box>\n )\n }\n\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={getTheme().secondaryText}>(No content)</Text>\n </Box>\n </Box>\n )\n }\n\n const lines = output.split('\\n').length\n return <OutputLine content={output} lines={lines} verbose={verbose} />\n },\n renderResultForAssistant(content) {\n return content\n },\n} satisfies Tool<typeof inputSchema, string>\n"],
5
- "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAE/C,SAAS,gBAAgB;AACzB,SAAS,aAAa,cAAc;AACpC,SAAS,kBAAkB;AAG3B,MAAM,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAEtC,MAAM,UAAU;AAAA,EACrB,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM;AAAA;AAAA,EAEN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA;AAAA;AAAA,EAEA,OAAO,OAAO;AACZ,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,oBAAoB;AAAA,IACtB;AAAA,EACF;AAAA,EACA,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,OAAO;AAC1B,WAAO,OAAO,QAAQ,KAAK,EACxB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE,EACxD,KAAK,IAAI;AAAA,EACd;AAAA;AAAA,EAEA,gBAAgB,MAAM;AAAA,EACtB,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ;AAC9B,UAAM,UAAU;AAChB,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,aACE,oCAAC,OAAI,eAAc,YAChB,OAAO,IAAI,CAAC,MAAM,MAAM;AACvB,YAAI,KAAK,SAAS,SAAS;AACzB,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,gBAAe;AAAA,cACf,WAAU;AAAA,cACV,OAAM;AAAA;AAAA,YAEN,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,YAAK,SAAO,CACf;AAAA,UACF;AAAA,QAEJ;AACA,cAAMA,SAAQ,KAAK,KAAK,MAAM,IAAI,EAAE;AACpC,eACE;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,SAAS,KAAK;AAAA,YACd,OAAOA;AAAA,YACP;AAAA;AAAA,QACF;AAAA,MAEJ,CAAC,CACH;AAAA,IAEJ;AAEA,QAAI,CAAC,QAAQ;AACX,aACE,oCAAC,OAAI,gBAAe,iBAAgB,WAAU,UAAS,OAAM,UAC3D,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,QAAK,OAAO,SAAS,EAAE,iBAAe,cAAY,CACrD,CACF;AAAA,IAEJ;AAEA,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE;AACjC,WAAO,oCAAC,cAAW,SAAS,QAAQ,OAAc,SAAkB;AAAA,EACtE;AAAA,EACA,yBAAyB,SAAS;AAChC,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { type Tool } from '@tool'\nimport { getTheme } from '@utils/theme'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { OutputLine } from '@tools/BashTool/OutputLine'\n\n// Allow any input object since MCP tools define their own schemas\nconst inputSchema = z.object({}).passthrough()\n\nexport const MCPTool = {\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // MCPTool can modify state through MCP calls, not safe for concurrent execution\n },\n // Overridden in mcpClient.ts\n name: 'mcp',\n // Overridden in mcpClient.ts\n async description() {\n return DESCRIPTION\n },\n // Overridden in mcpClient.ts\n async prompt() {\n return PROMPT\n },\n inputSchema,\n // Overridden in mcpClient.ts\n async *call() {\n yield {\n type: 'result',\n data: '',\n resultForAssistant: '',\n }\n },\n needsPermissions() {\n return true\n },\n renderToolUseMessage(input) {\n return Object.entries(input)\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n // Overridden in mcpClient.ts\n userFacingName: () => 'mcp',\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n const verbose = false // Set default value for verbose\n\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={getTheme().secondaryText}>(No content)</Text>\n </Box>\n </Box>\n )\n }\n\n if (Array.isArray(output)) {\n return (\n <Box flexDirection=\"column\">\n {output.map((item, i) => {\n // Guard against null/undefined items in array\n if (!item) {\n return null\n }\n if (item.type === 'image') {\n return (\n <Box\n key={i}\n justifyContent=\"space-between\"\n overflowX=\"hidden\"\n width=\"100%\"\n >\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>[Image]</Text>\n </Box>\n </Box>\n )\n }\n // Guard against missing text property\n const text = item.text || ''\n const lines = text.split('\\n').length\n return (\n <OutputLine key={i} content={text} lines={lines} verbose={verbose} />\n )\n })}\n </Box>\n )\n }\n\n // Guard against non-string output\n if (typeof output !== 'string') {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={getTheme().secondaryText}>MCP operation completed</Text>\n </Box>\n </Box>\n )\n }\n\n const lines = output.split('\\n').length\n return <OutputLine content={output} lines={lines} verbose={verbose} />\n },\n renderResultForAssistant(content) {\n return content\n },\n} satisfies Tool<typeof inputSchema, string>\n"],
5
+ "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAE/C,SAAS,gBAAgB;AACzB,SAAS,aAAa,cAAc;AACpC,SAAS,kBAAkB;AAG3B,MAAM,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAEtC,MAAM,UAAU;AAAA,EACrB,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM;AAAA;AAAA,EAEN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA;AAAA;AAAA,EAEA,OAAO,OAAO;AACZ,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,oBAAoB;AAAA,IACtB;AAAA,EACF;AAAA,EACA,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,OAAO;AAC1B,WAAO,OAAO,QAAQ,KAAK,EACxB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE,EACxD,KAAK,IAAI;AAAA,EACd;AAAA;AAAA,EAEA,gBAAgB,MAAM;AAAA,EACtB,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ;AAC9B,UAAM,UAAU;AAGhB,QAAI,CAAC,QAAQ;AACX,aACE,oCAAC,OAAI,gBAAe,iBAAgB,WAAU,UAAS,OAAM,UAC3D,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,QAAK,OAAO,SAAS,EAAE,iBAAe,cAAY,CACrD,CACF;AAAA,IAEJ;AAEA,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,aACE,oCAAC,OAAI,eAAc,YAChB,OAAO,IAAI,CAAC,MAAM,MAAM;AAEvB,YAAI,CAAC,MAAM;AACT,iBAAO;AAAA,QACT;AACA,YAAI,KAAK,SAAS,SAAS;AACzB,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,gBAAe;AAAA,cACf,WAAU;AAAA,cACV,OAAM;AAAA;AAAA,YAEN,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,YAAK,SAAO,CACf;AAAA,UACF;AAAA,QAEJ;AAEA,cAAM,OAAO,KAAK,QAAQ;AAC1B,cAAMA,SAAQ,KAAK,MAAM,IAAI,EAAE;AAC/B,eACE,oCAAC,cAAW,KAAK,GAAG,SAAS,MAAM,OAAOA,QAAO,SAAkB;AAAA,MAEvE,CAAC,CACH;AAAA,IAEJ;AAGA,QAAI,OAAO,WAAW,UAAU;AAC9B,aACE,oCAAC,OAAI,gBAAe,iBAAgB,WAAU,UAAS,OAAM,UAC3D,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,QAAK,OAAO,SAAS,EAAE,iBAAe,yBAAuB,CAChE,CACF;AAAA,IAEJ;AAEA,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE;AACjC,WAAO,oCAAC,cAAW,SAAS,QAAQ,OAAc,SAAkB;AAAA,EACtE;AAAA,EACA,yBAAyB,SAAS;AAChC,WAAO;AAAA,EACT;AACF;",
6
6
  "names": ["lines"]
7
7
  }
@@ -89,11 +89,15 @@ const NotebookReadTool = {
89
89
  };
90
90
  },
91
91
  renderResultForAssistant(data) {
92
+ if (!data || !Array.isArray(data)) {
93
+ return "No cells found in notebook";
94
+ }
92
95
  return data.map((cell, index) => {
93
- let content = `Cell ${index + 1} (${cell.cellType}):
94
- ${cell.source}`;
95
- if (cell.outputs && cell.outputs.length > 0) {
96
- const outputText = cell.outputs.map((output) => output.text).filter(Boolean).join("\n");
96
+ if (!cell) return `Cell ${index + 1}: (empty)`;
97
+ let content = `Cell ${index + 1} (${cell.cellType || "unknown"}):
98
+ ${cell.source || ""}`;
99
+ if (cell.outputs && Array.isArray(cell.outputs) && cell.outputs.length > 0) {
100
+ const outputText = cell.outputs.filter((output) => output != null).map((output) => output.text || "").filter(Boolean).join("\n");
97
101
  if (outputText) {
98
102
  content += `
99
103
  Output: