top level imports and such moved

This commit is contained in:
dal 2025-10-08 10:43:24 -06:00
parent f9df968a15
commit d123494372
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
13 changed files with 39 additions and 170 deletions

View File

@ -101,6 +101,34 @@ class UserService { // Never do this
}
```
### Import Patterns
- **Always use top-level imports** - Import all dependencies at the top of the file
- **No dynamic imports** - Avoid `await import()` in the middle of functions
- **Rationale** - Prioritize code simplicity and consistency over premature optimization
- **Exception** - Only use dynamic imports if you have clear evidence of measurable startup performance issues
```typescript
// Good: Top-level imports
import { runHeadless } from '../services/headless-handler';
import { render } from 'ink';
export function handleCommand(options) {
if (options.prompt) {
return runHeadless(options);
}
return render(<Main />);
}
// Bad: Dynamic imports without justification
export async function handleCommand(options) {
if (options.prompt) {
const { runHeadless } = await import('../services/headless-handler');
return runHeadless(options);
}
return render(<Main />);
}
```
## Cross-Cutting Concerns
### Environment Variables

View File

@ -3,6 +3,7 @@ import { render } from 'ink';
import { Main } from '../commands/main/main';
import { getCurrentVersion } from '../commands/update/update-handler';
import { setupPreActionHook } from './hooks';
import { runHeadless } from '../services/headless-handler';
interface RootOptions {
cwd?: string;
@ -32,7 +33,6 @@ program.action(async (options: RootOptions) => {
// Check if running in headless mode
if (options.prompt) {
try {
const { runHeadless } = await import('../services/headless-handler');
const chatId = await runHeadless({
prompt: options.prompt,
...(options.chatId && { chatId: options.chatId }),

View File

@ -1,5 +1,7 @@
import { createBusterSDK } from '@buster/sdk';
import { Command } from 'commander';
import { render } from 'ink';
import { saveCredentials } from '../../utils/credentials';
import { Auth } from './auth';
/**
@ -33,9 +35,6 @@ export function createAuthCommand(): Command {
// If we have an API key in CI, just validate and save it without interactive UI
if (isCIEnvironment && (options.apiKey || process.env.BUSTER_API_KEY)) {
const { createBusterSDK } = await import('@buster/sdk');
const { saveCredentials } = await import('../../utils/credentials');
const apiKey = options.apiKey || process.env.BUSTER_API_KEY;
const host =
options.host ||

View File

@ -1,7 +1,9 @@
import { Command } from 'commander';
import { render } from 'ink';
import { DeployCommand } from './deploy';
import { deployHandler } from './deploy-handler.js';
import { DeployOptionsSchema } from './schemas';
import { formatDeployError, getExitCode, isDeploymentValidationError } from './utils/errors.js';
/**
* Creates the deploy command for deploying semantic models
@ -32,15 +34,9 @@ export function createDeployCommand(): Command {
render(<DeployCommand {...parsedOptions} />);
} else {
// Direct execution for cleaner CLI output
const { deployHandler } = await import('./deploy-handler.js');
await deployHandler(parsedOptions);
}
} catch (error) {
// Import the error formatter and type guard
const { isDeploymentValidationError, formatDeployError, getExitCode } = await import(
'./utils/errors.js'
);
// Check if it's a DeploymentValidationError to handle it specially
if (isDeploymentValidationError(error)) {
// The error message already contains the formatted output

View File

@ -1,25 +0,0 @@
import { render } from 'ink-testing-library';
import { describe, expect, it } from 'vitest';
import { HelloCommand } from './hello';
describe('HelloCommand', () => {
it('should render greeting with default name', () => {
const { lastFrame } = render(<HelloCommand name='World' />);
expect(lastFrame()).toContain('Hello, World!');
expect(lastFrame()).toContain('Buster CLI');
});
it('should render greeting in uppercase when flag is set', () => {
const { lastFrame } = render(<HelloCommand name='Claude' uppercase={true} />);
expect(lastFrame()).toContain('HELLO, CLAUDE!');
});
it('should render greeting in normal case when uppercase flag is false', () => {
const { lastFrame } = render(<HelloCommand name='Claude' uppercase={false} />);
expect(lastFrame()).toContain('Hello, Claude!');
expect(lastFrame()).not.toContain('HELLO, CLAUDE!');
});
});

View File

@ -1,28 +0,0 @@
import chalk from 'chalk';
import { Box, Text } from 'ink';
import type React from 'react';
import { useEffect } from 'react';
interface HelloCommandProps {
name: string;
uppercase?: boolean;
}
export const HelloCommand: React.FC<HelloCommandProps> = ({ name, uppercase }) => {
const greeting = `Hello, ${name}!`;
const displayText = uppercase ? greeting.toUpperCase() : greeting;
useEffect(() => {
// Exit after rendering
setTimeout(() => {
process.exit(0);
}, 100);
}, []);
return (
<Box flexDirection='column'>
<Text color='green'>{chalk.bold('🚀 Buster CLI')}</Text>
<Text>{displayText}</Text>
</Box>
);
};

View File

@ -1,51 +0,0 @@
import chalk from 'chalk';
import { Box, Text, useApp, useInput } from 'ink';
import type React from 'react';
import { useState } from 'react';
export const InteractiveCommand: React.FC = () => {
const [selectedOption, setSelectedOption] = useState(0);
const { exit } = useApp();
const options = ['Create a new project', 'Deploy to production', 'Run tests', 'Exit'];
useInput((input, key) => {
if (key.upArrow) {
setSelectedOption((prev) => Math.max(0, prev - 1));
} else if (key.downArrow) {
setSelectedOption((prev) => Math.min(options.length - 1, prev + 1));
} else if (key.return) {
if (selectedOption === options.length - 1) {
exit();
} else {
// Handle selection
console.info(`\nYou selected: ${options[selectedOption]}`);
exit();
}
} else if (input === 'q' || key.escape) {
exit();
}
});
return (
<Box flexDirection='column'>
<Box marginBottom={1}>
<Text color='cyan' bold>
🚀 Buster CLI - Interactive Mode
</Text>
</Box>
<Text dimColor>Use arrow keys to navigate, Enter to select, Q to quit</Text>
<Box marginTop={1} flexDirection='column'>
{options.map((option, index) => (
<Box key={option}>
<Text {...(selectedOption === index ? { color: 'green' } : {})}>
{selectedOption === index ? chalk.bold('▶ ') : ' '}
{option}
</Text>
</Box>
))}
</Box>
</Box>
);
};

View File

@ -14,6 +14,7 @@ import { AgentMessageComponent } from '../../components/message';
import { SettingsForm } from '../../components/settings-form';
import { ExpansionContext } from '../../hooks/use-expansion';
import type { CliAgentMessage } from '../../services/analytics-engineer-handler';
import { runAnalyticsEngineerAgent } from '../../services/analytics-engineer-handler';
import type { Conversation } from '../../utils/conversation-history';
import { loadConversation, saveModelMessages } from '../../utils/conversation-history';
import { getCurrentChatId, initNewSession, setSessionChatId } from '../../utils/session';
@ -137,9 +138,6 @@ export function Main() {
// Save to disk
await saveModelMessages(chatId, cwd, updatedModelMessages);
// Import and run the analytics engineer agent
const { runAnalyticsEngineerAgent } = await import('../../services/analytics-engineer-handler');
// Create AbortController for this agent execution
const abortController = new AbortController();
abortControllerRef.current = abortController;

View File

@ -1,7 +1,7 @@
import { spawn } from 'node:child_process';
import { createHash } from 'node:crypto';
import { createWriteStream, existsSync } from 'node:fs';
import { chmod, mkdir, rename, unlink } from 'node:fs/promises';
import { createReadStream, createWriteStream, existsSync } from 'node:fs';
import { chmod, mkdir, readFile, rename, rm, unlink } from 'node:fs/promises';
import { arch, platform, tmpdir } from 'node:os';
import { join } from 'node:path';
import { pipeline } from 'node:stream/promises';
@ -102,7 +102,6 @@ async function downloadFile(url: string, destination: string): Promise<void> {
* Verify file checksum
*/
async function verifyChecksum(filePath: string, expectedChecksum: string): Promise<boolean> {
const { createReadStream } = await import('node:fs');
const hash = createHash('sha256');
const stream = createReadStream(filePath);
@ -315,7 +314,6 @@ export async function updateHandler(options: UpdateOptions): Promise<UpdateResul
]);
// Read and validate checksum
const { readFile } = await import('node:fs/promises');
const checksumContent = await readFile(checksumPath, 'utf-8');
// SHA256 checksums are 64 hex characters
@ -346,7 +344,6 @@ export async function updateHandler(options: UpdateOptions): Promise<UpdateResul
await replaceBinary(extractedBinary);
// Clean up temp directory
const { rm } = await import('node:fs/promises');
await rm(tempDir, { recursive: true, force: true });
return {
@ -361,7 +358,6 @@ export async function updateHandler(options: UpdateOptions): Promise<UpdateResul
} catch (error) {
// Clean up on error
try {
const { rm } = await import('node:fs/promises');
await rm(tempDir, { recursive: true, force: true });
} catch {
// Ignore cleanup errors

View File

@ -1,42 +0,0 @@
import { Box, Text, useApp } from 'ink';
import { useEffect } from 'react';
import { AnimatedLogo } from '../components/animated-logo';
export function Welcome() {
const { exit } = useApp();
// Auto-exit after a few seconds
useEffect(() => {
const timer = setTimeout(() => {
exit();
}, 5000);
return () => clearTimeout(timer);
}, [exit]);
return (
<Box paddingY={2} paddingX={2}>
<Box marginRight={4}>
<AnimatedLogo color='#7C3AED' />
</Box>
<Box flexDirection='column' justifyContent='center'>
<Text bold>Welcome to Buster</Text>
<Box marginTop={1}>
<Text dimColor>Type / to use slash commands</Text>
</Box>
<Box>
<Text dimColor>Type @ to mention files</Text>
</Box>
<Box>
<Text dimColor>Ctrl-C to exit</Text>
</Box>
<Box marginTop={2}>
<Text dimColor>/help for more</Text>
</Box>
<Box marginTop={2}>
<Text color='#7C3AED'>"Run `buster` and fix all the errors"</Text>
</Box>
</Box>
</Box>
);
}

View File

@ -1,4 +1,5 @@
import { z } from 'zod';
import { getCredentials } from './credentials';
const ProxyConfigSchema = z.object({
baseURL: z.string().url().describe('Base URL for the AI proxy endpoint'),
@ -18,7 +19,6 @@ export type ProxyConfig = z.infer<typeof ProxyConfigSchema>;
* API key comes from credentials (required)
*/
export async function getProxyConfig(): Promise<ProxyConfig> {
const { getCredentials } = await import('./credentials');
const creds = await getCredentials();
if (!creds?.apiKey) {

View File

@ -1,4 +1,4 @@
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
import { mkdir, readFile, readdir, unlink, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { z } from 'zod';
@ -205,7 +205,6 @@ export async function getLatestConversation(
*/
export async function deleteConversation(chatId: string, workingDirectory: string): Promise<void> {
const filePath = getConversationFilePath(chatId, workingDirectory);
const { unlink } = await import('node:fs/promises');
await unlink(filePath);
}

View File

@ -1,4 +1,4 @@
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { type VersionCache, VersionCacheSchema } from './version-schemas';
@ -72,7 +72,6 @@ export async function getCachedVersion(): Promise<VersionCache | null> {
*/
export async function clearVersionCache(): Promise<void> {
try {
const { unlink } = await import('node:fs/promises');
await unlink(CACHE_FILE);
} catch {
// Cache file might not exist, that's fine