add support for Grok live search (#20)
This commit is contained in:
1 parent
07ec1540c3
commit
1d45dd3d9e
8 files changed
+240
-144
No files matched your search
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@vibe-kit/grok-cli",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@vibe-kit/grok-cli",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vibe-kit/grok-cli",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"description": "An open-source AI agent that brings the power of Grok directly into your terminal.",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
|
||||
+17
-5
@@ -7,7 +7,7 @@ import { createTokenCounter, TokenCounter } from "../utils/token-counter";
|
||||
import { loadCustomInstructions } from "../utils/custom-instructions";
|
||||
|
||||
export interface ChatEntry {
|
||||
type: "user" | "assistant" | "tool_result";
|
||||
type: "user" | "assistant" | "tool_result" | "tool_call";
|
||||
content: string;
|
||||
timestamp: Date;
|
||||
toolCalls?: GrokToolCall[];
|
||||
@@ -64,6 +64,9 @@ You have access to these tools:
|
||||
- create_todo_list: Create a visual todo list for planning and tracking tasks
|
||||
- update_todo_list: Update existing todos in your todo list
|
||||
|
||||
REAL-TIME INFORMATION:
|
||||
You have access to real-time web search and X (Twitter) data. When users ask for current information, latest news, or recent events, you automatically have access to up-to-date information from the web and social media.
|
||||
|
||||
IMPORTANT TOOL USAGE RULES:
|
||||
- NEVER use create_file on files that already exist - this will overwrite them completely
|
||||
- ALWAYS use str_replace_editor to modify existing files, even for small changes
|
||||
@@ -127,7 +130,9 @@ Current working directory: ${process.cwd()}`,
|
||||
try {
|
||||
let currentResponse = await this.grokClient.chat(
|
||||
this.messages,
|
||||
GROK_TOOLS
|
||||
GROK_TOOLS,
|
||||
undefined,
|
||||
{ search_parameters: { mode: "auto" } }
|
||||
);
|
||||
|
||||
// Agent loop - continue until no more tool calls or max rounds reached
|
||||
@@ -191,7 +196,9 @@ Current working directory: ${process.cwd()}`,
|
||||
// Get next response - this might contain more tool calls
|
||||
currentResponse = await this.grokClient.chat(
|
||||
this.messages,
|
||||
GROK_TOOLS
|
||||
GROK_TOOLS,
|
||||
undefined,
|
||||
{ search_parameters: { mode: "auto" } }
|
||||
);
|
||||
} else {
|
||||
// No more tool calls, add final response
|
||||
@@ -270,7 +277,7 @@ Current working directory: ${process.cwd()}`,
|
||||
): AsyncGenerator<StreamingChunk, void, unknown> {
|
||||
// Create new abort controller for this request
|
||||
this.abortController = new AbortController();
|
||||
|
||||
|
||||
// Add user message to conversation
|
||||
const userEntry: ChatEntry = {
|
||||
type: "user",
|
||||
@@ -307,7 +314,12 @@ Current working directory: ${process.cwd()}`,
|
||||
}
|
||||
|
||||
// Stream response and accumulate
|
||||
const stream = this.grokClient.chatStream(this.messages, GROK_TOOLS);
|
||||
const stream = this.grokClient.chatStream(
|
||||
this.messages,
|
||||
GROK_TOOLS,
|
||||
undefined,
|
||||
{ search_parameters: { mode: "auto" } }
|
||||
);
|
||||
let accumulatedMessage: any = {};
|
||||
let accumulatedContent = "";
|
||||
let toolCallsYielded = false;
|
||||
|
||||
+61
-16
@@ -1,15 +1,15 @@
|
||||
import OpenAI from 'openai';
|
||||
import type { ChatCompletionMessageParam } from 'openai/resources/chat';
|
||||
import OpenAI from "openai";
|
||||
import type { ChatCompletionMessageParam } from "openai/resources/chat";
|
||||
|
||||
export type GrokMessage = ChatCompletionMessageParam;
|
||||
|
||||
export interface GrokTool {
|
||||
type: 'function';
|
||||
type: "function";
|
||||
function: {
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: {
|
||||
type: 'object';
|
||||
type: "object";
|
||||
properties: Record<string, any>;
|
||||
required: string[];
|
||||
};
|
||||
@@ -18,13 +18,22 @@ export interface GrokTool {
|
||||
|
||||
export interface GrokToolCall {
|
||||
id: string;
|
||||
type: 'function';
|
||||
type: "function";
|
||||
function: {
|
||||
name: string;
|
||||
arguments: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SearchParameters {
|
||||
mode?: "auto" | "on" | "off";
|
||||
// sources removed - let API use default sources to avoid format issues
|
||||
}
|
||||
|
||||
export interface SearchOptions {
|
||||
search_parameters?: SearchParameters;
|
||||
}
|
||||
|
||||
export interface GrokResponse {
|
||||
choices: Array<{
|
||||
message: {
|
||||
@@ -38,12 +47,12 @@ export interface GrokResponse {
|
||||
|
||||
export class GrokClient {
|
||||
private client: OpenAI;
|
||||
private currentModel: string = 'grok-3-latest';
|
||||
private currentModel: string = "grok-3-latest";
|
||||
|
||||
constructor(apiKey: string, model?: string, baseURL?: string) {
|
||||
this.client = new OpenAI({
|
||||
apiKey,
|
||||
baseURL: baseURL || process.env.GROK_BASE_URL || 'https://api.x.ai/v1',
|
||||
baseURL: baseURL || process.env.GROK_BASE_URL || "https://api.x.ai/v1",
|
||||
timeout: 360000,
|
||||
});
|
||||
if (model) {
|
||||
@@ -62,17 +71,27 @@ export class GrokClient {
|
||||
async chat(
|
||||
messages: GrokMessage[],
|
||||
tools?: GrokTool[],
|
||||
model?: string
|
||||
model?: string,
|
||||
searchOptions?: SearchOptions
|
||||
): Promise<GrokResponse> {
|
||||
try {
|
||||
const response = await this.client.chat.completions.create({
|
||||
const requestPayload: any = {
|
||||
model: model || this.currentModel,
|
||||
messages,
|
||||
tools: tools || [],
|
||||
tool_choice: tools ? 'auto' : undefined,
|
||||
tool_choice: tools && tools.length > 0 ? "auto" : undefined,
|
||||
temperature: 0.7,
|
||||
max_tokens: 4000,
|
||||
});
|
||||
};
|
||||
|
||||
// Add search parameters if specified
|
||||
if (searchOptions?.search_parameters) {
|
||||
requestPayload.search_parameters = searchOptions.search_parameters;
|
||||
}
|
||||
|
||||
const response = await this.client.chat.completions.create(
|
||||
requestPayload
|
||||
);
|
||||
|
||||
return response as GrokResponse;
|
||||
} catch (error: any) {
|
||||
@@ -83,18 +102,28 @@ export class GrokClient {
|
||||
async *chatStream(
|
||||
messages: GrokMessage[],
|
||||
tools?: GrokTool[],
|
||||
model?: string
|
||||
model?: string,
|
||||
searchOptions?: SearchOptions
|
||||
): AsyncGenerator<any, void, unknown> {
|
||||
try {
|
||||
const stream = await this.client.chat.completions.create({
|
||||
const requestPayload: any = {
|
||||
model: model || this.currentModel,
|
||||
messages,
|
||||
tools: tools || [],
|
||||
tool_choice: tools ? 'auto' : undefined,
|
||||
tool_choice: tools && tools.length > 0 ? "auto" : undefined,
|
||||
temperature: 0.7,
|
||||
max_tokens: 4000,
|
||||
stream: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Add search parameters if specified
|
||||
if (searchOptions?.search_parameters) {
|
||||
requestPayload.search_parameters = searchOptions.search_parameters;
|
||||
}
|
||||
|
||||
const stream = (await this.client.chat.completions.create(
|
||||
requestPayload
|
||||
)) as any;
|
||||
|
||||
for await (const chunk of stream) {
|
||||
yield chunk;
|
||||
@@ -103,4 +132,20 @@ export class GrokClient {
|
||||
throw new Error(`Grok API error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async search(
|
||||
query: string,
|
||||
searchParameters?: SearchParameters
|
||||
): Promise<GrokResponse> {
|
||||
const searchMessage: GrokMessage = {
|
||||
role: "user",
|
||||
content: query,
|
||||
};
|
||||
|
||||
const searchOptions: SearchOptions = {
|
||||
search_parameters: searchParameters || { mode: "on" },
|
||||
};
|
||||
|
||||
return this.chat([searchMessage], [], undefined, searchOptions);
|
||||
}
|
||||
}
|
||||
+102
-101
@@ -1,174 +1,175 @@
|
||||
import { GrokTool } from './client';
|
||||
import { GrokTool } from "./client";
|
||||
|
||||
export const GROK_TOOLS: GrokTool[] = [
|
||||
{
|
||||
type: 'function',
|
||||
type: "function",
|
||||
function: {
|
||||
name: 'view_file',
|
||||
description: 'View contents of a file or list directory contents',
|
||||
name: "view_file",
|
||||
description: "View contents of a file or list directory contents",
|
||||
parameters: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'Path to file or directory to view'
|
||||
type: "string",
|
||||
description: "Path to file or directory to view",
|
||||
},
|
||||
start_line: {
|
||||
type: 'number',
|
||||
description: 'Starting line number for partial file view (optional)'
|
||||
type: "number",
|
||||
description:
|
||||
"Starting line number for partial file view (optional)",
|
||||
},
|
||||
end_line: {
|
||||
type: 'number',
|
||||
description: 'Ending line number for partial file view (optional)'
|
||||
}
|
||||
type: "number",
|
||||
description: "Ending line number for partial file view (optional)",
|
||||
},
|
||||
},
|
||||
required: ['path']
|
||||
}
|
||||
}
|
||||
required: ["path"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
type: "function",
|
||||
function: {
|
||||
name: 'create_file',
|
||||
description: 'Create a new file with specified content',
|
||||
name: "create_file",
|
||||
description: "Create a new file with specified content",
|
||||
parameters: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'Path where the file should be created'
|
||||
type: "string",
|
||||
description: "Path where the file should be created",
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Content to write to the file'
|
||||
}
|
||||
type: "string",
|
||||
description: "Content to write to the file",
|
||||
},
|
||||
},
|
||||
required: ['path', 'content']
|
||||
}
|
||||
}
|
||||
required: ["path", "content"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
type: "function",
|
||||
function: {
|
||||
name: 'str_replace_editor',
|
||||
description: 'Replace specific text in a file',
|
||||
name: "str_replace_editor",
|
||||
description: "Replace specific text in a file",
|
||||
parameters: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: 'Path to the file to edit'
|
||||
type: "string",
|
||||
description: "Path to the file to edit",
|
||||
},
|
||||
old_str: {
|
||||
type: 'string',
|
||||
description: 'Text to replace (must match exactly)'
|
||||
type: "string",
|
||||
description: "Text to replace (must match exactly)",
|
||||
},
|
||||
new_str: {
|
||||
type: 'string',
|
||||
description: 'Text to replace with'
|
||||
}
|
||||
type: "string",
|
||||
description: "Text to replace with",
|
||||
},
|
||||
},
|
||||
required: ['path', 'old_str', 'new_str']
|
||||
}
|
||||
}
|
||||
required: ["path", "old_str", "new_str"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
type: "function",
|
||||
function: {
|
||||
name: 'bash',
|
||||
description: 'Execute a bash command',
|
||||
name: "bash",
|
||||
description: "Execute a bash command",
|
||||
parameters: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
command: {
|
||||
type: 'string',
|
||||
description: 'The bash command to execute'
|
||||
}
|
||||
type: "string",
|
||||
description: "The bash command to execute",
|
||||
},
|
||||
},
|
||||
required: ['command']
|
||||
}
|
||||
}
|
||||
required: ["command"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
type: "function",
|
||||
function: {
|
||||
name: 'create_todo_list',
|
||||
description: 'Create a new todo list for planning and tracking tasks',
|
||||
name: "create_todo_list",
|
||||
description: "Create a new todo list for planning and tracking tasks",
|
||||
parameters: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
todos: {
|
||||
type: 'array',
|
||||
description: 'Array of todo items',
|
||||
type: "array",
|
||||
description: "Array of todo items",
|
||||
items: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Unique identifier for the todo item'
|
||||
type: "string",
|
||||
description: "Unique identifier for the todo item",
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Description of the todo item'
|
||||
type: "string",
|
||||
description: "Description of the todo item",
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
enum: ['pending', 'in_progress', 'completed'],
|
||||
description: 'Current status of the todo item'
|
||||
type: "string",
|
||||
enum: ["pending", "in_progress", "completed"],
|
||||
description: "Current status of the todo item",
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
enum: ['high', 'medium', 'low'],
|
||||
description: 'Priority level of the todo item'
|
||||
}
|
||||
type: "string",
|
||||
enum: ["high", "medium", "low"],
|
||||
description: "Priority level of the todo item",
|
||||
},
|
||||
},
|
||||
required: ['id', 'content', 'status', 'priority']
|
||||
}
|
||||
}
|
||||
required: ["id", "content", "status", "priority"],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['todos']
|
||||
}
|
||||
}
|
||||
required: ["todos"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
type: "function",
|
||||
function: {
|
||||
name: 'update_todo_list',
|
||||
description: 'Update existing todos in the todo list',
|
||||
name: "update_todo_list",
|
||||
description: "Update existing todos in the todo list",
|
||||
parameters: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
updates: {
|
||||
type: 'array',
|
||||
description: 'Array of todo updates',
|
||||
type: "array",
|
||||
description: "Array of todo updates",
|
||||
items: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'ID of the todo item to update'
|
||||
type: "string",
|
||||
description: "ID of the todo item to update",
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
enum: ['pending', 'in_progress', 'completed'],
|
||||
description: 'New status for the todo item'
|
||||
type: "string",
|
||||
enum: ["pending", "in_progress", "completed"],
|
||||
description: "New status for the todo item",
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'New content for the todo item'
|
||||
type: "string",
|
||||
description: "New content for the todo item",
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
enum: ['high', 'medium', 'low'],
|
||||
description: 'New priority for the todo item'
|
||||
}
|
||||
type: "string",
|
||||
enum: ["high", "medium", "low"],
|
||||
description: "New priority for the todo item",
|
||||
},
|
||||
},
|
||||
required: ['id']
|
||||
}
|
||||
}
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['updates']
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
required: ["updates"],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -55,10 +55,13 @@ export function useInputHandler({
|
||||
];
|
||||
|
||||
const availableModels: ModelOption[] = [
|
||||
{ model: "grok-4-latest", description: "Latest Grok-4 model (most capable)" },
|
||||
{
|
||||
model: "grok-4-latest",
|
||||
description: "Latest Grok-4 model (most capable)",
|
||||
},
|
||||
{ model: "grok-3-latest", description: "Latest Grok-3 model" },
|
||||
{ model: "grok-3-fast", description: "Fast Grok-3 variant" },
|
||||
{ model: "grok-3-mini-fast", description: "Fastest Grok-3 variant" }
|
||||
{ model: "grok-3-mini-fast", description: "Fastest Grok-3 variant" },
|
||||
];
|
||||
|
||||
const handleDirectCommand = async (input: string): Promise<boolean> => {
|
||||
@@ -67,18 +70,18 @@ export function useInputHandler({
|
||||
if (trimmedInput === "/clear") {
|
||||
// Reset chat history
|
||||
setChatHistory([]);
|
||||
|
||||
|
||||
// Reset processing states
|
||||
setIsProcessing(false);
|
||||
setIsStreaming(false);
|
||||
setTokenCount(0);
|
||||
setProcessingTime(0);
|
||||
processingStartTime.current = 0;
|
||||
|
||||
|
||||
// Reset confirmation service session flags
|
||||
const confirmationService = ConfirmationService.getInstance();
|
||||
confirmationService.resetSession();
|
||||
|
||||
|
||||
setInput("");
|
||||
return true;
|
||||
}
|
||||
@@ -124,7 +127,7 @@ Examples:
|
||||
|
||||
if (trimmedInput.startsWith("/models ")) {
|
||||
const modelArg = trimmedInput.split(" ")[1];
|
||||
const modelNames = availableModels.map(m => m.model);
|
||||
const modelNames = availableModels.map((m) => m.model);
|
||||
|
||||
if (modelNames.includes(modelArg)) {
|
||||
agent.setModel(modelArg);
|
||||
@@ -150,7 +153,18 @@ Available models: ${modelNames.join(", ")}`,
|
||||
}
|
||||
|
||||
const directBashCommands = [
|
||||
"ls", "pwd", "cd", "cat", "mkdir", "touch", "echo", "grep", "find", "cp", "mv", "rm",
|
||||
"ls",
|
||||
"pwd",
|
||||
"cd",
|
||||
"cat",
|
||||
"mkdir",
|
||||
"touch",
|
||||
"echo",
|
||||
"grep",
|
||||
"find",
|
||||
"cp",
|
||||
"mv",
|
||||
"rm",
|
||||
];
|
||||
const firstWord = trimmedInput.split(" ")[0];
|
||||
|
||||
@@ -249,10 +263,27 @@ Available models: ${modelNames.join(", ")}`,
|
||||
// Stop streaming for the current assistant message
|
||||
setChatHistory((prev) =>
|
||||
prev.map((entry) =>
|
||||
entry.isStreaming ? { ...entry, isStreaming: false, toolCalls: chunk.toolCalls } : entry
|
||||
entry.isStreaming
|
||||
? {
|
||||
...entry,
|
||||
isStreaming: false,
|
||||
toolCalls: chunk.toolCalls,
|
||||
}
|
||||
: entry
|
||||
)
|
||||
);
|
||||
streamingEntry = null;
|
||||
|
||||
// Add individual tool call entries to show tools are being executed
|
||||
chunk.toolCalls.forEach((toolCall) => {
|
||||
const toolCallEntry: ChatEntry = {
|
||||
type: "tool_call",
|
||||
content: "Executing...",
|
||||
timestamp: new Date(),
|
||||
toolCall: toolCall,
|
||||
};
|
||||
setChatHistory((prev) => [...prev, toolCallEntry]);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -309,7 +340,7 @@ Available models: ${modelNames.join(", ")}`,
|
||||
if (isConfirmationActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (key.ctrl && inputChar === "c") {
|
||||
exit();
|
||||
return;
|
||||
@@ -345,7 +376,9 @@ Available models: ${modelNames.join(", ")}`,
|
||||
return;
|
||||
}
|
||||
if (key.downArrow) {
|
||||
setSelectedCommandIndex((prev) => (prev + 1) % commandSuggestions.length);
|
||||
setSelectedCommandIndex(
|
||||
(prev) => (prev + 1) % commandSuggestions.length
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (key.tab || key.return) {
|
||||
@@ -444,4 +477,4 @@ Available models: ${modelNames.join(", ")}`,
|
||||
availableModels,
|
||||
agent,
|
||||
};
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
export { TextEditorTool } from './text-editor';
|
||||
export { BashTool } from './bash';
|
||||
export { TodoTool } from './todo-tool';
|
||||
export { ConfirmationTool } from './confirmation-tool';
|
||||
export { TextEditorTool } from "./text-editor";
|
||||
export { BashTool } from "./bash";
|
||||
export { TodoTool } from "./todo-tool";
|
||||
export { ConfirmationTool } from "./confirmation-tool";
|
||||
@@ -76,6 +76,7 @@ export function ChatHistory({ entries }: ChatHistoryProps) {
|
||||
</Box>
|
||||
);
|
||||
|
||||
case "tool_call":
|
||||
case "tool_result":
|
||||
const getToolActionName = (toolName: string) => {
|
||||
switch (toolName) {
|
||||
@@ -87,6 +88,7 @@ export function ChatHistory({ entries }: ChatHistoryProps) {
|
||||
return "Create";
|
||||
case "bash":
|
||||
return "Bash";
|
||||
|
||||
case "create_todo_list":
|
||||
return "Created Todo";
|
||||
case "update_todo_list":
|
||||
@@ -107,6 +109,7 @@ export function ChatHistory({ entries }: ChatHistoryProps) {
|
||||
) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return args.path || args.file_path || args.command || "unknown";
|
||||
} catch {
|
||||
return "unknown";
|
||||
@@ -119,10 +122,10 @@ export function ChatHistory({ entries }: ChatHistoryProps) {
|
||||
const actionName = getToolActionName(toolName);
|
||||
const filePath = getToolFilePath(entry.toolCall);
|
||||
|
||||
|
||||
const shouldShowDiff =
|
||||
toolName === "str_replace_editor" || toolName === "create_file";
|
||||
const shouldShowFileContent = toolName === "view_file";
|
||||
const isExecuting = entry.type === "tool_call";
|
||||
|
||||
return (
|
||||
<Box key={index} flexDirection="column" marginTop={1}>
|
||||
@@ -134,7 +137,9 @@ export function ChatHistory({ entries }: ChatHistoryProps) {
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginLeft={2} flexDirection="column">
|
||||
{shouldShowFileContent ? (
|
||||
{isExecuting ? (
|
||||
<Text color="cyan">⎿ Executing...</Text>
|
||||
) : shouldShowFileContent ? (
|
||||
<Box flexDirection="column">
|
||||
<Text color="gray">⎿ File contents:</Text>
|
||||
<Box marginLeft={2} flexDirection="column">
|
||||
@@ -143,12 +148,12 @@ export function ChatHistory({ entries }: ChatHistoryProps) {
|
||||
</Box>
|
||||
) : shouldShowDiff ? (
|
||||
// For diff results, show only the summary line, not the raw content
|
||||
<Text color="gray">⎿ {entry.content.split('\n')[0]}</Text>
|
||||
<Text color="gray">⎿ {entry.content.split("\n")[0]}</Text>
|
||||
) : (
|
||||
<Text color="gray">⎿ {entry.content}</Text>
|
||||
)}
|
||||
</Box>
|
||||
{shouldShowDiff && (
|
||||
{shouldShowDiff && !isExecuting && (
|
||||
<Box marginLeft={4} flexDirection="column">
|
||||
{renderDiff(entry.content, filePath)}
|
||||
</Box>
|
||||
|
||||
Reference in new issue
Block a user