Fix remaining tslint errors (YAY).

- Also updated ci.yml to ensure that linting failures will break the build.

Fully fixes https://b.corp.google.com/issues/411384603
This commit is contained in:
Taylor Mullen 2025-04-18 19:09:41 -04:00 committed by N. Taylor Mullen
parent 2a850ed051
commit 40e11e053c
21 changed files with 53 additions and 96 deletions

View File

@ -44,12 +44,11 @@ jobs:
# 5. Linting
- name: Run linter
run: npm run lint
continue-on-error: true # TODO: Remove this when we have fixed lint errors
# 6. Type Checking
- name: Run type check
run: npm run typecheck # Or: tsc --noEmit
continue-on-error: true # TODO: Remove this when we have fixed type errors
continue-on-error: true
# 7. Build
# Optional if your tests run directly on TS files (e.g., using ts-jest, ts-node)

View File

@ -1,6 +1,6 @@
import React from 'react';
import { render } from 'ink';
import App from './ui/App.js';
import { App } from './ui/App.js';
import { toolRegistry } from './tools/tool-registry.js';
import { LSTool } from './tools/ls.tool.js';
import { ReadFileTool } from './tools/read-file.tool.js';

View File

@ -1,6 +1,7 @@
import { SchemaValidator } from '../utils/schemaValidator.js';
import { BaseTool, ToolResult } from './tools.js';
import { ToolCallConfirmationDetails } from '../ui/types.js'; // Added for shouldConfirmExecute
import { getErrorMessage } from '../utils/errors.js';
/**
* Parameters for the WebFetch tool
@ -12,18 +13,10 @@ export interface WebFetchToolParams {
url: string;
}
/**
* Standardized result from the WebFetch tool
*/
export interface WebFetchToolResult extends ToolResult {}
/**
* Implementation of the WebFetch tool that reads content from a URL.
*/
export class WebFetchTool extends BaseTool<
WebFetchToolParams,
WebFetchToolResult
> {
export class WebFetchTool extends BaseTool<WebFetchToolParams, ToolResult> {
static readonly Name: string = 'web_fetch';
/**
@ -73,7 +66,7 @@ export class WebFetchTool extends BaseTool<
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
return `Invalid URL protocol: "${parsedUrl.protocol}". Only 'http:' and 'https:' are supported.`;
}
} catch (error) {
} catch {
// The URL constructor throws if the format is invalid
return `Invalid URL format: "${params.url}". Please provide a valid absolute URL (e.g., 'https://example.com').`;
}
@ -101,6 +94,7 @@ export class WebFetchTool extends BaseTool<
* @returns Whether execute should be confirmed.
*/
async shouldConfirmExecute(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
params: WebFetchToolParams,
): Promise<ToolCallConfirmationDetails | false> {
// Could add logic here to confirm based on domain, etc. if needed
@ -112,7 +106,7 @@ export class WebFetchTool extends BaseTool<
* @param params Parameters for the web fetch operation.
* @returns Result with the fetched content or an error message.
*/
async execute(params: WebFetchToolParams): Promise<WebFetchToolResult> {
async execute(params: WebFetchToolParams): Promise<ToolResult> {
const validationError = this.invalidParams(params);
if (validationError) {
return {
@ -159,10 +153,10 @@ export class WebFetchTool extends BaseTool<
llmContent,
returnDisplay: `Fetched content from ${url}`, // Simple display message
};
} catch (error: any) {
} catch (error: unknown) {
// This catches network errors (DNS resolution, connection refused, etc.)
// and errors from the URL constructor if somehow bypassed validation (unlikely)
const errorMessage = `Failed to fetch data from ${url}. Error: ${error instanceof Error ? error.message : String(error)}`;
const errorMessage = `Failed to fetch data from ${url}. Error: ${getErrorMessage(error)}`;
return {
llmContent: `Error: ${errorMessage}`,
returnDisplay: `**Error:** ${errorMessage}`,

View File

@ -1,32 +1,27 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import { Box, Text } from 'ink';
import fs from 'fs';
import path from 'path';
import os from 'os';
import type { HistoryItem } from './types.js';
import { useGeminiStream } from './hooks/useGeminiStream.js';
import { useLoadingIndicator } from './hooks/useLoadingIndicator.js';
import Header from './components/Header.js';
import Tips from './components/Tips.js';
import HistoryDisplay from './components/HistoryDisplay.js';
import LoadingIndicator from './components/LoadingIndicator.js';
import InputPrompt from './components/InputPrompt.js';
import Footer from './components/Footer.js';
import { Header } from './components/Header.js';
import { Tips } from './components/Tips.js';
import { HistoryDisplay } from './components/HistoryDisplay.js';
import { LoadingIndicator } from './components/LoadingIndicator.js';
import { InputPrompt } from './components/InputPrompt.js';
import { Footer } from './components/Footer.js';
import { StreamingState } from '../core/gemini-stream.js';
import { PartListUnion } from '@google/genai';
import ITermDetectionWarning from './utils/itermDetection.js';
import { ITermDetectionWarning } from './utils/itermDetection.js';
import {
useStartupWarnings,
useInitializationErrorEffect,
} from './hooks/useAppEffects.js';
const warningsFilePath = path.join(os.tmpdir(), 'gemini-code-cli-warnings.txt');
interface AppProps {
directory: string;
}
const App = ({ directory }: AppProps) => {
export const App = ({ directory }: AppProps) => {
const [query, setQuery] = useState('');
const [history, setHistory] = useState<HistoryItem[]>([]);
const [startupWarnings, setStartupWarnings] = useState<string[]>([]);
@ -138,5 +133,3 @@ const App = ({ directory }: AppProps) => {
</Box>
);
};
export default App;

View File

@ -5,7 +5,7 @@ interface FooterProps {
queryLength: number;
}
const Footer: React.FC<FooterProps> = ({ queryLength }) => (
export const Footer: React.FC<FooterProps> = ({ queryLength }) => (
<Box marginTop={1} justifyContent="space-between">
<Box minWidth={15}>
<Text color="gray">{queryLength === 0 ? '? for shortcuts' : ''}</Text>
@ -13,5 +13,3 @@ const Footer: React.FC<FooterProps> = ({ queryLength }) => (
<Text color="blue">Gemini</Text>
</Box>
);
export default Footer;

View File

@ -7,7 +7,7 @@ interface HeaderProps {
cwd: string;
}
const Header: React.FC<HeaderProps> = ({ cwd }) => (
export const Header: React.FC<HeaderProps> = ({ cwd }) => (
<>
{/* Static Header Art */}
<Box marginBottom={1}>
@ -34,5 +34,3 @@ const Header: React.FC<HeaderProps> = ({ cwd }) => (
</Box>
</>
);
export default Header;

View File

@ -1,12 +1,11 @@
import React from 'react';
import { Box } from 'ink';
import type { HistoryItem } from '../types.js';
import { UI_WIDTH } from '../constants.js';
import UserMessage from './messages/UserMessage.js';
import GeminiMessage from './messages/GeminiMessage.js';
import InfoMessage from './messages/InfoMessage.js';
import ErrorMessage from './messages/ErrorMessage.js';
import ToolGroupMessage from './messages/ToolGroupMessage.js';
import { UserMessage } from './messages/UserMessage.js';
import { GeminiMessage } from './messages/GeminiMessage.js';
import { InfoMessage } from './messages/InfoMessage.js';
import { ErrorMessage } from './messages/ErrorMessage.js';
import { ToolGroupMessage } from './messages/ToolGroupMessage.js';
import { PartListUnion } from '@google/genai';
interface HistoryDisplayProps {
@ -14,7 +13,7 @@ interface HistoryDisplayProps {
onSubmit: (value: PartListUnion) => void;
}
const HistoryDisplay: React.FC<HistoryDisplayProps> = ({
export const HistoryDisplay: React.FC<HistoryDisplayProps> = ({
history,
onSubmit,
}) => (
@ -36,4 +35,3 @@ const HistoryDisplay: React.FC<HistoryDisplayProps> = ({
))}
</Box>
);
export default HistoryDisplay;

View File

@ -10,7 +10,7 @@ interface InputPromptProps {
isActive: boolean;
}
const InputPrompt: React.FC<InputPromptProps> = ({
export const InputPrompt: React.FC<InputPromptProps> = ({
query,
setQuery,
onSubmit,
@ -33,5 +33,3 @@ const InputPrompt: React.FC<InputPromptProps> = ({
</Box>
);
};
export default InputPrompt;

View File

@ -8,7 +8,7 @@ interface LoadingIndicatorProps {
elapsedTime: number;
}
const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
isLoading,
currentLoadingPhrase,
elapsedTime,
@ -30,5 +30,3 @@ const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
</Box>
);
};
export default LoadingIndicator;

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Box, Text } from 'ink';
import { UI_WIDTH } from '../constants.js';
const Tips: React.FC = () => (
export const Tips: React.FC = () => (
<Box flexDirection="column" marginBottom={1} width={UI_WIDTH}>
<Text>Tips for getting started:</Text>
<Text>
@ -16,5 +16,3 @@ const Tips: React.FC = () => (
<Text>4. Be specific for the best results.</Text>
</Box>
);
export default Tips;

View File

@ -85,7 +85,7 @@ interface DiffRendererProps {
const DEFAULT_TAB_WIDTH = 4; // Spaces per tab for normalization
const DiffRenderer: React.FC<DiffRendererProps> = ({
export const DiffRenderer: React.FC<DiffRendererProps> = ({
diffContent,
tabWidth = DEFAULT_TAB_WIDTH,
}) => {
@ -157,6 +157,9 @@ const DiffRenderer: React.FC<DiffRendererProps> = ({
dim = true;
prefixSymbol = ' ';
break;
default:
throw new Error(`Unknown line type: ${line.type}`);
break;
}
// Render the line content *after* stripping the calculated *minimum* baseIndentation.
@ -179,5 +182,3 @@ const DiffRenderer: React.FC<DiffRendererProps> = ({
</Box>
);
};
export default DiffRenderer;

View File

@ -5,7 +5,7 @@ interface ErrorMessageProps {
text: string;
}
const ErrorMessage: React.FC<ErrorMessageProps> = ({ text }) => {
export const ErrorMessage: React.FC<ErrorMessageProps> = ({ text }) => {
const prefix = '✕ ';
const prefixWidth = prefix.length;
@ -22,5 +22,3 @@ const ErrorMessage: React.FC<ErrorMessageProps> = ({ text }) => {
</Box>
);
};
export default ErrorMessage;

View File

@ -6,7 +6,7 @@ interface GeminiMessageProps {
text: string;
}
const GeminiMessage: React.FC<GeminiMessageProps> = ({ text }) => {
export const GeminiMessage: React.FC<GeminiMessageProps> = ({ text }) => {
const prefix = '✦ ';
const prefixWidth = prefix.length;
@ -40,5 +40,3 @@ const GeminiMessage: React.FC<GeminiMessageProps> = ({ text }) => {
</Box>
);
};
export default GeminiMessage;

View File

@ -5,7 +5,7 @@ interface InfoMessageProps {
text: string;
}
const InfoMessage: React.FC<InfoMessageProps> = ({ text }) => {
export const InfoMessage: React.FC<InfoMessageProps> = ({ text }) => {
const prefix = ' ';
const prefixWidth = prefix.length;
@ -22,5 +22,3 @@ const InfoMessage: React.FC<InfoMessageProps> = ({ text }) => {
</Box>
);
};
export default InfoMessage;

View File

@ -6,9 +6,9 @@ import {
ToolEditConfirmationDetails,
ToolConfirmationOutcome,
ToolExecuteConfirmationDetails,
} from '../../types.js'; // Adjust path as needed
} from '../../types.js';
import { PartListUnion } from '@google/genai';
import DiffRenderer from './DiffRenderer.js';
import { DiffRenderer } from './DiffRenderer.js';
import { UI_WIDTH } from '../../constants.js';
export interface ToolConfirmationMessageProps {
@ -27,9 +27,9 @@ interface InternalOption {
value: ToolConfirmationOutcome;
}
const ToolConfirmationMessage: React.FC<ToolConfirmationMessageProps> = ({
confirmationDetails,
}) => {
export const ToolConfirmationMessage: React.FC<
ToolConfirmationMessageProps
> = ({ confirmationDetails }) => {
const { onConfirm } = confirmationDetails;
useInput((_, key) => {
@ -42,14 +42,11 @@ const ToolConfirmationMessage: React.FC<ToolConfirmationMessageProps> = ({
onConfirm(item.value);
};
let title: string;
let bodyContent: React.ReactNode | null = null; // Removed contextDisplay here
let question: string;
const options: InternalOption[] = [];
if (isEditDetails(confirmationDetails)) {
title = 'Edit'; // Title for the outer box
// Body content is now the DiffRenderer, passing filename to it
// The bordered box is removed from here and handled within DiffRenderer
bodyContent = <DiffRenderer diffContent={confirmationDetails.fileDiff} />;
@ -69,7 +66,6 @@ const ToolConfirmationMessage: React.FC<ToolConfirmationMessageProps> = ({
} else {
const executionProps =
confirmationDetails as ToolExecuteConfirmationDetails;
title = 'Execute Command'; // Title for the outer box
// For execution, we still need context display and description
const commandDisplay = <Text color="cyan">{executionProps.command}</Text>;
@ -118,5 +114,3 @@ const ToolConfirmationMessage: React.FC<ToolConfirmationMessageProps> = ({
</Box>
);
};
export default ToolConfirmationMessage;

View File

@ -1,9 +1,9 @@
import React from 'react';
import { Box } from 'ink';
import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js';
import ToolMessage from './ToolMessage.js';
import { ToolMessage } from './ToolMessage.js';
import { PartListUnion } from '@google/genai';
import ToolConfirmationMessage from './ToolConfirmationMessage.js';
import { ToolConfirmationMessage } from './ToolConfirmationMessage.js';
interface ToolGroupMessageProps {
toolCalls: IndividualToolCallDisplay[];
@ -11,7 +11,7 @@ interface ToolGroupMessageProps {
}
// Main component renders the border and maps the tools using ToolMessage
const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
toolCalls,
onSubmit,
}) => {
@ -44,5 +44,3 @@ const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
</Box>
);
};
export default ToolGroupMessage;

View File

@ -3,7 +3,7 @@ import { Box, Text } from 'ink';
import Spinner from 'ink-spinner';
import { ToolCallStatus } from '../../types.js';
import { ToolResultDisplay } from '../../../tools/tools.js';
import DiffRenderer from './DiffRenderer.js';
import { DiffRenderer } from './DiffRenderer.js';
import { MarkdownRenderer } from '../../utils/MarkdownRenderer.js';
interface ToolMessageProps {
@ -13,7 +13,7 @@ interface ToolMessageProps {
status: ToolCallStatus;
}
const ToolMessage: React.FC<ToolMessageProps> = ({
export const ToolMessage: React.FC<ToolMessageProps> = ({
name,
description,
resultDisplay,
@ -70,5 +70,3 @@ const ToolMessage: React.FC<ToolMessageProps> = ({
</Box>
);
};
export default ToolMessage;

View File

@ -5,7 +5,7 @@ interface UserMessageProps {
text: string;
}
const UserMessage: React.FC<UserMessageProps> = ({ text }) => {
export const UserMessage: React.FC<UserMessageProps> = ({ text }) => {
const prefix = '> ';
const prefixWidth = prefix.length;
@ -20,5 +20,3 @@ const UserMessage: React.FC<UserMessageProps> = ({ text }) => {
</Box>
);
};
export default UserMessage;

View File

@ -3,6 +3,7 @@ import fs from 'fs';
import path from 'path';
import os from 'os';
import type { HistoryItem } from '../types.js';
import { getErrorMessage } from '../../utils/errors.js';
const warningsFilePath = path.join(os.tmpdir(), 'gemini-code-cli-warnings.txt');
@ -19,17 +20,17 @@ export function useStartupWarnings(
);
try {
fs.unlinkSync(warningsFilePath);
} catch (unlinkErr: any) {
} catch {
setStartupWarnings((prev) => [
...prev,
`Warning: Could not delete temporary warnings file.`,
]);
}
}
} catch (err: any) {
} catch (err: unknown) {
setStartupWarnings((prev) => [
...prev,
`Error checking/reading warnings file: ${err.message}`,
`Error checking/reading warnings file: ${getErrorMessage(err)}`,
]);
}
}, [setStartupWarnings]); // Include setStartupWarnings in dependency array

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Box, Text } from 'ink';
const ITermDetectionWarning: React.FC = () => {
export const ITermDetectionWarning: React.FC = () => {
if (process.env.TERM_PROGRAM !== 'iTerm.app') {
return null; // Don't render anything if not in iTerm
}
@ -12,5 +12,3 @@ const ITermDetectionWarning: React.FC = () => {
</Box>
);
};
export default ITermDetectionWarning;

View File

@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import fs from 'fs';
import path from 'path';
import os from 'os'; // Import os module