Run `npm run format`
- This has the entirety of the changes. Part of https://b.corp.google.com/issues/411720532
This commit is contained in:
parent
fa264e4286
commit
383b917784
|
@ -4,9 +4,9 @@ name: Gemini Code CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ] # Run on pushes to the main branch
|
branches: [main] # Run on pushes to the main branch
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ] # Run on pull requests targeting the main branch
|
branches: [main] # Run on pull requests targeting the main branch
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
build_and_test:
|
||||||
|
@ -16,7 +16,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# Specify the Node.js versions you want to test against
|
# Specify the Node.js versions you want to test against
|
||||||
node-version: [ 20.x ] # You can add more like [18.x, 20.x]
|
node-version: [20.x] # You can add more like [18.x, 20.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# 1. Checkout Code
|
# 1. Checkout Code
|
||||||
|
|
|
@ -35,7 +35,7 @@ export function loadConfig(): Config {
|
||||||
loadEnvironment();
|
loadEnvironment();
|
||||||
const argv = parseArguments();
|
const argv = parseArguments();
|
||||||
return new Config(
|
return new Config(
|
||||||
process.env.GEMINI_API_KEY || "",
|
process.env.GEMINI_API_KEY || '',
|
||||||
argv.model || process.env.GEMINI_API_KEY || DEFAULT_GEMINI_MODEL,
|
argv.model || process.env.GEMINI_API_KEY || DEFAULT_GEMINI_MODEL,
|
||||||
argv.target_dir || process.cwd(),
|
argv.target_dir || process.cwd(),
|
||||||
);
|
);
|
||||||
|
@ -66,15 +66,13 @@ function parseArguments(): CliArgs {
|
||||||
})
|
})
|
||||||
.help()
|
.help()
|
||||||
.alias('h', 'help')
|
.alias('h', 'help')
|
||||||
.strict() // Keep strict mode to error on unknown options
|
.strict().argv; // Keep strict mode to error on unknown options
|
||||||
.argv;
|
|
||||||
|
|
||||||
// Cast to the interface to ensure the structure aligns with expectations
|
// Cast to the interface to ensure the structure aligns with expectations
|
||||||
// Use `unknown` first for safer casting if types might not perfectly match
|
// Use `unknown` first for safer casting if types might not perfectly match
|
||||||
return argv as unknown as CliArgs;
|
return argv as unknown as CliArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function findEnvFile(startDir: string): string | null {
|
function findEnvFile(startDir: string): string | null {
|
||||||
// Start search from the provided directory (e.g., current working directory)
|
// Start search from the provided directory (e.g., current working directory)
|
||||||
let currentDir = path.resolve(startDir); // Ensure absolute path
|
let currentDir = path.resolve(startDir); // Ensure absolute path
|
||||||
|
|
|
@ -21,7 +21,6 @@ import { getFolderStructure } from '../utils/getFolderStructure.js';
|
||||||
import { GeminiEventType, GeminiStream } from './gemini-stream.js';
|
import { GeminiEventType, GeminiStream } from './gemini-stream.js';
|
||||||
import { Config } from '../config/config.js';
|
import { Config } from '../config/config.js';
|
||||||
|
|
||||||
|
|
||||||
type ToolExecutionOutcome = {
|
type ToolExecutionOutcome = {
|
||||||
callId: string;
|
callId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ToolCallEvent , HistoryItem } from '../ui/types.js';
|
import { ToolCallEvent, HistoryItem } from '../ui/types.js';
|
||||||
import { Part } from '@google/genai';
|
import { Part } from '@google/genai';
|
||||||
import {
|
import {
|
||||||
handleToolCallChunk,
|
handleToolCallChunk,
|
||||||
|
@ -161,7 +161,11 @@ export const processGeminiStream = async ({
|
||||||
renderTimeoutId = null;
|
renderTimeoutId = null;
|
||||||
}
|
}
|
||||||
// Delegate history update for error message
|
// Delegate history update for error message
|
||||||
addErrorMessageToHistory(error as (Error | DOMException), setHistory, getNextMessageId);
|
addErrorMessageToHistory(
|
||||||
|
error as Error | DOMException,
|
||||||
|
setHistory,
|
||||||
|
getNextMessageId,
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
isStreamComplete = true; // Signal stream end for render loop completion
|
isStreamComplete = true; // Signal stream end for render loop completion
|
||||||
if (renderTimeoutId) {
|
if (renderTimeoutId) {
|
||||||
|
|
|
@ -116,8 +116,12 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> {
|
||||||
* @returns An error message string if invalid, null otherwise
|
* @returns An error message string if invalid, null otherwise
|
||||||
*/
|
*/
|
||||||
validateToolParams(params: LSToolParams): string | null {
|
validateToolParams(params: LSToolParams): string | null {
|
||||||
if (this.schema.parameters &&
|
if (
|
||||||
!SchemaValidator.validate(this.schema.parameters as Record<string, unknown>, params)
|
this.schema.parameters &&
|
||||||
|
!SchemaValidator.validate(
|
||||||
|
this.schema.parameters as Record<string, unknown>,
|
||||||
|
params,
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return 'Parameters failed schema validation.';
|
return 'Parameters failed schema validation.';
|
||||||
}
|
}
|
||||||
|
@ -181,7 +185,8 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> {
|
||||||
if (validationError) {
|
if (validationError) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
`Error: Invalid parameters provided. Reason: ${validationError}`,
|
`Error: Invalid parameters provided. Reason: ${validationError}`,
|
||||||
`Failed to execute tool.`);
|
`Failed to execute tool.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -189,12 +194,14 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> {
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
`Directory does not exist: ${params.path}`,
|
`Directory does not exist: ${params.path}`,
|
||||||
`Directory does not exist.`);
|
`Directory does not exist.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!stats.isDirectory()) {
|
if (!stats.isDirectory()) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
`Path is not a directory: ${params.path}`,
|
`Path is not a directory: ${params.path}`,
|
||||||
`Path is not a directory.`);
|
`Path is not a directory.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = fs.readdirSync(params.path);
|
const files = fs.readdirSync(params.path);
|
||||||
|
@ -202,7 +209,8 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> {
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
`Directory is empty: ${params.path}`,
|
`Directory is empty: ${params.path}`,
|
||||||
`Directory is empty.`);
|
`Directory is empty.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
|
@ -249,7 +257,8 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
`Error listing directory: ${error instanceof Error ? error.message : String(error)}`,
|
`Error listing directory: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
'Failed to list directory.');
|
'Failed to list directory.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,7 @@ export interface ReadFileToolParams {
|
||||||
/**
|
/**
|
||||||
* Implementation of the ReadFile tool that reads files from the filesystem
|
* Implementation of the ReadFile tool that reads files from the filesystem
|
||||||
*/
|
*/
|
||||||
export class ReadFileTool extends BaseTool<
|
export class ReadFileTool extends BaseTool<ReadFileToolParams, ToolResult> {
|
||||||
ReadFileToolParams,
|
|
||||||
ToolResult
|
|
||||||
> {
|
|
||||||
static readonly Name: string = 'read_file';
|
static readonly Name: string = 'read_file';
|
||||||
|
|
||||||
// Maximum number of lines to read by default
|
// Maximum number of lines to read by default
|
||||||
|
|
|
@ -115,10 +115,7 @@ interface QueuedCommand {
|
||||||
/**
|
/**
|
||||||
* Implementation of the terminal tool that executes shell commands within a persistent session.
|
* Implementation of the terminal tool that executes shell commands within a persistent session.
|
||||||
*/
|
*/
|
||||||
export class TerminalTool extends BaseTool<
|
export class TerminalTool extends BaseTool<TerminalToolParams, ToolResult> {
|
||||||
TerminalToolParams,
|
|
||||||
ToolResult
|
|
||||||
> {
|
|
||||||
static Name: string = 'execute_bash_command';
|
static Name: string = 'execute_bash_command';
|
||||||
|
|
||||||
private readonly rootDirectory: string;
|
private readonly rootDirectory: string;
|
||||||
|
@ -134,10 +131,7 @@ export class TerminalTool extends BaseTool<
|
||||||
private rejectShellReady: ((reason?: unknown) => void) | undefined; // Definite assignment assertion
|
private rejectShellReady: ((reason?: unknown) => void) | undefined; // Definite assignment assertion
|
||||||
private readonly backgroundTerminalAnalyzer: BackgroundTerminalAnalyzer;
|
private readonly backgroundTerminalAnalyzer: BackgroundTerminalAnalyzer;
|
||||||
|
|
||||||
constructor(
|
constructor(rootDirectory: string, outputLimit: number = MAX_OUTPUT_LENGTH) {
|
||||||
rootDirectory: string,
|
|
||||||
outputLimit: number = MAX_OUTPUT_LENGTH,
|
|
||||||
) {
|
|
||||||
const toolDisplayName = 'Terminal';
|
const toolDisplayName = 'Terminal';
|
||||||
// --- LLM-Facing Description ---
|
// --- LLM-Facing Description ---
|
||||||
// Updated description for background tasks to mention polling and LLM analysis
|
// Updated description for background tasks to mention polling and LLM analysis
|
||||||
|
@ -454,9 +448,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
// Define temp file paths here to be accessible throughout
|
// Define temp file paths here to be accessible throughout
|
||||||
let tempStdoutPath: string | null = null;
|
let tempStdoutPath: string | null = null;
|
||||||
let tempStderrPath: string | null = null;
|
let tempStderrPath: string | null = null;
|
||||||
let originalResolve: (
|
let originalResolve: (value: ToolResult | PromiseLike<ToolResult>) => void; // To pass to polling
|
||||||
value: ToolResult | PromiseLike<ToolResult>,
|
|
||||||
) => void; // To pass to polling
|
|
||||||
let originalReject: (reason?: unknown) => void;
|
let originalReject: (reason?: unknown) => void;
|
||||||
|
|
||||||
const promise = new Promise<ToolResult>((resolve, reject) => {
|
const promise = new Promise<ToolResult>((resolve, reject) => {
|
||||||
|
@ -939,9 +931,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
initialStderr: string, // Stderr during launch phase
|
initialStderr: string, // Stderr during launch phase
|
||||||
tempStdoutPath: string, // Path to redirected stdout
|
tempStdoutPath: string, // Path to redirected stdout
|
||||||
tempStderrPath: string, // Path to redirected stderr
|
tempStderrPath: string, // Path to redirected stderr
|
||||||
resolve: (
|
resolve: (value: ToolResult | PromiseLike<ToolResult>) => void, // The original promise's resolve
|
||||||
value: ToolResult | PromiseLike<ToolResult>,
|
|
||||||
) => void, // The original promise's resolve
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// This function manages its own lifecycle but resolves the outer promise
|
// This function manages its own lifecycle but resolves the outer promise
|
||||||
let finalStdout = '';
|
let finalStdout = '';
|
||||||
|
|
|
@ -90,9 +90,7 @@ export class WebFetchTool extends BaseTool<
|
||||||
getDescription(params: WebFetchToolParams): string {
|
getDescription(params: WebFetchToolParams): string {
|
||||||
// Shorten long URLs for display
|
// Shorten long URLs for display
|
||||||
const displayUrl =
|
const displayUrl =
|
||||||
params.url.length > 80
|
params.url.length > 80 ? params.url.substring(0, 77) + '...' : params.url;
|
||||||
? params.url.substring(0, 77) + '...'
|
|
||||||
: params.url;
|
|
||||||
return `Fetching content from ${displayUrl}`;
|
return `Fetching content from ${displayUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +128,7 @@ export class WebFetchTool extends BaseTool<
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'GeminiCode-CLI/1.0',
|
'User-Agent': 'GeminiCode-CLI/1.0',
|
||||||
},
|
},
|
||||||
signal: AbortSignal.timeout(15000) // 15 seconds timeout
|
signal: AbortSignal.timeout(15000), // 15 seconds timeout
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|
|
@ -28,10 +28,7 @@ export interface WriteFileToolParams {
|
||||||
/**
|
/**
|
||||||
* Implementation of the WriteFile tool that writes files to the filesystem
|
* Implementation of the WriteFile tool that writes files to the filesystem
|
||||||
*/
|
*/
|
||||||
export class WriteFileTool extends BaseTool<
|
export class WriteFileTool extends BaseTool<WriteFileToolParams, ToolResult> {
|
||||||
WriteFileToolParams,
|
|
||||||
ToolResult
|
|
||||||
> {
|
|
||||||
static readonly Name: string = 'write_file';
|
static readonly Name: string = 'write_file';
|
||||||
private shouldAlwaysWrite = false;
|
private shouldAlwaysWrite = false;
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ interface FooterProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Footer: React.FC<FooterProps> = ({ queryLength }) => (
|
const Footer: React.FC<FooterProps> = ({ queryLength }) => (
|
||||||
<Box marginTop={1} justifyContent="space-between">
|
<Box marginTop={1} justifyContent="space-between">
|
||||||
<Box minWidth={15}>
|
<Box minWidth={15}>
|
||||||
<Text color="gray">{queryLength === 0 ? '? for shortcuts' : ''}</Text>
|
<Text color="gray">{queryLength === 0 ? '? for shortcuts' : ''}</Text>
|
||||||
</Box>
|
|
||||||
<Text color="blue">Gemini</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
<Text color="blue">Gemini</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
|
|
@ -8,31 +8,31 @@ interface HeaderProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header: React.FC<HeaderProps> = ({ cwd }) => (
|
const Header: React.FC<HeaderProps> = ({ cwd }) => (
|
||||||
<>
|
<>
|
||||||
{/* Static Header Art */}
|
{/* Static Header Art */}
|
||||||
<Box marginBottom={1}>
|
<Box marginBottom={1}>
|
||||||
<Text color="blue">{`
|
<Text color="blue">{`
|
||||||
______ ________ ____ ____ _____ ____ _____ _____
|
______ ________ ____ ____ _____ ____ _____ _____
|
||||||
.' ___ ||_ __ ||_ \\ / _||_ _||_ \\|_ _||_ _|
|
.' ___ ||_ __ ||_ \\ / _||_ _||_ \\|_ _||_ _|
|
||||||
/ .' \\_| | |_ \\_| | \\/ | | | | \\ | | | |
|
/ .' \\_| | |_ \\_| | \\/ | | | | \\ | | | |
|
||||||
| | ____ | _| _ | |\\ /| | | | | |\\ \\| | | |
|
| | ____ | _| _ | |\\ /| | | | | |\\ \\| | | |
|
||||||
\\ \`.___] |_| |__/ | _| |_\\/_| |_ _| |_ _| |_\\ |_ _| |_
|
\\ \`.___] |_| |__/ | _| |_\\/_| |_ _| |_ _| |_\\ |_ _| |_
|
||||||
\`._____.'|________||_____||_____||_____||_____|\\____||_____|`}</Text>
|
\`._____.'|________||_____||_____||_____||_____|\\____||_____|`}</Text>
|
||||||
|
</Box>
|
||||||
|
{/* CWD Display */}
|
||||||
|
<Box
|
||||||
|
borderStyle="round"
|
||||||
|
borderColor="blue"
|
||||||
|
paddingX={BOX_PADDING_X}
|
||||||
|
flexDirection="column"
|
||||||
|
marginBottom={1}
|
||||||
|
width={UI_WIDTH}
|
||||||
|
>
|
||||||
|
<Box paddingLeft={2}>
|
||||||
|
<Text color="gray">cwd: {shortenPath(cwd, /*maxLength*/ 70)}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{/* CWD Display */}
|
</Box>
|
||||||
<Box
|
</>
|
||||||
borderStyle="round"
|
);
|
||||||
borderColor="blue"
|
|
||||||
paddingX={BOX_PADDING_X}
|
|
||||||
flexDirection="column"
|
|
||||||
marginBottom={1}
|
|
||||||
width={UI_WIDTH}
|
|
||||||
>
|
|
||||||
<Box paddingLeft={2}>
|
|
||||||
<Text color="gray">cwd: {shortenPath(cwd, /*maxLength*/ 70)}</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Header;
|
export default Header;
|
||||||
|
|
|
@ -17,26 +17,23 @@ interface HistoryDisplayProps {
|
||||||
const HistoryDisplay: React.FC<HistoryDisplayProps> = ({
|
const HistoryDisplay: React.FC<HistoryDisplayProps> = ({
|
||||||
history,
|
history,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}) =>
|
}) => (
|
||||||
// No grouping logic needed here anymore
|
// No grouping logic needed here anymore
|
||||||
(
|
<Box flexDirection="column">
|
||||||
<Box flexDirection="column">
|
{history.map((item) => (
|
||||||
{history.map((item) => (
|
<Box key={item.id} marginBottom={1}>
|
||||||
<Box key={item.id} marginBottom={1}>
|
{/* Render standard message types */}
|
||||||
{/* Render standard message types */}
|
{item.type === 'user' && <UserMessage text={item.text} />}
|
||||||
{item.type === 'user' && <UserMessage text={item.text} />}
|
{item.type === 'gemini' && <GeminiMessage text={item.text} />}
|
||||||
{item.type === 'gemini' && <GeminiMessage text={item.text} />}
|
{item.type === 'info' && <InfoMessage text={item.text} />}
|
||||||
{item.type === 'info' && <InfoMessage text={item.text} />}
|
{item.type === 'error' && <ErrorMessage text={item.text} />}
|
||||||
{item.type === 'error' && <ErrorMessage text={item.text} />}
|
|
||||||
|
|
||||||
{/* Render the tool group component */}
|
|
||||||
{item.type === 'tool_group' && (
|
|
||||||
<ToolGroupMessage toolCalls={item.tools} onSubmit={onSubmit} />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
;
|
|
||||||
|
|
||||||
|
{/* Render the tool group component */}
|
||||||
|
{item.type === 'tool_group' && (
|
||||||
|
<ToolGroupMessage toolCalls={item.tools} onSubmit={onSubmit} />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
export default HistoryDisplay;
|
export default HistoryDisplay;
|
||||||
|
|
|
@ -3,8 +3,6 @@ import { Box, Text } from 'ink';
|
||||||
import TextInput from 'ink-text-input';
|
import TextInput from 'ink-text-input';
|
||||||
import { globalConfig } from '../../config/config.js';
|
import { globalConfig } from '../../config/config.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface InputPromptProps {
|
interface InputPromptProps {
|
||||||
query: string;
|
query: string;
|
||||||
setQuery: (value: string) => void;
|
setQuery: (value: string) => void;
|
||||||
|
@ -34,6 +32,6 @@ const InputPrompt: React.FC<InputPromptProps> = ({
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default InputPrompt;
|
export default InputPrompt;
|
||||||
|
|
|
@ -3,18 +3,18 @@ import { Box, Text } from 'ink';
|
||||||
import { UI_WIDTH } from '../constants.js';
|
import { UI_WIDTH } from '../constants.js';
|
||||||
|
|
||||||
const Tips: React.FC = () => (
|
const Tips: React.FC = () => (
|
||||||
<Box flexDirection="column" marginBottom={1} width={UI_WIDTH}>
|
<Box flexDirection="column" marginBottom={1} width={UI_WIDTH}>
|
||||||
<Text>Tips for getting started:</Text>
|
<Text>Tips for getting started:</Text>
|
||||||
<Text>
|
<Text>
|
||||||
1. <Text bold>/help</Text> for more information.
|
1. <Text bold>/help</Text> for more information.
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Text>
|
||||||
2. <Text bold>/init</Text> to create a GEMINI.md for instructions &
|
2. <Text bold>/init</Text> to create a GEMINI.md for instructions &
|
||||||
context.
|
context.
|
||||||
</Text>
|
</Text>
|
||||||
<Text>3. Ask coding questions, edit code or run commands.</Text>
|
<Text>3. Ask coding questions, edit code or run commands.</Text>
|
||||||
<Text>4. Be specific for the best results.</Text>
|
<Text>4. Be specific for the best results.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Tips;
|
export default Tips;
|
||||||
|
|
|
@ -21,23 +21,23 @@ const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" borderStyle="round" borderColor={borderColor}>
|
<Box flexDirection="column" borderStyle="round" borderColor={borderColor}>
|
||||||
{toolCalls.map((tool) => (
|
{toolCalls.map((tool) => (
|
||||||
<React.Fragment key={tool.callId}>
|
<React.Fragment key={tool.callId}>
|
||||||
<ToolMessage
|
<ToolMessage
|
||||||
key={tool.callId} // Use callId as the key
|
key={tool.callId} // Use callId as the key
|
||||||
name={tool.name}
|
name={tool.name}
|
||||||
description={tool.description}
|
description={tool.description}
|
||||||
resultDisplay={tool.resultDisplay}
|
resultDisplay={tool.resultDisplay}
|
||||||
status={tool.status}
|
status={tool.status}
|
||||||
/>
|
/>
|
||||||
{tool.status === ToolCallStatus.Confirming &&
|
{tool.status === ToolCallStatus.Confirming &&
|
||||||
tool.confirmationDetails && (
|
tool.confirmationDetails && (
|
||||||
<ToolConfirmationMessage
|
<ToolConfirmationMessage
|
||||||
confirmationDetails={tool.confirmationDetails}
|
confirmationDetails={tool.confirmationDetails}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
></ToolConfirmationMessage>
|
></ToolConfirmationMessage>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
{/* Optional: Add padding below the last item if needed,
|
{/* Optional: Add padding below the last item if needed,
|
||||||
though ToolMessage already has some vertical space implicitly */}
|
though ToolMessage already has some vertical space implicitly */}
|
||||||
{/* {tools.length > 0 && <Box height={1} />} */}
|
{/* {tools.length > 0 && <Box height={1} />} */}
|
||||||
|
|
|
@ -3,7 +3,10 @@ import { useInput } from 'ink';
|
||||||
import { GeminiClient } from '../../core/gemini-client.js';
|
import { GeminiClient } from '../../core/gemini-client.js';
|
||||||
import { type Chat, type PartListUnion } from '@google/genai';
|
import { type Chat, type PartListUnion } from '@google/genai';
|
||||||
import { HistoryItem } from '../types.js';
|
import { HistoryItem } from '../types.js';
|
||||||
import { processGeminiStream , StreamingState } from '../../core/gemini-stream.js';
|
import {
|
||||||
|
processGeminiStream,
|
||||||
|
StreamingState,
|
||||||
|
} from '../../core/gemini-stream.js';
|
||||||
import { globalConfig } from '../../config/config.js';
|
import { globalConfig } from '../../config/config.js';
|
||||||
import { getErrorMessage, isNodeError } from '../../utils/errors.js';
|
import { getErrorMessage, isNodeError } from '../../utils/errors.js';
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ export interface AiClient {
|
||||||
generateJson(
|
generateJson(
|
||||||
prompt: Content[], // Keep flexible or define a stricter prompt structure type
|
prompt: Content[], // Keep flexible or define a stricter prompt structure type
|
||||||
schema: SchemaUnion,
|
schema: SchemaUnion,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
): Promise<any>; // Ideally, specify the expected JSON structure TAnalysisResult | TAnalysisFailure
|
): Promise<any>; // Ideally, specify the expected JSON structure TAnalysisResult | TAnalysisFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,10 +127,14 @@ export class BackgroundTerminalAnalyzer {
|
||||||
// Reread files one last time in case output was written just before exit
|
// Reread files one last time in case output was written just before exit
|
||||||
try {
|
try {
|
||||||
currentStdout = await fs.readFile(tempStdoutFilePath, 'utf-8');
|
currentStdout = await fs.readFile(tempStdoutFilePath, 'utf-8');
|
||||||
} catch { /* ignore */ }
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
currentStderr = await fs.readFile(tempStderrFilePath, 'utf-8');
|
currentStderr = await fs.readFile(tempStderrFilePath, 'utf-8');
|
||||||
} catch { /* ignore */ }
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
|
||||||
lastAnalysisResult = await this.analyzeOutputWithLLM(
|
lastAnalysisResult = await this.analyzeOutputWithLLM(
|
||||||
currentStdout,
|
currentStdout,
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
export function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
export function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
||||||
return error instanceof Error && 'code' in error;
|
return error instanceof Error && 'code' in error;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getErrorMessage(error: unknown): string {
|
export function getErrorMessage(error: unknown): string {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
return error.message;
|
return error.message;
|
||||||
} else {
|
} else {
|
||||||
// Attempt to convert the non-Error value to a string for logging
|
// Attempt to convert the non-Error value to a string for logging
|
||||||
try {
|
try {
|
||||||
const errorMessage = String(error);
|
const errorMessage = String(error);
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
} catch {
|
} catch {
|
||||||
// If String() itself fails (highly unlikely)
|
// If String() itself fails (highly unlikely)
|
||||||
return 'Failed to get error details';
|
return 'Failed to get error details';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -137,7 +137,10 @@ async function readFullStructure(
|
||||||
folderInfo.subFolders.length +
|
folderInfo.subFolders.length +
|
||||||
folderInfo.subFolders.reduce((sum, sf) => sum + sf.totalChildren, 0);
|
folderInfo.subFolders.reduce((sum, sf) => sum + sf.totalChildren, 0);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (isNodeError(error) && (error.code === 'EACCES' || error.code === 'ENOENT')) {
|
if (
|
||||||
|
isNodeError(error) &&
|
||||||
|
(error.code === 'EACCES' || error.code === 'ENOENT')
|
||||||
|
) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Warning: Could not read directory ${folderPath}: ${error.message}`,
|
`Warning: Could not read directory ${folderPath}: ${error.message}`,
|
||||||
);
|
);
|
||||||
|
@ -345,10 +348,7 @@ export async function getFolderStructure(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Reduce the structure (handles ignored folders specifically)
|
// 2. Reduce the structure (handles ignored folders specifically)
|
||||||
const reducedRoot = reduceStructure(
|
const reducedRoot = reduceStructure(fullInfo, mergedOptions.maxItems);
|
||||||
fullInfo,
|
|
||||||
mergedOptions.maxItems,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. Count items in the *reduced* structure for the summary
|
// 3. Count items in the *reduced* structure for the summary
|
||||||
const rootNodeItselfCount = 0; // Don't count the root node in the items summary
|
const rootNodeItselfCount = 0; // Don't count the root node in the items summary
|
||||||
|
|
|
@ -13,7 +13,5 @@
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"],
|
"exclude": ["node_modules", "dist"],
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"references": [
|
"references": [{ "path": "../server" }]
|
||||||
{ "path": "../server" },
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
{
|
{
|
||||||
"name": "@gemini-code/server",
|
"name": "@gemini-code/server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Gemini Code Server",
|
"description": "Gemini Code Server",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && cp package.json dist/",
|
"build": "tsc && cp package.json dist/",
|
||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"lint": "eslint . --ext .ts,.tsx",
|
"lint": "eslint . --ext .ts,.tsx",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export function helloServer() {
|
export function helloServer() {
|
||||||
// TODO: add more things in this package
|
// TODO: add more things in this package
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
||||||
"module": "Node16",
|
"module": "Node16",
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"composite": true,
|
"composite": true
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist"],
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@ import os from 'os'; // Import os module
|
||||||
const cliPackageDir = path.resolve('packages', 'cli'); // Base directory for the CLI package
|
const cliPackageDir = path.resolve('packages', 'cli'); // Base directory for the CLI package
|
||||||
const buildTimestampPath = path.join(cliPackageDir, 'dist', '.last_build'); // Path to the timestamp file within the CLI package
|
const buildTimestampPath = path.join(cliPackageDir, 'dist', '.last_build'); // Path to the timestamp file within the CLI package
|
||||||
const sourceDirs = [path.join(cliPackageDir, 'src')]; // Source directory within the CLI package
|
const sourceDirs = [path.join(cliPackageDir, 'src')]; // Source directory within the CLI package
|
||||||
const filesToWatch = [path.join(cliPackageDir, 'package.json'), path.join(cliPackageDir, 'tsconfig.json')]; // Specific files within the CLI package
|
const filesToWatch = [
|
||||||
|
path.join(cliPackageDir, 'package.json'),
|
||||||
|
path.join(cliPackageDir, 'tsconfig.json'),
|
||||||
|
]; // Specific files within the CLI package
|
||||||
const buildDir = path.join(cliPackageDir, 'dist'); // Build output directory within the CLI package
|
const buildDir = path.join(cliPackageDir, 'dist'); // Build output directory within the CLI package
|
||||||
const warningsFilePath = path.join(os.tmpdir(), 'gemini-code-cli-warnings.txt'); // Temp file for warnings
|
const warningsFilePath = path.join(os.tmpdir(), 'gemini-code-cli-warnings.txt'); // Temp file for warnings
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
@ -28,7 +31,11 @@ function findSourceFiles(dir, allFiles = []) {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const fullPath = path.join(dir, entry.name);
|
const fullPath = path.join(dir, entry.name);
|
||||||
// Simple check to avoid recursing into node_modules or build dir itself
|
// Simple check to avoid recursing into node_modules or build dir itself
|
||||||
if (entry.isDirectory() && entry.name !== 'node_modules' && fullPath !== buildDir) {
|
if (
|
||||||
|
entry.isDirectory() &&
|
||||||
|
entry.name !== 'node_modules' &&
|
||||||
|
fullPath !== buildDir
|
||||||
|
) {
|
||||||
findSourceFiles(fullPath, allFiles);
|
findSourceFiles(fullPath, allFiles);
|
||||||
} else if (entry.isFile()) {
|
} else if (entry.isFile()) {
|
||||||
allFiles.push(fullPath);
|
allFiles.push(fullPath);
|
||||||
|
@ -45,7 +52,9 @@ try {
|
||||||
fs.unlinkSync(warningsFilePath);
|
fs.unlinkSync(warningsFilePath);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`[Check Script] Warning: Could not delete previous warnings file: ${err.message}`);
|
console.warn(
|
||||||
|
`[Check Script] Warning: Could not delete previous warnings file: ${err.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildMtime = getMtime(buildTimestampPath);
|
const buildMtime = getMtime(buildTimestampPath);
|
||||||
|
@ -56,7 +65,9 @@ if (!buildMtime) {
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(warningsFilePath, errorMessage);
|
fs.writeFileSync(warningsFilePath, errorMessage);
|
||||||
} catch (writeErr) {
|
} catch (writeErr) {
|
||||||
console.error(`[Check Script] Error writing missing build warning file: ${writeErr.message}`);
|
console.error(
|
||||||
|
`[Check Script] Error writing missing build warning file: ${writeErr.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
process.exit(0); // Allow app to start and show the error
|
process.exit(0); // Allow app to start and show the error
|
||||||
}
|
}
|
||||||
|
@ -67,25 +78,24 @@ const allSourceFiles = [];
|
||||||
|
|
||||||
// Collect files from specified directories
|
// Collect files from specified directories
|
||||||
sourceDirs.forEach((dir) => {
|
sourceDirs.forEach((dir) => {
|
||||||
const dirPath = path.resolve(dir);
|
const dirPath = path.resolve(dir);
|
||||||
if (fs.existsSync(dirPath)) {
|
if (fs.existsSync(dirPath)) {
|
||||||
findSourceFiles(dirPath, allSourceFiles);
|
findSourceFiles(dirPath, allSourceFiles);
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Warning: Source directory "${dir}" not found.`);
|
console.warn(`Warning: Source directory "${dir}" not found.`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add specific files
|
// Add specific files
|
||||||
filesToWatch.forEach((file) => {
|
filesToWatch.forEach((file) => {
|
||||||
const filePath = path.resolve(file);
|
const filePath = path.resolve(file);
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
allSourceFiles.push(filePath);
|
allSourceFiles.push(filePath);
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Warning: Watched file "${file}" not found.`);
|
console.warn(`Warning: Watched file "${file}" not found.`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Check modification times
|
// Check modification times
|
||||||
for (const file of allSourceFiles) {
|
for (const file of allSourceFiles) {
|
||||||
const sourceMtime = getMtime(file);
|
const sourceMtime = getMtime(file);
|
||||||
|
@ -102,7 +112,8 @@ for (const file of allSourceFiles) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newerSourceFileFound) {
|
if (newerSourceFileFound) {
|
||||||
const finalWarning = '\nRun "npm run build" to incorporate changes before starting.';
|
const finalWarning =
|
||||||
|
'\nRun "npm run build" to incorporate changes before starting.';
|
||||||
warningMessages.push(finalWarning);
|
warningMessages.push(finalWarning);
|
||||||
console.warn(finalWarning);
|
console.warn(finalWarning);
|
||||||
|
|
||||||
|
@ -118,11 +129,13 @@ if (newerSourceFileFound) {
|
||||||
console.log('Build is up-to-date.');
|
console.log('Build is up-to-date.');
|
||||||
// Ensure no stale warning file exists if build is ok
|
// Ensure no stale warning file exists if build is ok
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(warningsFilePath)) {
|
if (fs.existsSync(warningsFilePath)) {
|
||||||
fs.unlinkSync(warningsFilePath);
|
fs.unlinkSync(warningsFilePath);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`[Check Script] Warning: Could not delete previous warnings file: ${err.message}`);
|
console.warn(
|
||||||
|
`[Check Script] Warning: Could not delete previous warnings file: ${err.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"module": "Node16",
|
"module": "Node16",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"moduleResolution": "node16",
|
"moduleResolution": "node16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue