mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat: standardize logging across UI components
- Replaced console.log and console.error statements with logger methods from @automaker/utils in various UI components, ensuring consistent log formatting and improved readability. - Enhanced error handling by utilizing logger methods to provide clearer context for issues encountered during operations. - Updated multiple views and hooks to integrate the new logging system, improving maintainability and debugging capabilities. This update significantly enhances the observability of UI components, facilitating easier troubleshooting and monitoring.
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { LogLevel, createLogger, getLogLevel, setLogLevel } from '@automaker/utils';
|
||||
import {
|
||||
LogLevel,
|
||||
createLogger,
|
||||
getLogLevel,
|
||||
setLogLevel,
|
||||
setColorsEnabled,
|
||||
setTimestampsEnabled,
|
||||
} from '@automaker/utils';
|
||||
|
||||
describe('logger.ts', () => {
|
||||
let consoleSpy: {
|
||||
@@ -11,6 +18,9 @@ describe('logger.ts', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
originalLogLevel = getLogLevel();
|
||||
// Disable colors and timestamps for predictable test output
|
||||
setColorsEnabled(false);
|
||||
setTimestampsEnabled(false);
|
||||
consoleSpy = {
|
||||
log: vi.spyOn(console, 'log').mockImplementation(() => {}),
|
||||
warn: vi.spyOn(console, 'warn').mockImplementation(() => {}),
|
||||
@@ -51,7 +61,8 @@ describe('logger.ts', () => {
|
||||
|
||||
logger.info('test message');
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith('[TestContext]', 'test message');
|
||||
// New format: 'LEVEL [Context]' as first arg, then message
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith('INFO [TestContext]', 'test message');
|
||||
});
|
||||
|
||||
it('should log error at all log levels', () => {
|
||||
@@ -59,7 +70,7 @@ describe('logger.ts', () => {
|
||||
|
||||
setLogLevel(LogLevel.ERROR);
|
||||
logger.error('error message');
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith('[Test]', 'error message');
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith('ERROR [Test]', 'error message');
|
||||
});
|
||||
|
||||
it('should log warn when level is WARN or higher', () => {
|
||||
@@ -71,7 +82,7 @@ describe('logger.ts', () => {
|
||||
|
||||
setLogLevel(LogLevel.WARN);
|
||||
logger.warn('warn message 2');
|
||||
expect(consoleSpy.warn).toHaveBeenCalledWith('[Test]', 'warn message 2');
|
||||
expect(consoleSpy.warn).toHaveBeenCalledWith('WARN [Test]', 'warn message 2');
|
||||
});
|
||||
|
||||
it('should log info when level is INFO or higher', () => {
|
||||
@@ -83,7 +94,7 @@ describe('logger.ts', () => {
|
||||
|
||||
setLogLevel(LogLevel.INFO);
|
||||
logger.info('info message 2');
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith('[Test]', 'info message 2');
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith('INFO [Test]', 'info message 2');
|
||||
});
|
||||
|
||||
it('should log debug only when level is DEBUG', () => {
|
||||
@@ -95,7 +106,7 @@ describe('logger.ts', () => {
|
||||
|
||||
setLogLevel(LogLevel.DEBUG);
|
||||
logger.debug('debug message 2');
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith('[Test]', '[DEBUG]', 'debug message 2');
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith('DEBUG [Test]', 'debug message 2');
|
||||
});
|
||||
|
||||
it('should pass multiple arguments to log functions', () => {
|
||||
@@ -103,7 +114,27 @@ describe('logger.ts', () => {
|
||||
const logger = createLogger('Multi');
|
||||
|
||||
logger.info('message', { data: 'value' }, 123);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith('[Multi]', 'message', { data: 'value' }, 123);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
'INFO [Multi]',
|
||||
'message',
|
||||
{ data: 'value' },
|
||||
123
|
||||
);
|
||||
});
|
||||
|
||||
it('should include timestamps when enabled', () => {
|
||||
setTimestampsEnabled(true);
|
||||
setLogLevel(LogLevel.INFO);
|
||||
const logger = createLogger('Timestamp');
|
||||
|
||||
logger.info('test');
|
||||
|
||||
// First arg should contain ISO timestamp format
|
||||
const firstArg = consoleSpy.log.mock.calls[0][0];
|
||||
expect(firstArg).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z INFO \[Timestamp\]$/);
|
||||
expect(consoleSpy.log.mock.calls[0][1]).toBe('test');
|
||||
|
||||
setTimestampsEnabled(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -248,9 +248,9 @@ describe('claude-provider.ts', () => {
|
||||
await expect(collectAsyncGenerator(generator)).rejects.toThrow('SDK execution failed');
|
||||
|
||||
// Should log error with classification info (via logger)
|
||||
// Logger format: [Context] message, data
|
||||
// Logger format: 'ERROR [Context]' message, data
|
||||
const errorCall = consoleErrorSpy.mock.calls[0];
|
||||
expect(errorCall[0]).toBe('[ClaudeProvider]');
|
||||
expect(errorCall[0]).toMatch(/ERROR.*\[ClaudeProvider\]/);
|
||||
expect(errorCall[1]).toBe('executeQuery() error during execution:');
|
||||
expect(errorCall[2]).toMatchObject({
|
||||
type: expect.any(String),
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('feature-loader.ts', () => {
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('feature-2');
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[FeatureLoader]',
|
||||
expect.stringMatching(/WARN.*\[FeatureLoader\]/),
|
||||
expect.stringContaining("missing required 'id' field")
|
||||
);
|
||||
|
||||
@@ -191,7 +191,7 @@ describe('feature-loader.ts', () => {
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[FeatureLoader]',
|
||||
expect.stringMatching(/WARN.*\[FeatureLoader\]/),
|
||||
expect.stringContaining('Failed to parse feature.json')
|
||||
);
|
||||
|
||||
@@ -363,7 +363,7 @@ describe('feature-loader.ts', () => {
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[FeatureLoader]',
|
||||
expect.stringMatching(/ERROR.*\[FeatureLoader\]/),
|
||||
expect.stringContaining('Failed to delete feature'),
|
||||
expect.objectContaining({ message: 'Permission denied' })
|
||||
);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"preview": "vite preview",
|
||||
"lint": "npx eslint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"pretest": "node scripts/kill-test-servers.mjs && node scripts/setup-e2e-fixtures.mjs",
|
||||
"test": "playwright test",
|
||||
"test:headed": "playwright test --headed",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { RouterProvider } from '@tanstack/react-router';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { router } from './utils/router';
|
||||
import { SplashScreen } from './components/splash-screen';
|
||||
import { useSettingsMigration } from './hooks/use-settings-migration';
|
||||
@@ -7,6 +8,8 @@ import { useCursorStatusInit } from './hooks/use-cursor-status-init';
|
||||
import './styles/global.css';
|
||||
import './styles/theme-imports';
|
||||
|
||||
const logger = createLogger('App');
|
||||
|
||||
export default function App() {
|
||||
const [showSplash, setShowSplash] = useState(() => {
|
||||
// Only show splash once per session
|
||||
@@ -19,7 +22,7 @@ export default function App() {
|
||||
// Run settings migration on startup (localStorage -> file storage)
|
||||
const migrationState = useSettingsMigration();
|
||||
if (migrationState.migrated) {
|
||||
console.log('[App] Settings migrated to file storage');
|
||||
logger.info('Settings migrated to file storage');
|
||||
}
|
||||
|
||||
// Initialize Cursor CLI status at startup
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { ImageIcon, Upload, Loader2, Trash2 } from 'lucide-react';
|
||||
|
||||
const logger = createLogger('BoardBackgroundModal');
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
@@ -113,7 +116,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
||||
setPreviewImage(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to process image:', error);
|
||||
logger.error('Failed to process image:', error);
|
||||
toast.error('Failed to process image');
|
||||
setPreviewImage(null);
|
||||
} finally {
|
||||
@@ -185,7 +188,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
||||
toast.error(result.error || 'Failed to clear background image');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to clear background:', error);
|
||||
logger.error('Failed to clear background:', error);
|
||||
toast.error('Failed to clear background');
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -29,6 +30,8 @@ import { cn } from '@/lib/utils';
|
||||
import { useFileBrowser } from '@/contexts/file-browser-context';
|
||||
import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config';
|
||||
|
||||
const logger = createLogger('NewProjectModal');
|
||||
|
||||
interface ValidationErrors {
|
||||
projectName?: boolean;
|
||||
workspaceDir?: boolean;
|
||||
@@ -78,7 +81,7 @@ export function NewProjectModal({
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to get default workspace directory:', error);
|
||||
logger.error('Failed to get default workspace directory:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoadingWorkspace(false);
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { ShieldX, RefreshCw, Container, Copy, Check } from 'lucide-react';
|
||||
|
||||
const logger = createLogger('SandboxRejectionScreen');
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const DOCKER_COMMAND = 'npm run dev:docker';
|
||||
@@ -26,7 +29,7 @@ export function SandboxRejectionScreen() {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
logger.error('Failed to copy:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { ShieldAlert, Copy, Check } from 'lucide-react';
|
||||
|
||||
const logger = createLogger('SandboxRiskDialog');
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -43,7 +46,7 @@ export function SandboxRiskDialog({ open, onConfirm, onDeny }: SandboxRiskDialog
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
logger.error('Failed to copy:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useNavigate, useLocation } from '@tanstack/react-router';
|
||||
|
||||
const logger = createLogger('Sidebar');
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useAppStore, type ThemeMode } from '@/store/app-store';
|
||||
import { useKeyboardShortcuts, useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
|
||||
@@ -215,7 +218,7 @@ export function Sidebar() {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Sidebar] Failed to open project:', error);
|
||||
logger.error('Failed to open project:', error);
|
||||
toast.error('Failed to open project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('ProjectCreation');
|
||||
import { initializeProject } from '@/lib/project-init';
|
||||
import { toast } from 'sonner';
|
||||
import type { StarterTemplate } from '@/lib/templates';
|
||||
@@ -82,7 +85,7 @@ export function useProjectCreation({
|
||||
|
||||
toast.success('Project created successfully');
|
||||
} catch (error) {
|
||||
console.error('[ProjectCreation] Failed to finalize project:', error);
|
||||
logger.error('Failed to finalize project:', error);
|
||||
toast.error('Failed to initialize project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -108,7 +111,7 @@ export function useProjectCreation({
|
||||
// Finalize project setup
|
||||
await finalizeProjectCreation(projectPath, projectName);
|
||||
} catch (error) {
|
||||
console.error('[ProjectCreation] Failed to create blank project:', error);
|
||||
logger.error('Failed to create blank project:', error);
|
||||
toast.error('Failed to create project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -180,7 +183,7 @@ export function useProjectCreation({
|
||||
description: `Created ${projectName} from ${template.name}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[ProjectCreation] Failed to create from template:', error);
|
||||
logger.error('Failed to create from template:', error);
|
||||
toast.error('Failed to create project from template', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -252,7 +255,7 @@ export function useProjectCreation({
|
||||
description: `Created ${projectName} from ${repoUrl}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[ProjectCreation] Failed to create from custom URL:', error);
|
||||
logger.error('Failed to create from custom URL:', error);
|
||||
toast.error('Failed to create project from URL', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('RunningAgents');
|
||||
|
||||
export function useRunningAgents() {
|
||||
const [runningAgentsCount, setRunningAgentsCount] = useState(0);
|
||||
|
||||
@@ -15,7 +18,7 @@ export function useRunningAgents() {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Sidebar] Error fetching running agents count:', error);
|
||||
logger.error('Error fetching running agents count:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('SetupDialog');
|
||||
import { toast } from 'sonner';
|
||||
import type { FeatureCount } from '@/components/views/spec-view/types';
|
||||
|
||||
@@ -53,7 +56,7 @@ export function useSetupDialog({
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('[SetupDialog] Failed to start spec creation:', result.error);
|
||||
logger.error('Failed to start spec creation:', result.error);
|
||||
setSpecCreatingForProject(null);
|
||||
toast.error('Failed to create specification', {
|
||||
description: result.error,
|
||||
@@ -66,7 +69,7 @@ export function useSetupDialog({
|
||||
}
|
||||
// If successful, we'll wait for the events to update the state
|
||||
} catch (error) {
|
||||
console.error('[SetupDialog] Failed to create spec:', error);
|
||||
logger.error('Failed to create spec:', error);
|
||||
setSpecCreatingForProject(null);
|
||||
toast.error('Failed to create specification', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const logger = createLogger('SpecRegeneration');
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import type { SpecRegenerationEvent } from '@/types/electron';
|
||||
|
||||
@@ -30,16 +33,11 @@ export function useSpecRegeneration({
|
||||
if (!api.specRegeneration) return;
|
||||
|
||||
const unsubscribe = api.specRegeneration.onEvent((event: SpecRegenerationEvent) => {
|
||||
console.log(
|
||||
'[Sidebar] Spec regeneration event:',
|
||||
event.type,
|
||||
'for project:',
|
||||
event.projectPath
|
||||
);
|
||||
logger.debug('Spec regeneration event:', event.type, 'for project:', event.projectPath);
|
||||
|
||||
// Only handle events for the project we're currently setting up
|
||||
if (event.projectPath !== creatingSpecProjectPath && event.projectPath !== setupProjectPath) {
|
||||
console.log('[Sidebar] Ignoring event - not for project being set up');
|
||||
logger.debug('Ignoring event - not for project being set up');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const logger = createLogger('TrashOperations');
|
||||
import { getElectronAPI, type TrashedProject } from '@/lib/electron';
|
||||
|
||||
interface UseTrashOperationsProps {
|
||||
@@ -24,7 +27,7 @@ export function useTrashOperations({
|
||||
description: 'Added back to your project list.',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Sidebar] Failed to restore project:', error);
|
||||
logger.error('Failed to restore project:', error);
|
||||
toast.error('Failed to restore project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -52,7 +55,7 @@ export function useTrashOperations({
|
||||
description: trashedProject.path,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Sidebar] Failed to delete project from disk:', error);
|
||||
logger.error('Failed to delete project from disk:', error);
|
||||
toast.error('Failed to delete project folder', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -69,7 +72,7 @@ export function useTrashOperations({
|
||||
emptyTrash();
|
||||
toast.success('Recycle bin cleared');
|
||||
} catch (error) {
|
||||
console.error('[Sidebar] Failed to empty trash:', error);
|
||||
logger.error('Failed to empty trash:', error);
|
||||
toast.error('Failed to clear recycle bin', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('UnviewedValidations');
|
||||
import type { Project, StoredValidation } from '@/lib/electron';
|
||||
|
||||
/**
|
||||
@@ -38,7 +41,7 @@ export function useUnviewedValidations(currentProject: Project | null) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[useUnviewedValidations] Failed to load count:', err);
|
||||
logger.error('Failed to load count:', err);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
|
||||
const logger = createLogger('SessionManager');
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { HotkeyButton } from '@/components/ui/hotkey-button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -126,7 +129,7 @@ export function SessionManager({
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore errors for individual session checks
|
||||
console.warn(`[SessionManager] Failed to check running state for ${session.id}:`, err);
|
||||
logger.warn(`Failed to check running state for ${session.id}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +230,7 @@ export function SessionManager({
|
||||
const handleArchiveSession = async (sessionId: string) => {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.sessions) {
|
||||
console.error('[SessionManager] Sessions API not available');
|
||||
logger.error('[SessionManager] Sessions API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -240,10 +243,10 @@ export function SessionManager({
|
||||
}
|
||||
await loadSessions();
|
||||
} else {
|
||||
console.error('[SessionManager] Archive failed:', result.error);
|
||||
logger.error('[SessionManager] Archive failed:', result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[SessionManager] Archive error:', error);
|
||||
logger.error('[SessionManager] Archive error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -251,7 +254,7 @@ export function SessionManager({
|
||||
const handleUnarchiveSession = async (sessionId: string) => {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.sessions) {
|
||||
console.error('[SessionManager] Sessions API not available');
|
||||
logger.error('[SessionManager] Sessions API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -260,10 +263,10 @@ export function SessionManager({
|
||||
if (result.success) {
|
||||
await loadSessions();
|
||||
} else {
|
||||
console.error('[SessionManager] Unarchive failed:', result.error);
|
||||
logger.error('[SessionManager] Unarchive failed:', result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[SessionManager] Unarchive error:', error);
|
||||
logger.error('[SessionManager] Unarchive error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import React, { useState, useRef, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('DescriptionImageDropZone');
|
||||
import { ImageIcon, X, Loader2, FileText } from 'lucide-react';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
@@ -108,7 +111,7 @@ export function DescriptionImageDropZone({
|
||||
// Check if saveImageToTemp method exists
|
||||
if (!api.saveImageToTemp) {
|
||||
// Fallback path when saveImageToTemp is not available
|
||||
console.log('[DescriptionImageDropZone] Using fallback path for image');
|
||||
logger.info('Using fallback path for image');
|
||||
return `.automaker/images/${Date.now()}_${filename}`;
|
||||
}
|
||||
|
||||
@@ -118,10 +121,10 @@ export function DescriptionImageDropZone({
|
||||
if (result.success && result.path) {
|
||||
return result.path;
|
||||
}
|
||||
console.error('[DescriptionImageDropZone] Failed to save image:', result.error);
|
||||
logger.error('Failed to save image:', result.error);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('[DescriptionImageDropZone] Error saving image:', error);
|
||||
logger.error('Error saving image:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@@ -216,7 +219,7 @@ export function DescriptionImageDropZone({
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.warn('File upload errors:', errors);
|
||||
logger.warn('File upload errors:', errors);
|
||||
}
|
||||
|
||||
if (newImages.length > 0) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import React, { useState, useRef, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('FeatureImageUpload');
|
||||
import { ImageIcon, X, Upload } from 'lucide-react';
|
||||
import {
|
||||
fileToBase64,
|
||||
@@ -77,7 +80,7 @@ export function FeatureImageUpload({
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.warn('Image upload errors:', errors);
|
||||
logger.warn('Image upload errors:', errors);
|
||||
}
|
||||
|
||||
if (newImages.length > 0) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import React, { useState, useRef, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('ImageDropZone');
|
||||
import { ImageIcon, X, Upload } from 'lucide-react';
|
||||
import type { ImageAttachment } from '@/store/app-store';
|
||||
import {
|
||||
@@ -88,7 +91,7 @@ export function ImageDropZone({
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.warn('Image upload errors:', errors);
|
||||
logger.warn('Image upload errors:', errors);
|
||||
}
|
||||
|
||||
if (newImages.length > 0) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('TaskProgressPanel');
|
||||
import { Check, Loader2, Circle, ChevronDown, ChevronRight, FileCode } from 'lucide-react';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import type { AutoModeEvent } from '@/types/electron';
|
||||
@@ -72,7 +75,7 @@ export function TaskProgressPanel({
|
||||
setCurrentTaskId(currentId || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load initial tasks:', error);
|
||||
logger.error('Failed to load initial tasks:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -19,6 +20,8 @@ import {
|
||||
import { cn } from '@/lib/utils';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('AgentToolsView');
|
||||
|
||||
interface ToolResult {
|
||||
success: boolean;
|
||||
output?: string;
|
||||
@@ -62,7 +65,7 @@ export function AgentToolsView() {
|
||||
|
||||
try {
|
||||
// Simulate agent requesting file read
|
||||
console.log(`[Agent Tool] Requesting to read file: ${readFilePath}`);
|
||||
logger.info(`[Agent Tool] Requesting to read file: ${readFilePath}`);
|
||||
|
||||
const result = await api.readFile(readFilePath);
|
||||
|
||||
@@ -72,14 +75,14 @@ export function AgentToolsView() {
|
||||
output: result.content,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
console.log(`[Agent Tool] File read successful: ${readFilePath}`);
|
||||
logger.info(`[Agent Tool] File read successful: ${readFilePath}`);
|
||||
} else {
|
||||
setReadFileResult({
|
||||
success: false,
|
||||
error: result.error || 'Failed to read file',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
console.log(`[Agent Tool] File read failed: ${result.error}`);
|
||||
logger.info(`[Agent Tool] File read failed: ${result.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
setReadFileResult({
|
||||
@@ -101,7 +104,7 @@ export function AgentToolsView() {
|
||||
|
||||
try {
|
||||
// Simulate agent requesting file write
|
||||
console.log(`[Agent Tool] Requesting to write file: ${writeFilePath}`);
|
||||
logger.info(`[Agent Tool] Requesting to write file: ${writeFilePath}`);
|
||||
|
||||
const result = await api.writeFile(writeFilePath, writeFileContent);
|
||||
|
||||
@@ -111,14 +114,14 @@ export function AgentToolsView() {
|
||||
output: `File written successfully: ${writeFilePath}`,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
console.log(`[Agent Tool] File write successful: ${writeFilePath}`);
|
||||
logger.info(`[Agent Tool] File write successful: ${writeFilePath}`);
|
||||
} else {
|
||||
setWriteFileResult({
|
||||
success: false,
|
||||
error: result.error || 'Failed to write file',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
console.log(`[Agent Tool] File write failed: ${result.error}`);
|
||||
logger.info(`[Agent Tool] File write failed: ${result.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
setWriteFileResult({
|
||||
@@ -140,7 +143,7 @@ export function AgentToolsView() {
|
||||
|
||||
try {
|
||||
// Terminal command simulation for demonstration purposes
|
||||
console.log(`[Agent Tool] Simulating command: ${terminalCommand}`);
|
||||
logger.info(`[Agent Tool] Simulating command: ${terminalCommand}`);
|
||||
|
||||
// Simulated outputs for common commands (preview mode)
|
||||
// In production, the agent executes commands via Claude SDK
|
||||
@@ -165,7 +168,7 @@ export function AgentToolsView() {
|
||||
output: output,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
console.log(`[Agent Tool] Command executed successfully: ${terminalCommand}`);
|
||||
logger.info(`[Agent Tool] Command executed successfully: ${terminalCommand}`);
|
||||
} catch (error) {
|
||||
setTerminalResult({
|
||||
success: false,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('AgentSession');
|
||||
|
||||
interface UseAgentSessionOptions {
|
||||
projectPath: string | undefined;
|
||||
}
|
||||
@@ -44,7 +47,7 @@ export function useAgentSession({ projectPath }: UseAgentSessionOptions): UseAge
|
||||
|
||||
const lastSessionId = getLastSelectedSession(projectPath);
|
||||
if (lastSessionId) {
|
||||
console.log('[AgentView] Restoring last selected session:', lastSessionId);
|
||||
logger.info('Restoring last selected session:', lastSessionId);
|
||||
setCurrentSessionId(lastSessionId);
|
||||
}
|
||||
}, [projectPath, getLastSelectedSession]);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('FileAttachments');
|
||||
import {
|
||||
fileToBase64,
|
||||
generateImageId,
|
||||
@@ -138,7 +141,7 @@ export function useFileAttachments({
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.warn('File upload errors:', errors);
|
||||
logger.warn('File upload errors:', errors);
|
||||
}
|
||||
|
||||
if (newImages.length > 0) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore, FileTreeNode, ProjectAnalysis } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
@@ -21,6 +22,8 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('AnalysisView');
|
||||
|
||||
const IGNORE_PATTERNS = [
|
||||
'node_modules',
|
||||
'.git',
|
||||
@@ -109,7 +112,7 @@ export function AnalysisView() {
|
||||
|
||||
return nodes;
|
||||
} catch (error) {
|
||||
console.error('Failed to scan directory:', path, error);
|
||||
logger.error('Failed to scan directory:', path, error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
@@ -165,7 +168,7 @@ export function AnalysisView() {
|
||||
|
||||
setProjectAnalysis(analysis);
|
||||
} catch (error) {
|
||||
console.error('Analysis failed:', error);
|
||||
logger.error('Analysis failed:', error);
|
||||
} finally {
|
||||
setIsAnalyzing(false);
|
||||
}
|
||||
@@ -373,7 +376,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
||||
setSpecError(writeResult.error || 'Failed to write spec file');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to generate spec:', error);
|
||||
logger.error('Failed to generate spec:', error);
|
||||
setSpecError(error instanceof Error ? error.message : 'Failed to generate spec');
|
||||
} finally {
|
||||
setIsGeneratingSpec(false);
|
||||
@@ -644,7 +647,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
||||
|
||||
setFeatureListGenerated(true);
|
||||
} catch (error) {
|
||||
console.error('Failed to generate feature list:', error);
|
||||
logger.error('Failed to generate feature list:', error);
|
||||
setFeatureListError(
|
||||
error instanceof Error ? error.message : 'Failed to generate feature list'
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState, useCallback, useMemo, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
@@ -62,6 +63,8 @@ import {
|
||||
// Stable empty array to avoid infinite loop in selector
|
||||
const EMPTY_WORKTREES: ReturnType<ReturnType<typeof useAppStore.getState>['getWorktrees']> = [];
|
||||
|
||||
const logger = createLogger('Board');
|
||||
|
||||
export function BoardView() {
|
||||
const {
|
||||
currentProject,
|
||||
@@ -188,7 +191,7 @@ export function BoardView() {
|
||||
|
||||
return result.success && result.exists === true;
|
||||
} catch (error) {
|
||||
console.error('[Board] Error checking context:', error);
|
||||
logger.error('Error checking context:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@@ -222,7 +225,7 @@ export function BoardView() {
|
||||
setPipelineConfig(currentProject.path, result.config);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Failed to load pipeline config:', error);
|
||||
logger.error('Failed to load pipeline config:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -288,7 +291,7 @@ export function BoardView() {
|
||||
setBranchSuggestions(localBranches);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[BoardView] Error fetching branches:', error);
|
||||
logger.error('Error fetching branches:', error);
|
||||
setBranchSuggestions([]);
|
||||
}
|
||||
};
|
||||
@@ -497,7 +500,7 @@ export function BoardView() {
|
||||
if (newFeature) {
|
||||
await handleStartImplementation(newFeature);
|
||||
} else {
|
||||
console.error('Could not find newly created feature to start it automatically.');
|
||||
logger.error('Could not find newly created feature to start it automatically.');
|
||||
toast.error('Failed to auto-start feature', {
|
||||
description: 'The feature was created but could not be started automatically.',
|
||||
});
|
||||
@@ -538,7 +541,7 @@ export function BoardView() {
|
||||
if (newFeature) {
|
||||
await handleStartImplementation(newFeature);
|
||||
} else {
|
||||
console.error('Could not find newly created feature to start it automatically.');
|
||||
logger.error('Could not find newly created feature to start it automatically.');
|
||||
toast.error('Failed to auto-start feature', {
|
||||
description: 'The feature was created but could not be started automatically.',
|
||||
});
|
||||
@@ -561,7 +564,7 @@ export function BoardView() {
|
||||
if (newFeature) {
|
||||
await handleStartImplementation(newFeature);
|
||||
} else {
|
||||
console.error('Could not find newly created feature to start it automatically.');
|
||||
logger.error('Could not find newly created feature to start it automatically.');
|
||||
toast.error('Failed to auto-start feature', {
|
||||
description: 'The feature was created but could not be started automatically.',
|
||||
});
|
||||
@@ -889,10 +892,10 @@ export function BoardView() {
|
||||
// Reload features from server to ensure sync
|
||||
loadFeatures();
|
||||
} else {
|
||||
console.error('[Board] Failed to approve plan:', result.error);
|
||||
logger.error('Failed to approve plan:', result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error approving plan:', error);
|
||||
logger.error('Error approving plan:', error);
|
||||
} finally {
|
||||
setIsPlanApprovalLoading(false);
|
||||
setPendingPlanApproval(null);
|
||||
@@ -945,10 +948,10 @@ export function BoardView() {
|
||||
// Reload features from server to ensure sync
|
||||
loadFeatures();
|
||||
} else {
|
||||
console.error('[Board] Failed to reject plan:', result.error);
|
||||
logger.error('Failed to reject plan:', result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error rejecting plan:', error);
|
||||
logger.error('Error rejecting plan:', error);
|
||||
} finally {
|
||||
setIsPlanApprovalLoading(false);
|
||||
setPendingPlanApproval(null);
|
||||
@@ -1407,7 +1410,7 @@ export function BoardView() {
|
||||
// Persist changes asynchronously and in parallel
|
||||
Promise.all(
|
||||
featuresToUpdate.map((feature) => persistFeatureUpdate(feature.id, { prUrl }))
|
||||
).catch(console.error);
|
||||
).catch((err) => logger.error('Error in handleMove:', err));
|
||||
}
|
||||
setWorktreeRefreshKey((k) => k + 1);
|
||||
setSelectedWorktreeForAction(null);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -64,6 +65,8 @@ import {
|
||||
} from '@automaker/dependency-resolver';
|
||||
import { isCursorModel, PROVIDER_PREFIXES } from '@automaker/types';
|
||||
|
||||
const logger = createLogger('AddFeatureDialog');
|
||||
|
||||
type FeatureData = {
|
||||
title: string;
|
||||
category: string;
|
||||
@@ -331,7 +334,7 @@ export function AddFeatureDialog({
|
||||
toast.error(result?.error || 'Failed to enhance description');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Enhancement failed:', error);
|
||||
logger.error('Enhancement failed:', error);
|
||||
toast.error('Failed to enhance description');
|
||||
} finally {
|
||||
setIsEnhancing(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -29,6 +30,8 @@ interface AgentOutputModalProps {
|
||||
|
||||
type ViewMode = 'parsed' | 'raw' | 'changes';
|
||||
|
||||
const logger = createLogger('AgentOutputModal');
|
||||
|
||||
export function AgentOutputModal({
|
||||
open,
|
||||
onClose,
|
||||
@@ -88,7 +91,7 @@ export function AgentOutputModal({
|
||||
setOutput('');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load output:', error);
|
||||
logger.error('Failed to load output:', error);
|
||||
setOutput('');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -105,11 +108,11 @@ export function AgentOutputModal({
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode) return;
|
||||
|
||||
console.log('[AgentOutputModal] Subscribing to events for featureId:', featureId);
|
||||
logger.info('Subscribing to events for featureId:', featureId);
|
||||
|
||||
const unsubscribe = api.autoMode.onEvent((event) => {
|
||||
console.log(
|
||||
'[AgentOutputModal] Received event:',
|
||||
logger.debug(
|
||||
'Received event:',
|
||||
event.type,
|
||||
'featureId:',
|
||||
'featureId' in event ? event.featureId : 'none',
|
||||
@@ -119,7 +122,7 @@ export function AgentOutputModal({
|
||||
|
||||
// Filter events for this specific feature only (skip events without featureId)
|
||||
if ('featureId' in event && event.featureId !== featureId) {
|
||||
console.log('[AgentOutputModal] Skipping event - featureId mismatch');
|
||||
logger.debug('Skipping event - featureId mismatch');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -22,6 +23,8 @@ interface WorktreeInfo {
|
||||
changedFilesCount?: number;
|
||||
}
|
||||
|
||||
const logger = createLogger('CreateBranchDialog');
|
||||
|
||||
interface CreateBranchDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
@@ -77,7 +80,7 @@ export function CreateBranchDialog({
|
||||
setError(result.error || 'Failed to create branch');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Create branch failed:', err);
|
||||
logger.error('Create branch failed:', err);
|
||||
setError('Failed to create branch');
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -57,6 +58,8 @@ import {
|
||||
import { DependencyTreeDialog } from './dependency-tree-dialog';
|
||||
import { isCursorModel, PROVIDER_PREFIXES } from '@automaker/types';
|
||||
|
||||
const logger = createLogger('EditFeatureDialog');
|
||||
|
||||
interface EditFeatureDialogProps {
|
||||
feature: Feature | null;
|
||||
onClose: () => void;
|
||||
@@ -248,7 +251,7 @@ export function EditFeatureDialog({
|
||||
toast.error(result?.error || 'Failed to enhance description');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Enhancement failed:', error);
|
||||
logger.error('Enhancement failed:', error);
|
||||
toast.error('Failed to enhance description');
|
||||
} finally {
|
||||
setIsEnhancing(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -34,6 +35,8 @@ import { useAppStore, Feature } from '@/store/app-store';
|
||||
import { toast } from 'sonner';
|
||||
import { LogViewer } from '@/components/ui/log-viewer';
|
||||
|
||||
const logger = createLogger('FeatureSuggestions');
|
||||
|
||||
interface FeatureSuggestionsDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
@@ -176,7 +179,7 @@ export function FeatureSuggestionsDialog({
|
||||
setIsGenerating(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to generate suggestions:', error);
|
||||
logger.error('Failed to generate suggestions:', error);
|
||||
toast.error('Failed to start generation');
|
||||
setIsGenerating(false);
|
||||
}
|
||||
@@ -194,7 +197,7 @@ export function FeatureSuggestionsDialog({
|
||||
setIsGenerating(false);
|
||||
toast.info('Generation stopped');
|
||||
} catch (error) {
|
||||
console.error('Failed to stop generation:', error);
|
||||
logger.error('Failed to stop generation:', error);
|
||||
}
|
||||
}, [setIsGenerating]);
|
||||
|
||||
@@ -280,7 +283,7 @@ export function FeatureSuggestionsDialog({
|
||||
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Failed to import features:', error);
|
||||
logger.error('Failed to import features:', error);
|
||||
toast.error('Failed to import features');
|
||||
} finally {
|
||||
setIsImporting(false);
|
||||
|
||||
@@ -13,6 +13,9 @@ import { toast } from 'sonner';
|
||||
import { useAutoMode } from '@/hooks/use-auto-mode';
|
||||
import { truncateDescription } from '@/lib/utils';
|
||||
import { getBlockingDependencies } from '@automaker/dependency-resolver';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('BoardActions');
|
||||
|
||||
interface UseBoardActionsProps {
|
||||
currentProject: { path: string; id: string } | null;
|
||||
@@ -112,8 +115,8 @@ export function useBoardActions({
|
||||
if (api?.worktree?.create) {
|
||||
const result = await api.worktree.create(currentProject.path, finalBranchName);
|
||||
if (result.success && result.worktree) {
|
||||
console.log(
|
||||
`[Board] Worktree for branch "${finalBranchName}" ${
|
||||
logger.info(
|
||||
`Worktree for branch "${finalBranchName}" ${
|
||||
result.worktree?.isNew ? 'created' : 'already exists'
|
||||
}`
|
||||
);
|
||||
@@ -125,8 +128,8 @@ export function useBoardActions({
|
||||
// Refresh worktree list in UI
|
||||
onWorktreeCreated?.();
|
||||
} else if (!result.success) {
|
||||
console.error(
|
||||
`[Board] Failed to create worktree for branch "${finalBranchName}":`,
|
||||
logger.error(
|
||||
`Failed to create worktree for branch "${finalBranchName}":`,
|
||||
result.error
|
||||
);
|
||||
toast.error('Failed to create worktree', {
|
||||
@@ -135,7 +138,7 @@ export function useBoardActions({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error creating worktree:', error);
|
||||
logger.error('Error creating worktree:', error);
|
||||
toast.error('Failed to create worktree', {
|
||||
description: error instanceof Error ? error.message : 'An error occurred',
|
||||
});
|
||||
@@ -180,7 +183,7 @@ export function useBoardActions({
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('[Board] Error generating title:', error);
|
||||
logger.error('Error generating title:', error);
|
||||
// Clear generating flag on error
|
||||
const titleUpdates = { titleGenerating: false };
|
||||
updateFeature(createdFeature.id, titleUpdates);
|
||||
@@ -229,16 +232,16 @@ export function useBoardActions({
|
||||
if (api?.worktree?.create) {
|
||||
const result = await api.worktree.create(currentProject.path, finalBranchName);
|
||||
if (result.success) {
|
||||
console.log(
|
||||
`[Board] Worktree for branch "${finalBranchName}" ${
|
||||
logger.info(
|
||||
`Worktree for branch "${finalBranchName}" ${
|
||||
result.worktree?.isNew ? 'created' : 'already exists'
|
||||
}`
|
||||
);
|
||||
// Refresh worktree list in UI
|
||||
onWorktreeCreated?.();
|
||||
} else {
|
||||
console.error(
|
||||
`[Board] Failed to create worktree for branch "${finalBranchName}":`,
|
||||
logger.error(
|
||||
`Failed to create worktree for branch "${finalBranchName}":`,
|
||||
result.error
|
||||
);
|
||||
toast.error('Failed to create worktree', {
|
||||
@@ -247,7 +250,7 @@ export function useBoardActions({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error creating worktree:', error);
|
||||
logger.error('Error creating worktree:', error);
|
||||
toast.error('Failed to create worktree', {
|
||||
description: error instanceof Error ? error.message : 'An error occurred',
|
||||
});
|
||||
@@ -292,7 +295,7 @@ export function useBoardActions({
|
||||
description: `Stopped and deleted: ${truncateDescription(feature.description)}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Board] Error stopping feature before delete:', error);
|
||||
logger.error('Error stopping feature before delete:', error);
|
||||
toast.error('Failed to stop agent', {
|
||||
description: 'The feature will still be deleted.',
|
||||
});
|
||||
@@ -305,13 +308,13 @@ export function useBoardActions({
|
||||
for (const imagePathObj of feature.imagePaths) {
|
||||
try {
|
||||
await api.deleteFile(imagePathObj.path);
|
||||
console.log(`[Board] Deleted image: ${imagePathObj.path}`);
|
||||
logger.info(`Deleted image: ${imagePathObj.path}`);
|
||||
} catch (error) {
|
||||
console.error(`[Board] Failed to delete image ${imagePathObj.path}:`, error);
|
||||
logger.error(`Failed to delete image ${imagePathObj.path}:`, error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[Board] Error deleting images for feature ${featureId}:`, error);
|
||||
logger.error(`Error deleting images for feature ${featureId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +331,7 @@ export function useBoardActions({
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode) {
|
||||
console.error('Auto mode API not available');
|
||||
logger.error('Auto mode API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -341,16 +344,13 @@ export function useBoardActions({
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
console.log(
|
||||
'[Board] Feature run started successfully, branch:',
|
||||
feature.branchName || 'default'
|
||||
);
|
||||
logger.info('Feature run started successfully, branch:', feature.branchName || 'default');
|
||||
} else {
|
||||
console.error('[Board] Failed to run feature:', result.error);
|
||||
logger.error('Failed to run feature:', result.error);
|
||||
await loadFeatures();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error running feature:', error);
|
||||
logger.error('Error running feature:', error);
|
||||
await loadFeatures();
|
||||
}
|
||||
},
|
||||
@@ -392,7 +392,7 @@ export function useBoardActions({
|
||||
updateFeature(feature.id, updates);
|
||||
// Must await to ensure feature status is persisted before starting agent
|
||||
await persistFeatureUpdate(feature.id, updates);
|
||||
console.log('[Board] Feature moved to in_progress, starting agent...');
|
||||
logger.info('Feature moved to in_progress, starting agent...');
|
||||
await handleRunFeature(feature);
|
||||
return true;
|
||||
},
|
||||
@@ -413,20 +413,20 @@ export function useBoardActions({
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode) {
|
||||
console.error('Auto mode API not available');
|
||||
logger.error('Auto mode API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await api.autoMode.verifyFeature(currentProject.path, feature.id);
|
||||
|
||||
if (result.success) {
|
||||
console.log('[Board] Feature verification started successfully');
|
||||
logger.info('Feature verification started successfully');
|
||||
} else {
|
||||
console.error('[Board] Failed to verify feature:', result.error);
|
||||
logger.error('Failed to verify feature:', result.error);
|
||||
await loadFeatures();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error verifying feature:', error);
|
||||
logger.error('Error verifying feature:', error);
|
||||
await loadFeatures();
|
||||
}
|
||||
},
|
||||
@@ -435,20 +435,20 @@ export function useBoardActions({
|
||||
|
||||
const handleResumeFeature = useCallback(
|
||||
async (feature: Feature) => {
|
||||
console.log('[Board] handleResumeFeature called for feature:', feature.id);
|
||||
logger.info('handleResumeFeature called for feature:', feature.id);
|
||||
if (!currentProject) {
|
||||
console.error('[Board] No current project');
|
||||
logger.error('No current project');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode) {
|
||||
console.error('[Board] Auto mode API not available');
|
||||
logger.error('Auto mode API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Board] Calling resumeFeature API...', {
|
||||
logger.info('Calling resumeFeature API...', {
|
||||
projectPath: currentProject.path,
|
||||
featureId: feature.id,
|
||||
useWorktrees,
|
||||
@@ -460,16 +460,16 @@ export function useBoardActions({
|
||||
useWorktrees
|
||||
);
|
||||
|
||||
console.log('[Board] resumeFeature result:', result);
|
||||
logger.info('resumeFeature result:', result);
|
||||
|
||||
if (result.success) {
|
||||
console.log('[Board] Feature resume started successfully');
|
||||
logger.info('Feature resume started successfully');
|
||||
} else {
|
||||
console.error('[Board] Failed to resume feature:', result.error);
|
||||
logger.error('Failed to resume feature:', result.error);
|
||||
await loadFeatures();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error resuming feature:', error);
|
||||
logger.error('Error resuming feature:', error);
|
||||
await loadFeatures();
|
||||
}
|
||||
},
|
||||
@@ -523,7 +523,7 @@ export function useBoardActions({
|
||||
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode?.followUpFeature) {
|
||||
console.error('Follow-up feature API not available');
|
||||
logger.error('Follow-up feature API not available');
|
||||
toast.error('Follow-up not available', {
|
||||
description: 'This feature is not available in the current version.',
|
||||
});
|
||||
@@ -559,7 +559,7 @@ export function useBoardActions({
|
||||
// No worktreePath - server derives from feature.branchName
|
||||
)
|
||||
.catch((error) => {
|
||||
console.error('[Board] Error sending follow-up:', error);
|
||||
logger.error('Error sending follow-up:', error);
|
||||
toast.error('Failed to send follow-up', {
|
||||
description: error instanceof Error ? error.message : 'An error occurred',
|
||||
});
|
||||
@@ -587,7 +587,7 @@ export function useBoardActions({
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.autoMode?.commitFeature) {
|
||||
console.error('Commit feature API not available');
|
||||
logger.error('Commit feature API not available');
|
||||
toast.error('Commit not available', {
|
||||
description: 'This feature is not available in the current version.',
|
||||
});
|
||||
@@ -610,14 +610,14 @@ export function useBoardActions({
|
||||
// Refresh worktree selector to update commit counts
|
||||
onWorktreeCreated?.();
|
||||
} else {
|
||||
console.error('[Board] Failed to commit feature:', result.error);
|
||||
logger.error('Failed to commit feature:', result.error);
|
||||
toast.error('Failed to commit feature', {
|
||||
description: result.error || 'An error occurred',
|
||||
});
|
||||
await loadFeatures();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error committing feature:', error);
|
||||
logger.error('Error committing feature:', error);
|
||||
toast.error('Failed to commit feature', {
|
||||
description: error instanceof Error ? error.message : 'An error occurred',
|
||||
});
|
||||
@@ -634,7 +634,7 @@ export function useBoardActions({
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.worktree?.mergeFeature) {
|
||||
console.error('Worktree API not available');
|
||||
logger.error('Worktree API not available');
|
||||
toast.error('Merge not available', {
|
||||
description: 'This feature is not available in the current version.',
|
||||
});
|
||||
@@ -651,13 +651,13 @@ export function useBoardActions({
|
||||
)}`,
|
||||
});
|
||||
} else {
|
||||
console.error('[Board] Failed to merge feature:', result.error);
|
||||
logger.error('Failed to merge feature:', result.error);
|
||||
toast.error('Failed to merge feature', {
|
||||
description: result.error || 'An error occurred',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Error merging feature:', error);
|
||||
logger.error('Error merging feature:', error);
|
||||
toast.error('Failed to merge feature', {
|
||||
description: error instanceof Error ? error.message : 'An error occurred',
|
||||
});
|
||||
@@ -747,7 +747,7 @@ export function useBoardActions({
|
||||
: `Stopped working on: ${truncateDescription(feature.description)}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Board] Error stopping feature:', error);
|
||||
logger.error('Error stopping feature:', error);
|
||||
toast.error('Failed to stop agent', {
|
||||
description: error instanceof Error ? error.message : 'An error occurred',
|
||||
});
|
||||
@@ -857,7 +857,7 @@ export function useBoardActions({
|
||||
try {
|
||||
await autoMode.stopFeature(feature.id);
|
||||
} catch (error) {
|
||||
console.error('[Board] Error stopping feature before archive:', error);
|
||||
logger.error('Error stopping feature before archive:', error);
|
||||
}
|
||||
}
|
||||
// Archive the feature by setting status to completed
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
|
||||
import { Feature } from '@/store/app-store';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { toast } from 'sonner';
|
||||
import { COLUMNS, ColumnId } from '../constants';
|
||||
|
||||
const logger = createLogger('BoardDragDrop');
|
||||
|
||||
interface UseBoardDragDropProps {
|
||||
features: Feature[];
|
||||
currentProject: { path: string; id: string } | null;
|
||||
@@ -63,7 +66,7 @@ export function useBoardDragDrop({
|
||||
if (draggedFeature.status === 'in_progress') {
|
||||
// Only allow dragging in_progress if it's not currently running
|
||||
if (isRunningTask) {
|
||||
console.log('[Board] Cannot drag feature - currently running');
|
||||
logger.debug('Cannot drag feature - currently running');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('BoardEffects');
|
||||
|
||||
interface UseBoardEffectsProps {
|
||||
currentProject: { path: string; id: string } | null;
|
||||
@@ -70,12 +73,7 @@ export function useBoardEffects({
|
||||
if (!api.specRegeneration) return;
|
||||
|
||||
const unsubscribe = api.specRegeneration.onEvent((event) => {
|
||||
console.log(
|
||||
'[BoardView] Spec regeneration event:',
|
||||
event.type,
|
||||
'for project:',
|
||||
event.projectPath
|
||||
);
|
||||
logger.info('Spec regeneration event:', event.type, 'for project:', event.projectPath);
|
||||
|
||||
if (event.projectPath !== specCreatingForProject) {
|
||||
return;
|
||||
@@ -108,7 +106,7 @@ export function useBoardEffects({
|
||||
const { clearRunningTasks, addRunningTask } = useAppStore.getState();
|
||||
|
||||
if (status.runningFeatures) {
|
||||
console.log('[Board] Syncing running tasks from backend:', status.runningFeatures);
|
||||
logger.info('Syncing running tasks from backend:', status.runningFeatures);
|
||||
|
||||
clearRunningTasks(projectId);
|
||||
|
||||
@@ -118,7 +116,7 @@ export function useBoardEffects({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Board] Failed to sync running tasks:', error);
|
||||
logger.error('Failed to sync running tasks:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { useAppStore, Feature } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { toast } from 'sonner';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('BoardFeatures');
|
||||
|
||||
interface UseBoardFeaturesProps {
|
||||
currentProject: { path: string; id: string } | null;
|
||||
@@ -32,7 +35,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
// If project switched, mark it but don't clear features yet
|
||||
// We'll clear after successful API load to prevent data loss
|
||||
if (isProjectSwitch) {
|
||||
console.log(`[BoardView] Project switch detected: ${previousPath} -> ${currentPath}`);
|
||||
logger.info(`Project switch detected: ${previousPath} -> ${currentPath}`);
|
||||
isSwitchingProjectRef.current = true;
|
||||
isInitialLoadRef.current = true;
|
||||
}
|
||||
@@ -48,7 +51,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.features) {
|
||||
console.error('[BoardView] Features API not available');
|
||||
logger.error('Features API not available');
|
||||
// Keep cached features if API is unavailable
|
||||
return;
|
||||
}
|
||||
@@ -73,7 +76,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
setPersistedCategories([]);
|
||||
}
|
||||
} else if (!result.success && result.error) {
|
||||
console.error('[BoardView] API returned error:', result.error);
|
||||
logger.error('API returned error:', result.error);
|
||||
// If it's a new project or the error indicates no features found,
|
||||
// that's expected - start with empty array
|
||||
if (isProjectSwitch) {
|
||||
@@ -83,7 +86,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
// Otherwise keep cached features
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load features:', error);
|
||||
logger.error('Failed to load features:', error);
|
||||
// On error, keep existing cached features for the current project
|
||||
// Only clear on project switch if we have no features from server
|
||||
if (isProjectSwitch && cachedFeatures.length === 0) {
|
||||
@@ -115,7 +118,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
setPersistedCategories([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load categories:', error);
|
||||
logger.error('Failed to load categories:', error);
|
||||
// If file doesn't exist, ensure categories are cleared
|
||||
setPersistedCategories([]);
|
||||
}
|
||||
@@ -147,7 +150,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
setPersistedCategories(categories);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save category:', error);
|
||||
logger.error('Failed to save category:', error);
|
||||
}
|
||||
},
|
||||
[currentProject, persistedCategories]
|
||||
@@ -165,7 +168,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
currentProject &&
|
||||
event.projectPath === currentProject.path
|
||||
) {
|
||||
console.log('[BoardView] Spec regeneration complete, refreshing features');
|
||||
logger.info('Spec regeneration complete, refreshing features');
|
||||
loadFeatures();
|
||||
}
|
||||
});
|
||||
@@ -190,27 +193,27 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
|
||||
if (event.type === 'auto_mode_feature_complete') {
|
||||
// Reload features when a feature is completed
|
||||
console.log('[Board] Feature completed, reloading features...');
|
||||
logger.info('Feature completed, reloading features...');
|
||||
loadFeatures();
|
||||
// Play ding sound when feature is done (unless muted)
|
||||
const { muteDoneSound } = useAppStore.getState();
|
||||
if (!muteDoneSound) {
|
||||
const audio = new Audio('/sounds/ding.mp3');
|
||||
audio.play().catch((err) => console.warn('Could not play ding sound:', err));
|
||||
audio.play().catch((err) => logger.warn('Could not play ding sound:', err));
|
||||
}
|
||||
} else if (event.type === 'plan_approval_required') {
|
||||
// Reload features when plan is generated and requires approval
|
||||
// This ensures the feature card shows the "Approve Plan" button
|
||||
console.log('[Board] Plan approval required, reloading features...');
|
||||
logger.info('Plan approval required, reloading features...');
|
||||
loadFeatures();
|
||||
} else if (event.type === 'pipeline_step_started') {
|
||||
// Pipeline steps update the feature status to `pipeline_*` before the step runs.
|
||||
// Reload so the card moves into the correct pipeline column immediately.
|
||||
console.log('[Board] Pipeline step started, reloading features...');
|
||||
logger.info('Pipeline step started, reloading features...');
|
||||
loadFeatures();
|
||||
} else if (event.type === 'auto_mode_error') {
|
||||
// Reload features when an error occurs (feature moved to waiting_approval)
|
||||
console.log('[Board] Feature error, reloading features...', event.error);
|
||||
logger.info('Feature error, reloading features...', event.error);
|
||||
|
||||
// Remove from running tasks so it moves to the correct column
|
||||
if (event.featureId) {
|
||||
|
||||
@@ -2,6 +2,9 @@ import { useCallback } from 'react';
|
||||
import { Feature } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('BoardPersistence');
|
||||
|
||||
interface UseBoardPersistenceProps {
|
||||
currentProject: { path: string; id: string } | null;
|
||||
@@ -18,7 +21,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.features) {
|
||||
console.error('[BoardView] Features API not available');
|
||||
logger.error('Features API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,7 +30,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
||||
updateFeature(result.feature.id, result.feature);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to persist feature update:', error);
|
||||
logger.error('Failed to persist feature update:', error);
|
||||
}
|
||||
},
|
||||
[currentProject, updateFeature]
|
||||
@@ -41,7 +44,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.features) {
|
||||
console.error('[BoardView] Features API not available');
|
||||
logger.error('Features API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,7 +53,7 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
||||
updateFeature(result.feature.id, result.feature);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to persist feature creation:', error);
|
||||
logger.error('Failed to persist feature creation:', error);
|
||||
}
|
||||
},
|
||||
[currentProject, updateFeature]
|
||||
@@ -64,13 +67,13 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.features) {
|
||||
console.error('[BoardView] Features API not available');
|
||||
logger.error('Features API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
await api.features.delete(currentProject.path, featureId);
|
||||
} catch (error) {
|
||||
console.error('Failed to persist feature deletion:', error);
|
||||
logger.error('Failed to persist feature deletion:', error);
|
||||
}
|
||||
},
|
||||
[currentProject]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import type { BranchInfo, GitRepoStatus } from '../types';
|
||||
|
||||
const logger = createLogger('Branches');
|
||||
|
||||
export function useBranches() {
|
||||
const [branches, setBranches] = useState<BranchInfo[]>([]);
|
||||
const [aheadCount, setAheadCount] = useState(0);
|
||||
@@ -26,7 +29,7 @@ export function useBranches() {
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.worktree?.listBranches) {
|
||||
console.warn('List branches API not available');
|
||||
logger.warn('List branches API not available');
|
||||
return;
|
||||
}
|
||||
const result = await api.worktree.listBranches(worktreePath);
|
||||
@@ -45,11 +48,11 @@ export function useBranches() {
|
||||
setGitRepoStatus({ isGitRepo: true, hasCommits: false });
|
||||
} else if (!result.success) {
|
||||
// Other errors - log them
|
||||
console.warn('Failed to fetch branches:', result.error);
|
||||
logger.warn('Failed to fetch branches:', result.error);
|
||||
resetBranchState();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch branches:', error);
|
||||
logger.error('Failed to fetch branches:', error);
|
||||
resetBranchState();
|
||||
// Reset git status to unknown state on network/API errors
|
||||
setGitRepoStatus({ isGitRepo: true, hasCommits: true });
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('DefaultEditor');
|
||||
|
||||
export function useDefaultEditor() {
|
||||
const [defaultEditorName, setDefaultEditorName] = useState<string>('Editor');
|
||||
|
||||
@@ -15,7 +18,7 @@ export function useDefaultEditor() {
|
||||
setDefaultEditorName(result.result.editorName);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch default editor:', error);
|
||||
logger.error('Failed to fetch default editor:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { normalizePath } from '@/lib/utils';
|
||||
import { toast } from 'sonner';
|
||||
import type { DevServerInfo, WorktreeInfo } from '../types';
|
||||
|
||||
const logger = createLogger('DevServers');
|
||||
|
||||
interface UseDevServersOptions {
|
||||
projectPath: string;
|
||||
}
|
||||
@@ -27,7 +30,7 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
||||
setRunningDevServers(serversMap);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch dev servers:', error);
|
||||
logger.error('Failed to fetch dev servers:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -73,7 +76,7 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
||||
toast.error(result.error || 'Failed to start dev server');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Start dev server failed:', error);
|
||||
logger.error('Start dev server failed:', error);
|
||||
toast.error('Failed to start dev server');
|
||||
} finally {
|
||||
setIsStartingDevServer(false);
|
||||
@@ -105,7 +108,7 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
|
||||
toast.error(result.error || 'Failed to stop dev server');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Stop dev server failed:', error);
|
||||
logger.error('Stop dev server failed:', error);
|
||||
toast.error('Failed to stop dev server');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { toast } from 'sonner';
|
||||
import type { WorktreeInfo } from '../types';
|
||||
|
||||
const logger = createLogger('WorktreeActions');
|
||||
|
||||
// Error codes that need special user-friendly handling
|
||||
const GIT_STATUS_ERROR_CODES = ['NOT_GIT_REPO', 'NO_COMMITS'] as const;
|
||||
type GitStatusErrorCode = (typeof GIT_STATUS_ERROR_CODES)[number];
|
||||
@@ -56,7 +59,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
||||
toast.error(result.error || 'Failed to switch branch');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Switch branch failed:', error);
|
||||
logger.error('Switch branch failed:', error);
|
||||
toast.error('Failed to switch branch');
|
||||
} finally {
|
||||
setIsSwitching(false);
|
||||
@@ -84,7 +87,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
||||
toast.error(result.error || 'Failed to pull latest changes');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Pull failed:', error);
|
||||
logger.error('Pull failed:', error);
|
||||
toast.error('Failed to pull latest changes');
|
||||
} finally {
|
||||
setIsPulling(false);
|
||||
@@ -113,7 +116,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
||||
toast.error(result.error || 'Failed to push changes');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Push failed:', error);
|
||||
logger.error('Push failed:', error);
|
||||
toast.error('Failed to push changes');
|
||||
} finally {
|
||||
setIsPushing(false);
|
||||
@@ -126,7 +129,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.worktree?.openInEditor) {
|
||||
console.warn('Open in editor API not available');
|
||||
logger.warn('Open in editor API not available');
|
||||
return;
|
||||
}
|
||||
const result = await api.worktree.openInEditor(worktree.path);
|
||||
@@ -136,7 +139,7 @@ export function useWorktreeActions({ fetchWorktrees, fetchBranches }: UseWorktre
|
||||
toast.error(result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Open in editor failed:', error);
|
||||
logger.error('Open in editor failed:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { pathsEqual } from '@/lib/utils';
|
||||
import type { WorktreeInfo } from '../types';
|
||||
|
||||
const logger = createLogger('Worktrees');
|
||||
|
||||
interface UseWorktreesOptions {
|
||||
projectPath: string;
|
||||
refreshTrigger?: number;
|
||||
@@ -33,7 +36,7 @@ export function useWorktrees({
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.worktree?.listAll) {
|
||||
console.warn('Worktree API not available');
|
||||
logger.warn('Worktree API not available');
|
||||
return;
|
||||
}
|
||||
const result = await api.worktree.listAll(projectPath, true);
|
||||
@@ -44,7 +47,7 @@ export function useWorktrees({
|
||||
// Return removed worktrees so they can be handled by the caller
|
||||
return result.removedWorktrees;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch worktrees:', error);
|
||||
logger.error('Failed to fetch worktrees:', error);
|
||||
return undefined;
|
||||
} finally {
|
||||
if (!silent) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
@@ -6,6 +7,8 @@ import { Button } from '@/components/ui/button';
|
||||
import { File, Folder, FolderOpen, ChevronRight, ChevronDown, RefreshCw, Code } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('CodeView');
|
||||
|
||||
interface FileTreeNode {
|
||||
name: string;
|
||||
path: string;
|
||||
@@ -60,7 +63,7 @@ export function CodeView() {
|
||||
setFileTree(entries);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load file tree:', error);
|
||||
logger.error('Failed to load file tree:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -91,7 +94,7 @@ export function CodeView() {
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load subdirectory:', error);
|
||||
logger.error('Failed to load subdirectory:', error);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
@@ -107,7 +110,7 @@ export function CodeView() {
|
||||
setSelectedFile(path);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load file:', error);
|
||||
logger.error('Failed to load file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState, useCallback, useMemo, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
@@ -38,6 +39,8 @@ import {
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('ContextView');
|
||||
import { sanitizeFilename } from '@/lib/image-utils';
|
||||
import { Markdown } from '../ui/markdown';
|
||||
import {
|
||||
@@ -160,7 +163,7 @@ export function ContextView() {
|
||||
const metadataPath = `${contextPath}/context-metadata.json`;
|
||||
await api.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
||||
} catch (error) {
|
||||
console.error('Failed to save metadata:', error);
|
||||
logger.error('Failed to save metadata:', error);
|
||||
}
|
||||
},
|
||||
[getContextPath]
|
||||
@@ -202,7 +205,7 @@ export function ContextView() {
|
||||
setContextFiles(files);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load context files:', error);
|
||||
logger.error('Failed to load context files:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -223,7 +226,7 @@ export function ContextView() {
|
||||
setHasChanges(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load file content:', error);
|
||||
logger.error('Failed to load file content:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -247,7 +250,7 @@ export function ContextView() {
|
||||
setSelectedFile({ ...selectedFile, content: editedContent });
|
||||
setHasChanges(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to save file:', error);
|
||||
logger.error('Failed to save file:', error);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -279,7 +282,7 @@ export function ContextView() {
|
||||
result.error || `Automaker couldn't generate a description for “${fileName}”.`;
|
||||
toast.error('Failed to generate description', { description: message });
|
||||
} catch (error) {
|
||||
console.error('Failed to generate description:', error);
|
||||
logger.error('Failed to generate description:', error);
|
||||
const message =
|
||||
error instanceof Error
|
||||
? error.message
|
||||
@@ -315,7 +318,7 @@ export function ContextView() {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to generate description:', error);
|
||||
logger.error('Failed to generate description:', error);
|
||||
} finally {
|
||||
// Remove from generating set
|
||||
setGeneratingDescriptions((prev) => {
|
||||
@@ -401,7 +404,7 @@ export function ContextView() {
|
||||
// For images, use the path in the images directory
|
||||
generateDescriptionAsync(imagePathForDescription || filePath, fileName, isImage);
|
||||
} catch (error) {
|
||||
console.error('Failed to upload file:', error);
|
||||
logger.error('Failed to upload file:', error);
|
||||
toast.error('Failed to upload file', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -492,7 +495,7 @@ export function ContextView() {
|
||||
setNewMarkdownDescription('');
|
||||
setNewMarkdownContent('');
|
||||
} catch (error) {
|
||||
console.error('Failed to create markdown:', error);
|
||||
logger.error('Failed to create markdown:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -515,7 +518,7 @@ export function ContextView() {
|
||||
setHasChanges(false);
|
||||
await loadContextFiles();
|
||||
} catch (error) {
|
||||
console.error('Failed to delete file:', error);
|
||||
logger.error('Failed to delete file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -537,14 +540,14 @@ export function ContextView() {
|
||||
// Check if file with new name already exists
|
||||
const exists = await api.exists(newPath);
|
||||
if (exists) {
|
||||
console.error('A file with this name already exists');
|
||||
logger.error('A file with this name already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
// Read current file content
|
||||
const result = await api.readFile(selectedFile.path);
|
||||
if (!result.success || result.content === undefined) {
|
||||
console.error('Failed to read file for rename');
|
||||
logger.error('Failed to read file for rename');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -578,7 +581,7 @@ export function ContextView() {
|
||||
};
|
||||
setSelectedFile(renamedFile);
|
||||
} catch (error) {
|
||||
console.error('Failed to rename file:', error);
|
||||
logger.error('Failed to rename file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -603,7 +606,7 @@ export function ContextView() {
|
||||
setEditDescriptionValue('');
|
||||
setEditDescriptionFileName('');
|
||||
} catch (error) {
|
||||
console.error('Failed to save description:', error);
|
||||
logger.error('Failed to save description:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -634,7 +637,7 @@ export function ContextView() {
|
||||
|
||||
await loadContextFiles();
|
||||
} catch (error) {
|
||||
console.error('Failed to delete file:', error);
|
||||
logger.error('Failed to delete file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { CircleDot, RefreshCw } from 'lucide-react';
|
||||
import { getElectronAPI, GitHubIssue, IssueValidationResult } from '@/lib/electron';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
@@ -14,6 +15,8 @@ import { formatDate, getFeaturePriority } from './github-issues-view/utils';
|
||||
import { useModelOverride } from '@/components/shared';
|
||||
import type { ValidateIssueOptions } from './github-issues-view/types';
|
||||
|
||||
const logger = createLogger('GitHubIssuesView');
|
||||
|
||||
export function GitHubIssuesView() {
|
||||
const [selectedIssue, setSelectedIssue] = useState<GitHubIssue | null>(null);
|
||||
const [validationResult, setValidationResult] = useState<IssueValidationResult | null>(null);
|
||||
@@ -118,7 +121,7 @@ export function GitHubIssuesView() {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[GitHubIssuesView] Convert to task error:', err);
|
||||
logger.error('Convert to task error:', err);
|
||||
toast.error(err instanceof Error ? err.message : 'Failed to create task');
|
||||
}
|
||||
},
|
||||
@@ -247,7 +250,7 @@ export function GitHubIssuesView() {
|
||||
confirmText="Re-validate"
|
||||
onConfirm={() => {
|
||||
if (selectedIssue && pendingRevalidateOptions) {
|
||||
console.log('[GitHubIssuesView] Revalidating with options:', {
|
||||
logger.info('Revalidating with options:', {
|
||||
commentsCount: pendingRevalidateOptions.comments?.length ?? 0,
|
||||
linkedPRsCount: pendingRevalidateOptions.linkedPRs?.length ?? 0,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI, GitHubIssue } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('GitHubIssues');
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
export function useGithubIssues() {
|
||||
@@ -38,7 +41,7 @@ export function useGithubIssues() {
|
||||
}
|
||||
} catch (err) {
|
||||
if (isMountedRef.current) {
|
||||
console.error('[GitHubIssuesView] Error fetching issues:', err);
|
||||
logger.error('Error fetching issues:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch issues');
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI, GitHubComment } from '@/lib/electron';
|
||||
|
||||
const logger = createLogger('IssueComments');
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
interface UseIssueCommentsResult {
|
||||
@@ -69,7 +72,7 @@ export function useIssueComments(issueNumber: number | null): UseIssueCommentsRe
|
||||
}
|
||||
} catch (err) {
|
||||
if (isMountedRef.current) {
|
||||
console.error('[useIssueComments] Error fetching comments:', err);
|
||||
logger.error('Error fetching comments:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch comments');
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
getElectronAPI,
|
||||
GitHubIssue,
|
||||
@@ -12,6 +13,8 @@ import { useAppStore } from '@/store/app-store';
|
||||
import { toast } from 'sonner';
|
||||
import { isValidationStale } from '../utils';
|
||||
|
||||
const logger = createLogger('IssueValidation');
|
||||
|
||||
/**
|
||||
* Extract model string from PhaseModelEntry or string (handles both formats)
|
||||
*/
|
||||
@@ -78,7 +81,7 @@ export function useIssueValidation({
|
||||
}
|
||||
} catch (err) {
|
||||
if (isMounted) {
|
||||
console.error('[GitHubIssuesView] Failed to load cached validations:', err);
|
||||
logger.error('Failed to load cached validations:', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -107,7 +110,7 @@ export function useIssueValidation({
|
||||
}
|
||||
} catch (err) {
|
||||
if (isMounted) {
|
||||
console.error('[GitHubIssuesView] Failed to load running validations:', err);
|
||||
logger.error('Failed to load running validations:', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -283,7 +286,7 @@ export function useIssueValidation({
|
||||
// On success, the result will come through the event stream
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[GitHubIssuesView] Validation error:', err);
|
||||
logger.error('Validation error:', err);
|
||||
toast.error(err instanceof Error ? err.message : 'Failed to validate issue');
|
||||
}
|
||||
},
|
||||
@@ -325,7 +328,7 @@ export function useIssueValidation({
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[GitHubIssuesView] Failed to mark validation as viewed:', err);
|
||||
logger.error('Failed to mark validation as viewed:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { GitPullRequest, Loader2, RefreshCw, ExternalLink, GitMerge, X } from 'lucide-react';
|
||||
import { getElectronAPI, GitHubPR } from '@/lib/electron';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
@@ -6,6 +7,8 @@ import { Button } from '@/components/ui/button';
|
||||
import { Markdown } from '@/components/ui/markdown';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('GitHubPRsView');
|
||||
|
||||
export function GitHubPRsView() {
|
||||
const [openPRs, setOpenPRs] = useState<GitHubPR[]>([]);
|
||||
const [mergedPRs, setMergedPRs] = useState<GitHubPR[]>([]);
|
||||
@@ -35,7 +38,7 @@ export function GitHubPRsView() {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[GitHubPRsView] Error fetching PRs:', err);
|
||||
logger.error('Error fetching PRs:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch pull requests');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore, Feature } from '@/store/app-store';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -12,6 +13,8 @@ import { toast } from 'sonner';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config';
|
||||
|
||||
const logger = createLogger('InterviewView');
|
||||
|
||||
interface InterviewMessage {
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
@@ -97,7 +100,7 @@ export function InterviewView() {
|
||||
setProjectPath(defaultDir);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load default workspace directory:', error);
|
||||
logger.error('Failed to load default workspace directory:', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -367,7 +370,7 @@ export function InterviewView() {
|
||||
addProject(project);
|
||||
setCurrentProject(project);
|
||||
} catch (error) {
|
||||
console.error('Failed to create project:', error);
|
||||
logger.error('Failed to create project:', error);
|
||||
setIsGenerating(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { Bot, Folder, Loader2, RefreshCw, Square, Activity, FileText } from 'lucide-react';
|
||||
import { getElectronAPI, RunningAgent } from '@/lib/electron';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
@@ -7,6 +8,8 @@ import { cn } from '@/lib/utils';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { AgentOutputModal } from './board-view/dialogs/agent-output-modal';
|
||||
|
||||
const logger = createLogger('RunningAgentsView');
|
||||
|
||||
export function RunningAgentsView() {
|
||||
const [runningAgents, setRunningAgents] = useState<RunningAgent[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -25,7 +28,7 @@ export function RunningAgentsView() {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[RunningAgentsView] Error fetching running agents:', error);
|
||||
logger.error('Error fetching running agents:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
@@ -78,7 +81,7 @@ export function RunningAgentsView() {
|
||||
fetchRunningAgents();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[RunningAgentsView] Error stopping agent:', error);
|
||||
logger.error('Error stopping agent:', error);
|
||||
}
|
||||
},
|
||||
[fetchRunningAgents]
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('ApiKeyManagement');
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import type { ProviderConfigParams } from '@/config/api-providers';
|
||||
|
||||
@@ -60,7 +63,7 @@ export function useApiKeyManagement() {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check API key status:', error);
|
||||
logger.error('Failed to check API key status:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
|
||||
const logger = createLogger('CliStatus');
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
interface CliStatusResult {
|
||||
@@ -40,7 +43,7 @@ export function useCliStatus() {
|
||||
const status = await api.checkClaudeCli();
|
||||
setClaudeCliStatus(status);
|
||||
} catch (error) {
|
||||
console.error('Failed to check Claude CLI status:', error);
|
||||
logger.error('Failed to check Claude CLI status:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +87,7 @@ export function useCliStatus() {
|
||||
setClaudeAuthStatus(authStatus);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check Claude auth status:', error);
|
||||
logger.error('Failed to check Claude auth status:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -102,7 +105,7 @@ export function useCliStatus() {
|
||||
setClaudeCliStatus(status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh Claude CLI status:', error);
|
||||
logger.error('Failed to refresh Claude CLI status:', error);
|
||||
} finally {
|
||||
setIsCheckingClaudeCli(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const logger = createLogger('CursorPermissions');
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import type { CursorPermissionProfile } from '@automaker/types';
|
||||
|
||||
@@ -41,7 +44,7 @@ export function useCursorPermissions(projectPath?: string) {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load Cursor permissions:', error);
|
||||
logger.error('Failed to load Cursor permissions:', error);
|
||||
} finally {
|
||||
setIsLoadingPermissions(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const logger = createLogger('CursorStatus');
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
|
||||
@@ -48,7 +51,7 @@ export function useCursorStatus() {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load Cursor settings:', error);
|
||||
logger.error('Failed to load Cursor settings:', error);
|
||||
toast.error('Failed to load Cursor settings');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('MCPServers');
|
||||
import { toast } from 'sonner';
|
||||
import type { MCPServerConfig } from '@automaker/types';
|
||||
import { syncSettingsToServer, loadMCPServersFromServer } from '@/hooks/use-settings-migration';
|
||||
@@ -72,7 +75,7 @@ export function useMCPServers() {
|
||||
// Auto-load MCP servers from settings file on mount
|
||||
useEffect(() => {
|
||||
loadMCPServersFromServer().catch((error) => {
|
||||
console.error('Failed to load MCP servers on mount:', error);
|
||||
logger.error('Failed to load MCP servers on mount:', error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -431,7 +434,7 @@ export function useMCPServers() {
|
||||
|
||||
if (serverData.type === 'stdio') {
|
||||
if (!serverConfig.command) {
|
||||
console.warn(`Skipping ${name}: no command specified`);
|
||||
logger.warn(`Skipping ${name}: no command specified`);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -458,7 +461,7 @@ export function useMCPServers() {
|
||||
}
|
||||
} else {
|
||||
if (!serverConfig.url) {
|
||||
console.warn(`Skipping ${name}: no url specified`);
|
||||
logger.warn(`Skipping ${name}: no url specified`);
|
||||
return null;
|
||||
}
|
||||
serverData.url = serverConfig.url as string;
|
||||
@@ -491,7 +494,7 @@ export function useMCPServers() {
|
||||
const name = config.name as string;
|
||||
|
||||
if (!name) {
|
||||
console.warn('Skipping server: no name specified');
|
||||
logger.warn('Skipping server: no name specified');
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
import { StepIndicator } from './setup-view/components';
|
||||
import {
|
||||
@@ -10,6 +11,8 @@ import {
|
||||
} from './setup-view/steps';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
|
||||
const logger = createLogger('SetupView');
|
||||
|
||||
// Main Setup View
|
||||
export function SetupView() {
|
||||
const { currentStep, setCurrentStep, completeSetup, setSkipClaudeSetup } = useSetupStore();
|
||||
@@ -28,33 +31,33 @@ export function SetupView() {
|
||||
const currentIndex = steps.indexOf(getStepName());
|
||||
|
||||
const handleNext = (from: string) => {
|
||||
console.log('[Setup Flow] handleNext called from:', from, 'currentStep:', currentStep);
|
||||
logger.debug('[Setup Flow] handleNext called from:', from, 'currentStep:', currentStep);
|
||||
switch (from) {
|
||||
case 'welcome':
|
||||
console.log('[Setup Flow] Moving to theme step');
|
||||
logger.debug('[Setup Flow] Moving to theme step');
|
||||
setCurrentStep('theme');
|
||||
break;
|
||||
case 'theme':
|
||||
console.log('[Setup Flow] Moving to claude_detect step');
|
||||
logger.debug('[Setup Flow] Moving to claude_detect step');
|
||||
setCurrentStep('claude_detect');
|
||||
break;
|
||||
case 'claude':
|
||||
console.log('[Setup Flow] Moving to cursor step');
|
||||
logger.debug('[Setup Flow] Moving to cursor step');
|
||||
setCurrentStep('cursor');
|
||||
break;
|
||||
case 'cursor':
|
||||
console.log('[Setup Flow] Moving to github step');
|
||||
logger.debug('[Setup Flow] Moving to github step');
|
||||
setCurrentStep('github');
|
||||
break;
|
||||
case 'github':
|
||||
console.log('[Setup Flow] Moving to complete step');
|
||||
logger.debug('[Setup Flow] Moving to complete step');
|
||||
setCurrentStep('complete');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleBack = (from: string) => {
|
||||
console.log('[Setup Flow] handleBack called from:', from);
|
||||
logger.debug('[Setup Flow] handleBack called from:', from);
|
||||
switch (from) {
|
||||
case 'theme':
|
||||
setCurrentStep('welcome');
|
||||
@@ -72,25 +75,25 @@ export function SetupView() {
|
||||
};
|
||||
|
||||
const handleSkipClaude = () => {
|
||||
console.log('[Setup Flow] Skipping Claude setup');
|
||||
logger.debug('[Setup Flow] Skipping Claude setup');
|
||||
setSkipClaudeSetup(true);
|
||||
setCurrentStep('cursor');
|
||||
};
|
||||
|
||||
const handleSkipCursor = () => {
|
||||
console.log('[Setup Flow] Skipping Cursor setup');
|
||||
logger.debug('[Setup Flow] Skipping Cursor setup');
|
||||
setCurrentStep('github');
|
||||
};
|
||||
|
||||
const handleSkipGithub = () => {
|
||||
console.log('[Setup Flow] Skipping GitHub setup');
|
||||
logger.debug('[Setup Flow] Skipping GitHub setup');
|
||||
setCurrentStep('complete');
|
||||
};
|
||||
|
||||
const handleFinish = () => {
|
||||
console.log('[Setup Flow] handleFinish called - completing setup');
|
||||
logger.debug('[Setup Flow] handleFinish called - completing setup');
|
||||
completeSetup();
|
||||
console.log('[Setup Flow] Setup completed, redirecting to welcome view');
|
||||
logger.debug('[Setup Flow] Setup completed, redirecting to welcome view');
|
||||
navigate({ to: '/' });
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('CliInstallation');
|
||||
|
||||
interface UseCliInstallationOptions {
|
||||
cliType: 'claude';
|
||||
@@ -82,7 +85,7 @@ export function useCliInstallation({
|
||||
toast.error('Installation failed', { description: result.error });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to install ${cliType}:`, error);
|
||||
logger.error(`Failed to install ${cliType}:`, error);
|
||||
toast.error('Installation failed');
|
||||
} finally {
|
||||
setIsInstalling(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
interface UseCliStatusOptions {
|
||||
cliType: 'claude';
|
||||
@@ -14,13 +15,14 @@ export function useCliStatus({
|
||||
setAuthStatus,
|
||||
}: UseCliStatusOptions) {
|
||||
const [isChecking, setIsChecking] = useState(false);
|
||||
const logger = createLogger('CliStatus');
|
||||
|
||||
const checkStatus = useCallback(async () => {
|
||||
console.log(`[${cliType} Setup] Starting status check...`);
|
||||
logger.info(`Starting status check for ${cliType}...`);
|
||||
setIsChecking(true);
|
||||
try {
|
||||
const result = await statusApi();
|
||||
console.log(`[${cliType} Setup] Raw status result:`, result);
|
||||
logger.info(`Raw status result for ${cliType}:`, result);
|
||||
|
||||
if (result.success) {
|
||||
const cliStatus = {
|
||||
@@ -29,7 +31,7 @@ export function useCliStatus({
|
||||
version: result.version || null,
|
||||
method: result.method || 'none',
|
||||
};
|
||||
console.log(`[${cliType} Setup] CLI Status:`, cliStatus);
|
||||
logger.info(`CLI Status for ${cliType}:`, cliStatus);
|
||||
setCliStatus(cliStatus);
|
||||
|
||||
if (result.auth) {
|
||||
@@ -60,11 +62,11 @@ export function useCliStatus({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[${cliType} Setup] Failed to check status:`, error);
|
||||
logger.error(`Failed to check status for ${cliType}:`, error);
|
||||
} finally {
|
||||
setIsChecking(false);
|
||||
}
|
||||
}, [cliType, statusApi, setCliStatus, setAuthStatus]);
|
||||
}, [cliType, statusApi, setCliStatus, setAuthStatus, logger]);
|
||||
|
||||
return { isChecking, checkStatus };
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('TokenSave');
|
||||
|
||||
interface UseTokenSaveOptions {
|
||||
provider: string; // e.g., "anthropic_oauth_token", "anthropic", "openai"
|
||||
@@ -24,7 +27,7 @@ export function useTokenSave({ provider, onSuccess }: UseTokenSaveOptions) {
|
||||
|
||||
if (setupApi?.storeApiKey) {
|
||||
const result = await setupApi.storeApiKey(provider, tokenValue);
|
||||
console.log(`[Token Save] Store result for ${provider}:`, result);
|
||||
logger.info(`Store result for ${provider}:`, result);
|
||||
|
||||
if (result.success) {
|
||||
const tokenType = provider.includes('oauth') ? 'subscription token' : 'API key';
|
||||
@@ -42,7 +45,7 @@ export function useTokenSave({ provider, onSuccess }: UseTokenSaveOptions) {
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[Token Save] Failed to save ${provider}:`, error);
|
||||
logger.error(`Failed to save ${provider}:`, error);
|
||||
toast.error('Failed to save token');
|
||||
return false;
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -19,6 +20,8 @@ import {
|
||||
import { toast } from 'sonner';
|
||||
import { StatusBadge } from '../components';
|
||||
|
||||
const logger = createLogger('CursorSetupStep');
|
||||
|
||||
interface CursorSetupStepProps {
|
||||
onNext: () => void;
|
||||
onBack: () => void;
|
||||
@@ -67,7 +70,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check Cursor status:', error);
|
||||
logger.error('Failed to check Cursor status:', error);
|
||||
} finally {
|
||||
setIsChecking(false);
|
||||
}
|
||||
@@ -140,7 +143,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
|
||||
}
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
logger.error('Login failed:', error);
|
||||
toast.error('Failed to start login process');
|
||||
setIsLoggingIn(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
@@ -18,6 +19,8 @@ import {
|
||||
import { toast } from 'sonner';
|
||||
import { StatusBadge } from '../components';
|
||||
|
||||
const logger = createLogger('GitHubSetupStep');
|
||||
|
||||
interface GitHubSetupStepProps {
|
||||
onNext: () => void;
|
||||
onBack: () => void;
|
||||
@@ -46,7 +49,7 @@ export function GitHubSetupStep({ onNext, onBack, onSkip }: GitHubSetupStepProps
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check gh status:', error);
|
||||
logger.error('Failed to check gh status:', error);
|
||||
} finally {
|
||||
setIsChecking(false);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('SpecGeneration');
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { toast } from 'sonner';
|
||||
import { CheckCircle2 } from 'lucide-react';
|
||||
@@ -79,7 +82,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
|
||||
const status = await api.specRegeneration.status();
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Status check on mount:',
|
||||
status,
|
||||
'for project:',
|
||||
@@ -87,7 +90,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
);
|
||||
|
||||
if (status.success && status.isRunning) {
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Spec generation is running globally. Tentatively showing loader.'
|
||||
);
|
||||
|
||||
@@ -103,7 +106,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
clearTimeout(pendingStatusTimeoutRef.current);
|
||||
}
|
||||
pendingStatusTimeoutRef.current = setTimeout(() => {
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] No events received for current project - clearing tentative state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
@@ -118,7 +121,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
stateRestoredRef.current = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[useSpecGeneration] Failed to check status:', error);
|
||||
logger.error('[useSpecGeneration] Failed to check status:', error);
|
||||
} finally {
|
||||
statusCheckRef.current = false;
|
||||
}
|
||||
@@ -141,10 +144,10 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
if (!api.specRegeneration) return;
|
||||
|
||||
const status = await api.specRegeneration.status();
|
||||
console.log('[useSpecGeneration] Visibility change - status check:', status);
|
||||
logger.debug('[useSpecGeneration] Visibility change - status check:', status);
|
||||
|
||||
if (!status.isRunning) {
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Visibility change: Backend indicates generation complete - clearing state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
@@ -157,7 +160,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
setCurrentPhase(status.currentPhase);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[useSpecGeneration] Failed to check status on visibility change:', error);
|
||||
logger.error('[useSpecGeneration] Failed to check status on visibility change:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -180,7 +183,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
const status = await api.specRegeneration.status();
|
||||
|
||||
if (!status.isRunning) {
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Periodic check: Backend indicates generation complete - clearing state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
@@ -190,14 +193,14 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
} else if (status.currentPhase && status.currentPhase !== currentPhase) {
|
||||
console.log('[useSpecGeneration] Periodic check: Phase updated from backend', {
|
||||
logger.debug('[useSpecGeneration] Periodic check: Phase updated from backend', {
|
||||
old: currentPhase,
|
||||
new: status.currentPhase,
|
||||
});
|
||||
setCurrentPhase(status.currentPhase);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[useSpecGeneration] Periodic status check error:', error);
|
||||
logger.error('[useSpecGeneration] Periodic status check error:', error);
|
||||
}
|
||||
}, STATUS_CHECK_INTERVAL_MS);
|
||||
|
||||
@@ -214,7 +217,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
if (!api.specRegeneration) return;
|
||||
|
||||
const unsubscribe = api.specRegeneration.onEvent((event: SpecRegenerationEvent) => {
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Regeneration event:',
|
||||
event.type,
|
||||
'for project:',
|
||||
@@ -224,14 +227,14 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
);
|
||||
|
||||
if (event.projectPath !== currentProject?.path) {
|
||||
console.log('[useSpecGeneration] Ignoring event - not for current project');
|
||||
logger.debug('[useSpecGeneration] Ignoring event - not for current project');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingStatusTimeoutRef.current) {
|
||||
clearTimeout(pendingStatusTimeoutRef.current);
|
||||
pendingStatusTimeoutRef.current = null;
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Event confirmed this is for current project - clearing timeout'
|
||||
);
|
||||
}
|
||||
@@ -244,10 +247,10 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
if (phaseMatch) {
|
||||
const phase = phaseMatch[1];
|
||||
setCurrentPhase(phase);
|
||||
console.log(`[useSpecGeneration] Phase updated: ${phase}`);
|
||||
logger.debug(`[useSpecGeneration] Phase updated: ${phase}`);
|
||||
|
||||
if (phase === 'complete') {
|
||||
console.log('[useSpecGeneration] Phase is complete - clearing state');
|
||||
logger.debug('[useSpecGeneration] Phase is complete - clearing state');
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
stateRestoredRef.current = false;
|
||||
@@ -261,7 +264,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
event.content.includes('All tasks completed') ||
|
||||
event.content.includes('✓ All tasks completed')
|
||||
) {
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Detected completion in progress message - clearing state'
|
||||
);
|
||||
setIsCreating(false);
|
||||
@@ -276,7 +279,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
const newLog = logsRef.current + event.content;
|
||||
logsRef.current = newLog;
|
||||
setLogs(newLog);
|
||||
console.log('[useSpecGeneration] Progress:', event.content.substring(0, 100));
|
||||
logger.debug('[useSpecGeneration] Progress:', event.content.substring(0, 100));
|
||||
|
||||
if (errorMessage) {
|
||||
setErrorMessage('');
|
||||
@@ -292,7 +295,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
setCurrentPhase('feature_generation');
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Detected feature creation tool - setting phase to feature_generation'
|
||||
);
|
||||
}
|
||||
@@ -305,7 +308,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
const newLog = logsRef.current + toolLog;
|
||||
logsRef.current = newLog;
|
||||
setLogs(newLog);
|
||||
console.log('[useSpecGeneration] Tool:', event.tool, event.input);
|
||||
logger.debug('[useSpecGeneration] Tool:', event.tool, event.input);
|
||||
} else if (event.type === 'spec_regeneration_complete') {
|
||||
const completionLog = logsRef.current + `\n[Complete] ${event.message}\n`;
|
||||
logsRef.current = completionLog;
|
||||
@@ -328,7 +331,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
(isFinalCompletionMessage || hasCompletePhase) && !isIntermediateCompletion;
|
||||
|
||||
if (shouldComplete) {
|
||||
console.log('[useSpecGeneration] Final completion detected - clearing state', {
|
||||
logger.debug('[useSpecGeneration] Final completion detected - clearing state', {
|
||||
isFinalCompletionMessage,
|
||||
hasCompletePhase,
|
||||
message: event.message,
|
||||
@@ -367,12 +370,12 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
setCurrentPhase('feature_generation');
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Intermediate completion, continuing with feature generation'
|
||||
);
|
||||
}
|
||||
|
||||
console.log('[useSpecGeneration] Spec generation event:', event.message);
|
||||
logger.debug('[useSpecGeneration] Spec generation event:', event.message);
|
||||
} else if (event.type === 'spec_regeneration_error') {
|
||||
setIsRegenerating(false);
|
||||
setIsCreating(false);
|
||||
@@ -383,7 +386,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
const errorLog = logsRef.current + `\n\n[ERROR] ${event.error}\n`;
|
||||
logsRef.current = errorLog;
|
||||
setLogs(errorLog);
|
||||
console.error('[useSpecGeneration] Regeneration error:', event.error);
|
||||
logger.error('[useSpecGeneration] Regeneration error:', event.error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -402,11 +405,11 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
setErrorMessage('');
|
||||
logsRef.current = '';
|
||||
setLogs('');
|
||||
console.log('[useSpecGeneration] Starting spec creation, generateFeatures:', generateFeatures);
|
||||
logger.debug('[useSpecGeneration] Starting spec creation, generateFeatures:', generateFeatures);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
||||
logger.error('[useSpecGeneration] Spec regeneration not available');
|
||||
setIsCreating(false);
|
||||
return;
|
||||
}
|
||||
@@ -420,7 +423,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error || 'Unknown error';
|
||||
console.error('[useSpecGeneration] Failed to start spec creation:', errorMsg);
|
||||
logger.error('[useSpecGeneration] Failed to start spec creation:', errorMsg);
|
||||
setIsCreating(false);
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
@@ -430,7 +433,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
console.error('[useSpecGeneration] Failed to create spec:', errorMsg);
|
||||
logger.error('[useSpecGeneration] Failed to create spec:', errorMsg);
|
||||
setIsCreating(false);
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
@@ -455,14 +458,14 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
setErrorMessage('');
|
||||
logsRef.current = '';
|
||||
setLogs('');
|
||||
console.log(
|
||||
logger.debug(
|
||||
'[useSpecGeneration] Starting spec regeneration, generateFeatures:',
|
||||
generateFeaturesOnRegenerate
|
||||
);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
||||
logger.error('[useSpecGeneration] Spec regeneration not available');
|
||||
setIsRegenerating(false);
|
||||
return;
|
||||
}
|
||||
@@ -476,7 +479,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error || 'Unknown error';
|
||||
console.error('[useSpecGeneration] Failed to start regeneration:', errorMsg);
|
||||
logger.error('[useSpecGeneration] Failed to start regeneration:', errorMsg);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
@@ -486,7 +489,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
console.error('[useSpecGeneration] Failed to regenerate spec:', errorMsg);
|
||||
logger.error('[useSpecGeneration] Failed to regenerate spec:', errorMsg);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
@@ -511,11 +514,11 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
setErrorMessage('');
|
||||
logsRef.current = '';
|
||||
setLogs('');
|
||||
console.log('[useSpecGeneration] Starting feature generation from existing spec');
|
||||
logger.debug('[useSpecGeneration] Starting feature generation from existing spec');
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
console.error('[useSpecGeneration] Spec regeneration not available');
|
||||
logger.error('[useSpecGeneration] Spec regeneration not available');
|
||||
setIsGeneratingFeatures(false);
|
||||
return;
|
||||
}
|
||||
@@ -523,7 +526,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error || 'Unknown error';
|
||||
console.error('[useSpecGeneration] Failed to start feature generation:', errorMsg);
|
||||
logger.error('[useSpecGeneration] Failed to start feature generation:', errorMsg);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
@@ -533,7 +536,7 @@ export function useSpecGeneration({ loadSpec }: UseSpecGenerationOptions) {
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
console.error('[useSpecGeneration] Failed to generate features:', errorMsg);
|
||||
logger.error('[useSpecGeneration] Failed to generate features:', errorMsg);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase('error');
|
||||
setErrorMessage(errorMsg);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('SpecLoading');
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
export function useSpecLoading() {
|
||||
@@ -24,7 +27,7 @@ export function useSpecLoading() {
|
||||
setSpecExists(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load spec:', error);
|
||||
logger.error('Failed to load spec:', error);
|
||||
setSpecExists(false);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('SpecSave');
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
|
||||
export function useSpecSave() {
|
||||
@@ -16,7 +19,7 @@ export function useSpecSave() {
|
||||
await api.writeFile(`${currentProject.path}/.automaker/app_spec.txt`, appSpec);
|
||||
setHasChanges(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to save spec:', error);
|
||||
logger.error('Failed to save spec:', error);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Terminal as TerminalIcon,
|
||||
Plus,
|
||||
@@ -50,6 +51,8 @@ import { cn } from '@/lib/utils';
|
||||
import { apiFetch, apiGet, apiPost, apiDeleteRaw, getAuthHeaders } from '@/lib/api-fetch';
|
||||
import { getApiKey } from '@/lib/http-api-client';
|
||||
|
||||
const logger = createLogger('Terminal');
|
||||
|
||||
interface TerminalStatus {
|
||||
enabled: boolean;
|
||||
passwordRequired: boolean;
|
||||
@@ -301,7 +304,7 @@ export function TerminalView() {
|
||||
headers['X-Terminal-Token'] = terminalState.authToken;
|
||||
}
|
||||
|
||||
console.log(`[Terminal] Killing ${sessionIds.length} sessions on server`);
|
||||
logger.info(`Killing ${sessionIds.length} sessions on server`);
|
||||
|
||||
// Kill all sessions in parallel
|
||||
await Promise.allSettled(
|
||||
@@ -309,7 +312,7 @@ export function TerminalView() {
|
||||
try {
|
||||
await apiDeleteRaw(`/api/terminal/sessions/${sessionId}`, { headers });
|
||||
} catch (err) {
|
||||
console.error(`[Terminal] Failed to kill session ${sessionId}:`, err);
|
||||
logger.error(`Failed to kill session ${sessionId}:`, err);
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -320,7 +323,7 @@ export function TerminalView() {
|
||||
const canCreateTerminal = (debounceMessage: string): boolean => {
|
||||
const now = Date.now();
|
||||
if (now - lastCreateTimeRef.current < CREATE_COOLDOWN_MS || isCreatingRef.current) {
|
||||
console.log(debounceMessage);
|
||||
logger.debug(debounceMessage);
|
||||
return false;
|
||||
}
|
||||
lastCreateTimeRef.current = now;
|
||||
@@ -447,7 +450,7 @@ export function TerminalView() {
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Failed to connect to server');
|
||||
console.error('[Terminal] Status fetch error:', err);
|
||||
logger.error('Status fetch error:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -469,7 +472,7 @@ export function TerminalView() {
|
||||
setServerSessionInfo({ current: data.data.currentSessions, max: data.data.maxSessions });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Failed to fetch server settings:', err);
|
||||
logger.error('Failed to fetch server settings:', err);
|
||||
}
|
||||
}, [terminalState.isUnlocked, terminalState.authToken]);
|
||||
|
||||
@@ -573,7 +576,7 @@ export function TerminalView() {
|
||||
|
||||
// If no saved layout or no tabs, we're done - terminal starts fresh for this project
|
||||
if (!savedLayout || savedLayout.tabs.length === 0) {
|
||||
console.log('[Terminal] No saved layout for project, starting fresh');
|
||||
logger.info('No saved layout for project, starting fresh');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -585,7 +588,7 @@ export function TerminalView() {
|
||||
const restoreLayout = async () => {
|
||||
// Check if we're still restoring the same project (user may have switched)
|
||||
if (restoringProjectPathRef.current !== currentPath) {
|
||||
console.log('[Terminal] Restore cancelled - project changed');
|
||||
logger.info('Restore cancelled - project changed');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -623,7 +626,7 @@ export function TerminalView() {
|
||||
);
|
||||
return data.success && data.data ? data.data.id : null;
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Failed to create terminal session:', err);
|
||||
logger.error('Failed to create terminal session:', err);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -691,7 +694,7 @@ export function TerminalView() {
|
||||
for (let tabIndex = 0; tabIndex < savedLayout.tabs.length; tabIndex++) {
|
||||
// Check if project changed during restore - bail out early
|
||||
if (restoringProjectPathRef.current !== currentPath) {
|
||||
console.log('[Terminal] Restore cancelled mid-loop - project changed');
|
||||
logger.info('Restore cancelled mid-loop - project changed');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -730,7 +733,7 @@ export function TerminalView() {
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Failed to restore terminal layout:', err);
|
||||
logger.error('Failed to restore terminal layout:', err);
|
||||
toast.error('Failed to restore terminals', {
|
||||
description: 'Could not restore terminal layout. Please try creating new terminals.',
|
||||
duration: 5000,
|
||||
@@ -806,7 +809,7 @@ export function TerminalView() {
|
||||
}
|
||||
} catch (err) {
|
||||
setAuthError('Failed to authenticate');
|
||||
console.error('[Terminal] Auth error:', err);
|
||||
logger.error('Auth error:', err);
|
||||
} finally {
|
||||
setAuthLoading(false);
|
||||
}
|
||||
@@ -851,14 +854,14 @@ export function TerminalView() {
|
||||
`Please close unused terminals. Limit: ${data.maxSessions || 'unknown'}`,
|
||||
});
|
||||
} else {
|
||||
console.error('[Terminal] Failed to create session:', data.error);
|
||||
logger.error('Failed to create session:', data.error);
|
||||
toast.error('Failed to create terminal', {
|
||||
description: data.error || 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Create session error:', err);
|
||||
logger.error('Create session error:', err);
|
||||
toast.error('Failed to create terminal', {
|
||||
description: 'Could not connect to server',
|
||||
});
|
||||
@@ -915,7 +918,7 @@ export function TerminalView() {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Create session error:', err);
|
||||
logger.error('Create session error:', err);
|
||||
// Remove the empty tab on error
|
||||
const { removeTerminalTab } = useAppStore.getState();
|
||||
removeTerminalTab(tabId);
|
||||
@@ -943,16 +946,13 @@ export function TerminalView() {
|
||||
if (!response.ok && response.status !== 404) {
|
||||
// Log non-404 errors but still proceed with UI cleanup
|
||||
const data = await response.json().catch(() => ({}));
|
||||
console.error(
|
||||
'[Terminal] Server failed to kill session:',
|
||||
data.error || response.statusText
|
||||
);
|
||||
logger.error('Server failed to kill session:', data.error || response.statusText);
|
||||
}
|
||||
|
||||
// Refresh session count
|
||||
fetchServerSettings();
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Kill session error:', err);
|
||||
logger.error('Kill session error:', err);
|
||||
// Still remove from UI on network error - better UX than leaving broken terminal
|
||||
removeTerminalFromLayout(sessionId);
|
||||
}
|
||||
@@ -983,7 +983,7 @@ export function TerminalView() {
|
||||
try {
|
||||
await apiDeleteRaw(`/api/terminal/sessions/${sessionId}`, { headers });
|
||||
} catch (err) {
|
||||
console.error(`[Terminal] Failed to kill session ${sessionId}:`, err);
|
||||
logger.error(`Failed to kill session ${sessionId}:`, err);
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -1210,9 +1210,7 @@ export function TerminalView() {
|
||||
onSessionInvalid={() => {
|
||||
// Auto-remove stale session when server says it doesn't exist
|
||||
// This handles cases like server restart where sessions are lost
|
||||
console.log(
|
||||
`[Terminal] Session ${content.sessionId} is invalid, removing from layout`
|
||||
);
|
||||
logger.info(`Session ${content.sessionId} is invalid, removing from layout`);
|
||||
killTerminal(content.sessionId);
|
||||
}}
|
||||
isDragging={activeDragId === content.sessionId}
|
||||
@@ -1587,9 +1585,7 @@ export function TerminalView() {
|
||||
onNewTab={createTerminalInNewTab}
|
||||
onSessionInvalid={() => {
|
||||
const sessionId = terminalState.maximizedSessionId!;
|
||||
console.log(
|
||||
`[Terminal] Maximized session ${sessionId} is invalid, removing from layout`
|
||||
);
|
||||
logger.info(`Maximized session ${sessionId} is invalid, removing from layout`);
|
||||
killTerminal(sessionId);
|
||||
}}
|
||||
isDragging={false}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React, { Component, ErrorInfo } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { AlertCircle, RefreshCw } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const logger = createLogger('TerminalErrorBoundary');
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
sessionId: string;
|
||||
@@ -30,7 +33,7 @@ export class TerminalErrorBoundary extends Component<Props, State> {
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
console.error('[TerminalErrorBoundary] Terminal crashed:', {
|
||||
logger.error('Terminal crashed:', {
|
||||
sessionId: this.props.sessionId,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
X,
|
||||
SplitSquareHorizontal,
|
||||
@@ -42,6 +43,8 @@ import { toast } from 'sonner';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { getApiKey, getSessionToken, getServerUrlSync } from '@/lib/http-api-client';
|
||||
|
||||
const logger = createLogger('Terminal');
|
||||
|
||||
// Font size constraints
|
||||
const MIN_FONT_SIZE = 8;
|
||||
const MAX_FONT_SIZE = 32;
|
||||
@@ -296,7 +299,7 @@ export function TerminalPanel({
|
||||
toast.success('Copied to clipboard');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Copy failed:', err);
|
||||
logger.error('Copy failed:', err);
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
toast.error('Copy failed', {
|
||||
description: errorMessage.includes('permission')
|
||||
@@ -361,7 +364,7 @@ export function TerminalPanel({
|
||||
|
||||
await sendTextInChunks(text);
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Paste failed:', err);
|
||||
logger.error('Paste failed:', err);
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
toast.error('Paste failed', {
|
||||
description: errorMessage.includes('permission')
|
||||
@@ -504,7 +507,7 @@ export function TerminalPanel({
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('[Terminal] Failed to fetch wsToken:', response.status);
|
||||
logger.warn('Failed to fetch wsToken:', response.status);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -515,7 +518,7 @@ export function TerminalPanel({
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('[Terminal] Error fetching wsToken:', error);
|
||||
logger.error('Error fetching wsToken:', error);
|
||||
return null;
|
||||
}
|
||||
}, [serverUrl]);
|
||||
@@ -595,7 +598,7 @@ export function TerminalPanel({
|
||||
const api = getElectronAPI();
|
||||
if (api?.openExternalLink) {
|
||||
api.openExternalLink(uri).catch((error) => {
|
||||
console.error('[Terminal] Failed to open URL:', error);
|
||||
logger.error('Failed to open URL:', error);
|
||||
// Fallback to window.open if Electron API fails
|
||||
window.open(uri, '_blank', 'noopener,noreferrer');
|
||||
});
|
||||
@@ -697,7 +700,7 @@ export function TerminalPanel({
|
||||
}
|
||||
} catch {
|
||||
// If we can't get home path, just use the path as-is
|
||||
console.warn('[Terminal] Could not resolve home directory path');
|
||||
logger.warn('Could not resolve home directory path');
|
||||
}
|
||||
} else if (!clickedPath.startsWith('/') && !clickedPath.match(/^[a-zA-Z]:\\/)) {
|
||||
// Relative path - resolve against project path
|
||||
@@ -721,7 +724,7 @@ export function TerminalPanel({
|
||||
toast.error('Failed to open in editor', { description: result.error });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Terminal] Failed to open file:', error);
|
||||
logger.error('Failed to open file:', error);
|
||||
toast.error('Failed to open file', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -744,7 +747,7 @@ export function TerminalPanel({
|
||||
});
|
||||
terminal.loadAddon(webglAddon);
|
||||
} catch {
|
||||
console.warn('[Terminal] WebGL addon not available, falling back to canvas');
|
||||
logger.warn('WebGL addon not available, falling back to canvas');
|
||||
}
|
||||
|
||||
// Fit terminal to container - wait for stable dimensions
|
||||
@@ -770,7 +773,7 @@ export function TerminalPanel({
|
||||
try {
|
||||
fitAddon.fit();
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Initial fit error:', err);
|
||||
logger.error('Initial fit error:', err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1000,7 +1003,7 @@ export function TerminalPanel({
|
||||
wsRef.current = ws;
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log(`[Terminal] WebSocket connected for session ${sessionId}`);
|
||||
logger.info(`WebSocket connected for session ${sessionId}`);
|
||||
|
||||
setConnectionStatus('connected');
|
||||
reconnectAttemptsRef.current = 0;
|
||||
@@ -1037,7 +1040,7 @@ export function TerminalPanel({
|
||||
}
|
||||
break;
|
||||
case 'connected': {
|
||||
console.log(`[Terminal] Session connected: ${msg.shell} in ${msg.cwd}`);
|
||||
logger.info(`Session connected: ${msg.shell} in ${msg.cwd}`);
|
||||
// Detect shell type from path
|
||||
const shellPath = (msg.shell || '').toLowerCase();
|
||||
// Windows shells use backslash paths and include powershell/pwsh/cmd
|
||||
@@ -1088,16 +1091,12 @@ export function TerminalPanel({
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Message parse error:', err);
|
||||
logger.error('Message parse error:', err);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = (event) => {
|
||||
console.log(
|
||||
`[Terminal] WebSocket closed for session ${sessionId}:`,
|
||||
event.code,
|
||||
event.reason
|
||||
);
|
||||
logger.info(`WebSocket closed for session ${sessionId}: ${event.code} ${event.reason}`);
|
||||
wsRef.current = null;
|
||||
|
||||
// Clear heartbeat interval
|
||||
@@ -1167,8 +1166,8 @@ export function TerminalPanel({
|
||||
// Attempt reconnect after exponential delay
|
||||
reconnectTimeoutRef.current = setTimeout(() => {
|
||||
if (xtermRef.current) {
|
||||
console.log(
|
||||
`[Terminal] Attempting reconnect for session ${sessionId} (attempt ${reconnectAttemptsRef.current}/${MAX_RECONNECT_ATTEMPTS})`
|
||||
logger.info(
|
||||
`Attempting reconnect for session ${sessionId} (attempt ${reconnectAttemptsRef.current}/${MAX_RECONNECT_ATTEMPTS})`
|
||||
);
|
||||
connect();
|
||||
}
|
||||
@@ -1176,7 +1175,7 @@ export function TerminalPanel({
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error(`[Terminal] WebSocket error for session ${sessionId}:`, error);
|
||||
logger.error(`WebSocket error for session ${sessionId}:`, error);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1234,7 +1233,7 @@ export function TerminalPanel({
|
||||
wsRef.current.send(JSON.stringify({ type: 'resize', cols, rows }));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Terminal] Resize error:', err);
|
||||
logger.error('Resize error:', err);
|
||||
}
|
||||
}, RESIZE_DEBOUNCE_MS);
|
||||
}, []);
|
||||
@@ -1551,7 +1550,7 @@ export function TerminalPanel({
|
||||
const api = getElectronAPI();
|
||||
if (!api.saveImageToTemp) {
|
||||
// Fallback path when Electron API is not available (browser mode)
|
||||
console.warn('[Terminal] saveImageToTemp not available, returning fallback path');
|
||||
logger.warn('saveImageToTemp not available, returning fallback path');
|
||||
return `.automaker/images/${Date.now()}_${filename}`;
|
||||
}
|
||||
|
||||
@@ -1560,10 +1559,10 @@ export function TerminalPanel({
|
||||
if (result.success && result.path) {
|
||||
return result.path;
|
||||
}
|
||||
console.error('[Terminal] Failed to save image:', result.error);
|
||||
logger.error('Failed to save image:', result.error);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('[Terminal] Error saving image:', error);
|
||||
logger.error('Error saving image:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@@ -1662,7 +1661,7 @@ export function TerminalPanel({
|
||||
toast.error(`Failed to save: ${file.name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Terminal] Error processing image:', error);
|
||||
logger.error('Error processing image:', error);
|
||||
toast.error(`Error processing: ${file.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -34,6 +35,8 @@ import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import type { StarterTemplate } from '@/lib/templates';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
|
||||
const logger = createLogger('WelcomeView');
|
||||
|
||||
export function WelcomeView() {
|
||||
const {
|
||||
projects,
|
||||
@@ -65,13 +68,13 @@ export function WelcomeView() {
|
||||
const api = getElectronAPI();
|
||||
|
||||
if (!api.autoMode?.analyzeProject) {
|
||||
console.log('[Welcome] Auto mode API not available, skipping analysis');
|
||||
logger.info('[Welcome] Auto mode API not available, skipping analysis');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsAnalyzing(true);
|
||||
try {
|
||||
console.log('[Welcome] Starting project analysis for:', projectPath);
|
||||
logger.info('[Welcome] Starting project analysis for:', projectPath);
|
||||
const result = await api.autoMode.analyzeProject(projectPath);
|
||||
|
||||
if (result.success) {
|
||||
@@ -79,10 +82,10 @@ export function WelcomeView() {
|
||||
description: 'AI agent has analyzed your project structure',
|
||||
});
|
||||
} else {
|
||||
console.error('[Welcome] Project analysis failed:', result.error);
|
||||
logger.error('[Welcome] Project analysis failed:', result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Welcome] Failed to analyze project:', error);
|
||||
logger.error('[Welcome] Failed to analyze project:', error);
|
||||
} finally {
|
||||
setIsAnalyzing(false);
|
||||
}
|
||||
@@ -125,8 +128,8 @@ export function WelcomeView() {
|
||||
setShowInitDialog(true);
|
||||
|
||||
// Kick off agent to analyze the project and update app_spec.txt
|
||||
console.log('[Welcome] Project initialized, created files:', initResult.createdFiles);
|
||||
console.log('[Welcome] Kicking off project analysis agent...');
|
||||
logger.info('[Welcome] Project initialized, created files:', initResult.createdFiles);
|
||||
logger.info('[Welcome] Kicking off project analysis agent...');
|
||||
|
||||
// Start analysis in background (don't await, let it run async)
|
||||
analyzeProject(path);
|
||||
@@ -139,7 +142,7 @@ export function WelcomeView() {
|
||||
// Navigate to the board view
|
||||
navigate({ to: '/board' });
|
||||
} catch (error) {
|
||||
console.error('[Welcome] Failed to open project:', error);
|
||||
logger.error('[Welcome] Failed to open project:', error);
|
||||
toast.error('Failed to open project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -179,7 +182,7 @@ export function WelcomeView() {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Welcome] Failed to check workspace config:', error);
|
||||
logger.error('[Welcome] Failed to check workspace config:', error);
|
||||
// Fall back to current behavior on error
|
||||
const api = getElectronAPI();
|
||||
const result = await api.openDirectory();
|
||||
@@ -317,7 +320,7 @@ export function WelcomeView() {
|
||||
});
|
||||
setShowInitDialog(true);
|
||||
} catch (error) {
|
||||
console.error('Failed to create project:', error);
|
||||
logger.error('Failed to create project:', error);
|
||||
toast.error('Failed to create project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -418,7 +421,7 @@ export function WelcomeView() {
|
||||
// Kick off project analysis
|
||||
analyzeProject(projectPath);
|
||||
} catch (error) {
|
||||
console.error('Failed to create project from template:', error);
|
||||
logger.error('Failed to create project from template:', error);
|
||||
toast.error('Failed to create project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
@@ -515,7 +518,7 @@ export function WelcomeView() {
|
||||
// Kick off project analysis
|
||||
analyzeProject(projectPath);
|
||||
} catch (error) {
|
||||
console.error('Failed to create project from custom URL:', error);
|
||||
logger.error('Failed to create project from custom URL:', error);
|
||||
toast.error('Failed to create project', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useEffect, useCallback, useMemo } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import type { AutoModeEvent } from '@/types/electron';
|
||||
|
||||
const logger = createLogger('AutoMode');
|
||||
|
||||
// Type guard for plan_approval_required event
|
||||
function isPlanApprovalEvent(
|
||||
event: AutoModeEvent
|
||||
@@ -67,7 +70,7 @@ export function useAutoMode() {
|
||||
if (!api?.autoMode) return;
|
||||
|
||||
const unsubscribe = api.autoMode.onEvent((event: AutoModeEvent) => {
|
||||
console.log('[AutoMode Event]', event);
|
||||
logger.info('Event:', event);
|
||||
|
||||
// Events include projectPath from backend - use it to look up project ID
|
||||
// Fall back to current projectId if not provided in event
|
||||
@@ -84,7 +87,7 @@ export function useAutoMode() {
|
||||
|
||||
// Skip event if we couldn't determine the project
|
||||
if (!eventProjectId) {
|
||||
console.warn('[AutoMode] Could not determine project for event:', event);
|
||||
logger.warn('Could not determine project for event:', event);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ export function useAutoMode() {
|
||||
case 'auto_mode_feature_complete':
|
||||
// Feature completed - remove from running tasks and UI will reload features on its own
|
||||
if (event.featureId) {
|
||||
console.log('[AutoMode] Feature completed:', event.featureId, 'passes:', event.passes);
|
||||
logger.info('Feature completed:', event.featureId, 'passes:', event.passes);
|
||||
removeRunningTask(eventProjectId, event.featureId);
|
||||
addAutoModeActivity({
|
||||
featureId: event.featureId,
|
||||
@@ -121,7 +124,7 @@ export function useAutoMode() {
|
||||
// Check if this is a user-initiated cancellation or abort (not a real error)
|
||||
if (event.errorType === 'cancellation' || event.errorType === 'abort') {
|
||||
// User cancelled/aborted the feature - just log as info, not an error
|
||||
console.log('[AutoMode] Feature cancelled/aborted:', event.error);
|
||||
logger.info('Feature cancelled/aborted:', event.error);
|
||||
// Remove from running tasks
|
||||
if (eventProjectId) {
|
||||
removeRunningTask(eventProjectId, event.featureId);
|
||||
@@ -130,7 +133,7 @@ export function useAutoMode() {
|
||||
}
|
||||
|
||||
// Real error - log and show to user
|
||||
console.error('[AutoMode Error]', event.error);
|
||||
logger.error('Error:', event.error);
|
||||
|
||||
// Check for authentication errors and provide a more helpful message
|
||||
const isAuthError =
|
||||
@@ -182,7 +185,7 @@ export function useAutoMode() {
|
||||
case 'auto_mode_phase':
|
||||
// Log phase transitions (Planning, Action, Verification)
|
||||
if (event.featureId && event.phase && event.message) {
|
||||
console.log(`[AutoMode] Phase: ${event.phase} for ${event.featureId}`);
|
||||
logger.debug(`[AutoMode] Phase: ${event.phase} for ${event.featureId}`);
|
||||
addAutoModeActivity({
|
||||
featureId: event.featureId,
|
||||
type: event.phase,
|
||||
@@ -195,7 +198,7 @@ export function useAutoMode() {
|
||||
case 'plan_approval_required':
|
||||
// Plan requires user approval before proceeding
|
||||
if (isPlanApprovalEvent(event)) {
|
||||
console.log(`[AutoMode] Plan approval required for ${event.featureId}`);
|
||||
logger.debug(`[AutoMode] Plan approval required for ${event.featureId}`);
|
||||
setPendingPlanApproval({
|
||||
featureId: event.featureId,
|
||||
projectPath: event.projectPath || currentProject?.path || '',
|
||||
@@ -208,7 +211,7 @@ export function useAutoMode() {
|
||||
case 'planning_started':
|
||||
// Log when planning phase begins
|
||||
if (event.featureId && event.mode && event.message) {
|
||||
console.log(`[AutoMode] Planning started (${event.mode}) for ${event.featureId}`);
|
||||
logger.debug(`[AutoMode] Planning started (${event.mode}) for ${event.featureId}`);
|
||||
addAutoModeActivity({
|
||||
featureId: event.featureId,
|
||||
type: 'planning',
|
||||
@@ -221,7 +224,7 @@ export function useAutoMode() {
|
||||
case 'plan_approved':
|
||||
// Log when plan is approved by user
|
||||
if (event.featureId) {
|
||||
console.log(`[AutoMode] Plan approved for ${event.featureId}`);
|
||||
logger.debug(`[AutoMode] Plan approved for ${event.featureId}`);
|
||||
addAutoModeActivity({
|
||||
featureId: event.featureId,
|
||||
type: 'action',
|
||||
@@ -236,7 +239,7 @@ export function useAutoMode() {
|
||||
case 'plan_auto_approved':
|
||||
// Log when plan is auto-approved (requirePlanApproval=false)
|
||||
if (event.featureId) {
|
||||
console.log(`[AutoMode] Plan auto-approved for ${event.featureId}`);
|
||||
logger.debug(`[AutoMode] Plan auto-approved for ${event.featureId}`);
|
||||
addAutoModeActivity({
|
||||
featureId: event.featureId,
|
||||
type: 'action',
|
||||
@@ -253,7 +256,7 @@ export function useAutoMode() {
|
||||
AutoModeEvent,
|
||||
{ type: 'plan_revision_requested' }
|
||||
>;
|
||||
console.log(
|
||||
logger.debug(
|
||||
`[AutoMode] Plan revision requested for ${event.featureId} (v${revisionEvent.planVersion})`
|
||||
);
|
||||
addAutoModeActivity({
|
||||
@@ -269,7 +272,7 @@ export function useAutoMode() {
|
||||
// Task started - show which task is being worked on
|
||||
if (event.featureId && 'taskId' in event && 'taskDescription' in event) {
|
||||
const taskEvent = event as Extract<AutoModeEvent, { type: 'auto_mode_task_started' }>;
|
||||
console.log(
|
||||
logger.debug(
|
||||
`[AutoMode] Task ${taskEvent.taskId} started for ${event.featureId}: ${taskEvent.taskDescription}`
|
||||
);
|
||||
addAutoModeActivity({
|
||||
@@ -284,7 +287,7 @@ export function useAutoMode() {
|
||||
// Task completed - show progress
|
||||
if (event.featureId && 'taskId' in event) {
|
||||
const taskEvent = event as Extract<AutoModeEvent, { type: 'auto_mode_task_complete' }>;
|
||||
console.log(
|
||||
logger.debug(
|
||||
`[AutoMode] Task ${taskEvent.taskId} completed for ${event.featureId} (${taskEvent.tasksCompleted}/${taskEvent.tasksTotal})`
|
||||
);
|
||||
addAutoModeActivity({
|
||||
@@ -302,7 +305,7 @@ export function useAutoMode() {
|
||||
AutoModeEvent,
|
||||
{ type: 'auto_mode_phase_complete' }
|
||||
>;
|
||||
console.log(
|
||||
logger.debug(
|
||||
`[AutoMode] Phase ${phaseEvent.phaseNumber} completed for ${event.featureId}`
|
||||
);
|
||||
addAutoModeActivity({
|
||||
@@ -330,18 +333,18 @@ export function useAutoMode() {
|
||||
// Start auto mode - UI only, feature pickup is handled in board-view.tsx
|
||||
const start = useCallback(() => {
|
||||
if (!currentProject) {
|
||||
console.error('No project selected');
|
||||
logger.error('No project selected');
|
||||
return;
|
||||
}
|
||||
|
||||
setAutoModeRunning(currentProject.id, true);
|
||||
console.log(`[AutoMode] Started with maxConcurrency: ${maxConcurrency}`);
|
||||
logger.debug(`[AutoMode] Started with maxConcurrency: ${maxConcurrency}`);
|
||||
}, [currentProject, setAutoModeRunning, maxConcurrency]);
|
||||
|
||||
// Stop auto mode - UI only, running tasks continue until natural completion
|
||||
const stop = useCallback(() => {
|
||||
if (!currentProject) {
|
||||
console.error('No project selected');
|
||||
logger.error('No project selected');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -350,14 +353,14 @@ export function useAutoMode() {
|
||||
// Stopping auto mode only turns off the toggle to prevent new features
|
||||
// from being picked up. Running tasks will complete naturally and be
|
||||
// removed via the auto_mode_feature_complete event.
|
||||
console.log('[AutoMode] Stopped - running tasks will continue');
|
||||
logger.info('Stopped - running tasks will continue');
|
||||
}, [currentProject, setAutoModeRunning]);
|
||||
|
||||
// Stop a specific feature
|
||||
const stopFeature = useCallback(
|
||||
async (featureId: string) => {
|
||||
if (!currentProject) {
|
||||
console.error('No project selected');
|
||||
logger.error('No project selected');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -371,7 +374,7 @@ export function useAutoMode() {
|
||||
|
||||
if (result.success) {
|
||||
removeRunningTask(currentProject.id, featureId);
|
||||
console.log('[AutoMode] Feature stopped successfully:', featureId);
|
||||
logger.info('Feature stopped successfully:', featureId);
|
||||
addAutoModeActivity({
|
||||
featureId,
|
||||
type: 'complete',
|
||||
@@ -379,11 +382,11 @@ export function useAutoMode() {
|
||||
passes: false,
|
||||
});
|
||||
} else {
|
||||
console.error('[AutoMode] Failed to stop feature:', result.error);
|
||||
logger.error('Failed to stop feature:', result.error);
|
||||
throw new Error(result.error || 'Failed to stop feature');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[AutoMode] Error stopping feature:', error);
|
||||
logger.error('Error stopping feature:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const logger = createLogger('BoardBackground');
|
||||
|
||||
/**
|
||||
* Hook for managing board background settings with automatic persistence to server
|
||||
*/
|
||||
@@ -19,11 +22,11 @@ export function useBoardBackgroundSettings() {
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
console.error('Failed to persist settings:', result.error);
|
||||
logger.error('Failed to persist settings:', result.error);
|
||||
toast.error('Failed to save settings');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to persist settings:', error);
|
||||
logger.error('Failed to persist settings:', error);
|
||||
toast.error('Failed to save settings');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -4,6 +4,9 @@ import { useMessageQueue } from './use-message-queue';
|
||||
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { sanitizeFilename } from '@/lib/image-utils';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('ElectronAgent');
|
||||
|
||||
interface UseElectronAgentOptions {
|
||||
sessionId: string;
|
||||
@@ -93,7 +96,7 @@ export function useElectronAgent({
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
console.log('[useElectronAgent] Sending message directly', {
|
||||
logger.info('Sending message directly', {
|
||||
hasImages: images && images.length > 0,
|
||||
imageCount: images?.length || 0,
|
||||
hasTextFiles: textFiles && textFiles.length > 0,
|
||||
@@ -123,9 +126,9 @@ export function useElectronAgent({
|
||||
);
|
||||
if (result.success && result.path) {
|
||||
imagePaths.push(result.path);
|
||||
console.log('[useElectronAgent] Saved image to .automaker/images:', result.path);
|
||||
logger.info('Saved image to .automaker/images:', result.path);
|
||||
} else {
|
||||
console.error('[useElectronAgent] Failed to save image:', result.error);
|
||||
logger.error('Failed to save image:', result.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,7 +149,7 @@ export function useElectronAgent({
|
||||
// Note: We don't set isProcessing to false here because
|
||||
// it will be set by the "complete" or "error" stream event
|
||||
} catch (err) {
|
||||
console.error('[useElectronAgent] Failed to send message:', err);
|
||||
logger.error('Failed to send message:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to send message');
|
||||
setIsProcessing(false);
|
||||
throw err;
|
||||
@@ -191,13 +194,13 @@ export function useElectronAgent({
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
console.log('[useElectronAgent] Starting session:', sessionId);
|
||||
logger.info('Starting session:', sessionId);
|
||||
const result = await api.agent!.start(sessionId, workingDirectory);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (result.success && result.messages) {
|
||||
console.log('[useElectronAgent] Loaded', result.messages.length, 'messages');
|
||||
logger.info('Loaded', result.messages.length, 'messages');
|
||||
setMessages(result.messages);
|
||||
setIsConnected(true);
|
||||
|
||||
@@ -205,7 +208,7 @@ export function useElectronAgent({
|
||||
const historyResult = await api.agent!.getHistory(sessionId);
|
||||
if (mounted && historyResult.success) {
|
||||
const isRunning = historyResult.isRunning || false;
|
||||
console.log('[useElectronAgent] Session running state:', isRunning);
|
||||
logger.info('Session running state:', isRunning);
|
||||
setIsProcessing(isRunning);
|
||||
}
|
||||
} else {
|
||||
@@ -214,7 +217,7 @@ export function useElectronAgent({
|
||||
}
|
||||
} catch (err) {
|
||||
if (!mounted) return;
|
||||
console.error('[useElectronAgent] Failed to initialize:', err);
|
||||
logger.error('Failed to initialize:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to initialize');
|
||||
setIsProcessing(false);
|
||||
}
|
||||
@@ -230,7 +233,7 @@ export function useElectronAgent({
|
||||
// Auto-process queue when agent finishes processing
|
||||
useEffect(() => {
|
||||
if (!isProcessing && !isProcessingQueue && queuedMessages.length > 0) {
|
||||
console.log('[useElectronAgent] Auto-processing next queued message');
|
||||
logger.info('Auto-processing next queued message');
|
||||
processNext();
|
||||
}
|
||||
}, [isProcessing, isProcessingQueue, queuedMessages.length, processNext]);
|
||||
@@ -241,21 +244,21 @@ export function useElectronAgent({
|
||||
if (!api?.agent) return;
|
||||
if (!sessionId) return; // Don't subscribe if no session
|
||||
|
||||
console.log('[useElectronAgent] Subscribing to stream events for session:', sessionId);
|
||||
logger.info('Subscribing to stream events for session:', sessionId);
|
||||
|
||||
const handleStream = (event: StreamEvent) => {
|
||||
// CRITICAL: Only process events for our specific session
|
||||
if (event.sessionId !== sessionId) {
|
||||
console.log('[useElectronAgent] Ignoring event for different session:', event.sessionId);
|
||||
logger.info('Ignoring event for different session:', event.sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[useElectronAgent] Stream event for', sessionId, ':', event.type);
|
||||
logger.info('Stream event for', sessionId, ':', event.type);
|
||||
|
||||
switch (event.type) {
|
||||
case 'started':
|
||||
// Agent started processing (including from queue)
|
||||
console.log('[useElectronAgent] Agent started processing for session:', sessionId);
|
||||
logger.info('Agent started processing for session:', sessionId);
|
||||
setIsProcessing(true);
|
||||
break;
|
||||
|
||||
@@ -300,13 +303,13 @@ export function useElectronAgent({
|
||||
|
||||
case 'tool_use':
|
||||
// Tool being used
|
||||
console.log('[useElectronAgent] Tool use:', event.tool.name);
|
||||
logger.info('Tool use:', event.tool.name);
|
||||
onToolUse?.(event.tool.name, event.tool.input);
|
||||
break;
|
||||
|
||||
case 'complete':
|
||||
// Agent finished processing for THIS session
|
||||
console.log('[useElectronAgent] Processing complete for session:', sessionId);
|
||||
logger.info('Processing complete for session:', sessionId);
|
||||
setIsProcessing(false);
|
||||
if (event.messageId) {
|
||||
setMessages((prev) =>
|
||||
@@ -319,7 +322,7 @@ export function useElectronAgent({
|
||||
|
||||
case 'error':
|
||||
// Error occurred for THIS session
|
||||
console.error('[useElectronAgent] Agent error for session:', sessionId, event.error);
|
||||
logger.error('Agent error for session:', sessionId, event.error);
|
||||
setIsProcessing(false);
|
||||
setError(event.error);
|
||||
if (event.message) {
|
||||
@@ -330,13 +333,13 @@ export function useElectronAgent({
|
||||
|
||||
case 'queue_updated':
|
||||
// Server queue was updated
|
||||
console.log('[useElectronAgent] Queue updated:', event.queue);
|
||||
logger.info('Queue updated:', event.queue);
|
||||
setServerQueue(event.queue || []);
|
||||
break;
|
||||
|
||||
case 'queue_error':
|
||||
// Error processing a queued prompt
|
||||
console.error('[useElectronAgent] Queue error:', event.error);
|
||||
logger.error('Queue error:', event.error);
|
||||
setError(event.error);
|
||||
break;
|
||||
}
|
||||
@@ -346,7 +349,7 @@ export function useElectronAgent({
|
||||
|
||||
return () => {
|
||||
if (unsubscribeRef.current) {
|
||||
console.log('[useElectronAgent] Unsubscribing from stream events for session:', sessionId);
|
||||
logger.info('Unsubscribing from stream events for session:', sessionId);
|
||||
unsubscribeRef.current();
|
||||
unsubscribeRef.current = null;
|
||||
}
|
||||
@@ -363,7 +366,7 @@ export function useElectronAgent({
|
||||
}
|
||||
|
||||
if (isProcessing) {
|
||||
console.warn('[useElectronAgent] Already processing a message');
|
||||
logger.warn('Already processing a message');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -371,7 +374,7 @@ export function useElectronAgent({
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
console.log('[useElectronAgent] Sending message', {
|
||||
logger.info('Sending message', {
|
||||
hasImages: images && images.length > 0,
|
||||
imageCount: images?.length || 0,
|
||||
hasTextFiles: textFiles && textFiles.length > 0,
|
||||
@@ -401,9 +404,9 @@ export function useElectronAgent({
|
||||
);
|
||||
if (result.success && result.path) {
|
||||
imagePaths.push(result.path);
|
||||
console.log('[useElectronAgent] Saved image to .automaker/images:', result.path);
|
||||
logger.info('Saved image to .automaker/images:', result.path);
|
||||
} else {
|
||||
console.error('[useElectronAgent] Failed to save image:', result.error);
|
||||
logger.error('Failed to save image:', result.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,7 +427,7 @@ export function useElectronAgent({
|
||||
// Note: We don't set isProcessing to false here because
|
||||
// it will be set by the "complete" or "error" stream event
|
||||
} catch (err) {
|
||||
console.error('[useElectronAgent] Failed to send message:', err);
|
||||
logger.error('Failed to send message:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to send message');
|
||||
setIsProcessing(false);
|
||||
}
|
||||
@@ -441,7 +444,7 @@ export function useElectronAgent({
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[useElectronAgent] Stopping execution');
|
||||
logger.info('Stopping execution');
|
||||
const result = await api.agent!.stop(sessionId);
|
||||
|
||||
if (!result.success) {
|
||||
@@ -450,7 +453,7 @@ export function useElectronAgent({
|
||||
setIsProcessing(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[useElectronAgent] Failed to stop:', err);
|
||||
logger.error('Failed to stop:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to stop execution');
|
||||
}
|
||||
}, [sessionId]);
|
||||
@@ -464,7 +467,7 @@ export function useElectronAgent({
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[useElectronAgent] Clearing history');
|
||||
logger.info('Clearing history');
|
||||
const result = await api.agent!.clear(sessionId);
|
||||
|
||||
if (result.success) {
|
||||
@@ -474,7 +477,7 @@ export function useElectronAgent({
|
||||
setError(result.error || 'Failed to clear history');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[useElectronAgent] Failed to clear:', err);
|
||||
logger.error('Failed to clear:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to clear history');
|
||||
}
|
||||
}, [sessionId]);
|
||||
@@ -516,14 +519,14 @@ export function useElectronAgent({
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[useElectronAgent] Adding to server queue');
|
||||
logger.info('Adding to server queue');
|
||||
const result = await api.agent.queueAdd(sessionId, messageContent, imagePaths, model);
|
||||
|
||||
if (!result.success) {
|
||||
setError(result.error || 'Failed to add to queue');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[useElectronAgent] Failed to add to queue:', err);
|
||||
logger.error('Failed to add to queue:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to add to queue');
|
||||
}
|
||||
},
|
||||
@@ -540,14 +543,14 @@ export function useElectronAgent({
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[useElectronAgent] Removing from server queue:', promptId);
|
||||
logger.info('Removing from server queue:', promptId);
|
||||
const result = await api.agent.queueRemove(sessionId, promptId);
|
||||
|
||||
if (!result.success) {
|
||||
setError(result.error || 'Failed to remove from queue');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[useElectronAgent] Failed to remove from queue:', err);
|
||||
logger.error('Failed to remove from queue:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to remove from queue');
|
||||
}
|
||||
},
|
||||
@@ -563,14 +566,14 @@ export function useElectronAgent({
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[useElectronAgent] Clearing server queue');
|
||||
logger.info('Clearing server queue');
|
||||
const result = await api.agent.queueClear(sessionId);
|
||||
|
||||
if (!result.success) {
|
||||
setError(result.error || 'Failed to clear queue');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[useElectronAgent] Failed to clear queue:', err);
|
||||
logger.error('Failed to clear queue:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to clear queue');
|
||||
}
|
||||
}, [sessionId]);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import type { ImageAttachment, TextFileAttachment } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('MessageQueue');
|
||||
|
||||
export interface QueuedMessage {
|
||||
id: string;
|
||||
content: string;
|
||||
@@ -72,7 +75,7 @@ export function useMessageQueue({ onProcessNext }: UseMessageQueueOptions): UseM
|
||||
// Remove the processed message from queue
|
||||
setQueuedMessages((prev) => prev.slice(1));
|
||||
} catch (error) {
|
||||
console.error('Error processing queued message:', error);
|
||||
logger.error('Error processing queued message:', error);
|
||||
// Keep the message in queue for retry or manual removal
|
||||
} finally {
|
||||
setIsProcessingQueue(false);
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
*/
|
||||
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getHttpApiClient, waitForApiKeyInit } from '@/lib/http-api-client';
|
||||
import { isElectron } from '@/lib/electron';
|
||||
import { getItem, removeItem } from '@/lib/storage';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
const logger = createLogger('SettingsMigration');
|
||||
|
||||
/**
|
||||
* State returned by useSettingsMigration hook
|
||||
*/
|
||||
@@ -109,7 +112,7 @@ export function useSettingsMigration(): MigrationState {
|
||||
const status = await api.settings.getStatus();
|
||||
|
||||
if (!status.success) {
|
||||
console.error('[Settings Migration] Failed to get status:', status);
|
||||
logger.error('Failed to get status:', status);
|
||||
setState({
|
||||
checked: true,
|
||||
migrated: false,
|
||||
@@ -120,7 +123,7 @@ export function useSettingsMigration(): MigrationState {
|
||||
|
||||
// If settings files already exist, no migration needed
|
||||
if (!status.needsMigration) {
|
||||
console.log('[Settings Migration] Settings files exist, no migration needed');
|
||||
logger.info('Settings files exist, no migration needed');
|
||||
setState({ checked: true, migrated: false, error: null });
|
||||
return;
|
||||
}
|
||||
@@ -128,12 +131,12 @@ export function useSettingsMigration(): MigrationState {
|
||||
// Check if we have localStorage data to migrate
|
||||
const automakerStorage = getItem('automaker-storage');
|
||||
if (!automakerStorage) {
|
||||
console.log('[Settings Migration] No localStorage data to migrate');
|
||||
logger.info('No localStorage data to migrate');
|
||||
setState({ checked: true, migrated: false, error: null });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Settings Migration] Starting migration...');
|
||||
logger.info('Starting migration...');
|
||||
|
||||
// Collect all localStorage data
|
||||
const localStorageData: Record<string, string> = {};
|
||||
@@ -148,7 +151,7 @@ export function useSettingsMigration(): MigrationState {
|
||||
const result = await api.settings.migrate(localStorageData);
|
||||
|
||||
if (result.success) {
|
||||
console.log('[Settings Migration] Migration successful:', {
|
||||
logger.info('Migration successful:', {
|
||||
globalSettings: result.migratedGlobalSettings,
|
||||
credentials: result.migratedCredentials,
|
||||
projects: result.migratedProjectCount,
|
||||
@@ -161,7 +164,7 @@ export function useSettingsMigration(): MigrationState {
|
||||
|
||||
setState({ checked: true, migrated: true, error: null });
|
||||
} else {
|
||||
console.warn('[Settings Migration] Migration had errors:', result.errors);
|
||||
logger.warn('Migration had errors:', result.errors);
|
||||
setState({
|
||||
checked: true,
|
||||
migrated: false,
|
||||
@@ -169,7 +172,7 @@ export function useSettingsMigration(): MigrationState {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Settings Migration] Migration failed:', error);
|
||||
logger.error('Migration failed:', error);
|
||||
setState({
|
||||
checked: true,
|
||||
migrated: false,
|
||||
@@ -244,7 +247,7 @@ export async function syncSettingsToServer(): Promise<boolean> {
|
||||
const result = await api.settings.updateGlobal(updates);
|
||||
return result.success;
|
||||
} catch (error) {
|
||||
console.error('[Settings Sync] Failed to sync settings:', error);
|
||||
logger.error('Failed to sync settings:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -271,7 +274,7 @@ export async function syncCredentialsToServer(apiKeys: {
|
||||
const result = await api.settings.updateCredentials({ apiKeys });
|
||||
return result.success;
|
||||
} catch (error) {
|
||||
console.error('[Settings Sync] Failed to sync credentials:', error);
|
||||
logger.error('Failed to sync credentials:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -312,7 +315,7 @@ export async function syncProjectSettingsToServer(
|
||||
const result = await api.settings.updateProject(projectPath, updates);
|
||||
return result.success;
|
||||
} catch (error) {
|
||||
console.error('[Settings Sync] Failed to sync project settings:', error);
|
||||
logger.error('Failed to sync project settings:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -332,7 +335,7 @@ export async function loadMCPServersFromServer(): Promise<boolean> {
|
||||
const result = await api.settings.getGlobal();
|
||||
|
||||
if (!result.success || !result.settings) {
|
||||
console.error('[Settings Load] Failed to load settings:', result.error);
|
||||
logger.error('Failed to load settings:', result.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -344,10 +347,10 @@ export async function loadMCPServersFromServer(): Promise<boolean> {
|
||||
// We need to update the store directly since we can't use hooks here
|
||||
useAppStore.setState({ mcpServers, mcpAutoApproveTools, mcpUnrestrictedTools });
|
||||
|
||||
console.log(`[Settings Load] Loaded ${mcpServers.length} MCP servers from server`);
|
||||
logger.info(`Loaded ${mcpServers.length} MCP servers from server`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[Settings Load] Failed to load MCP servers:', error);
|
||||
logger.error('Failed to load MCP servers:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Type definitions for Electron IPC API
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import type { SessionListItem, Message } from '@/types/electron';
|
||||
import type { ClaudeUsageResponse } from '@/store/app-store';
|
||||
import type {
|
||||
@@ -94,6 +95,8 @@ import type {
|
||||
ProviderStatus,
|
||||
} from '@/types/electron';
|
||||
|
||||
const logger = createLogger('Electron');
|
||||
|
||||
// Import HTTP API client (ES module)
|
||||
import { getHttpApiClient, getServerUrlSync } from './http-api-client';
|
||||
|
||||
@@ -774,8 +777,8 @@ export const getCurrentApiMode = (): 'http' => {
|
||||
// Debug helpers
|
||||
if (typeof window !== 'undefined') {
|
||||
(window as any).__checkApiMode = () => {
|
||||
console.log('Current API mode:', getCurrentApiMode());
|
||||
console.log('isElectron():', isElectron());
|
||||
logger.info('Current API mode:', getCurrentApiMode());
|
||||
logger.info('isElectron():', isElectron());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1016,7 +1019,7 @@ const getMockElectronAPI = (): ElectronAPI => {
|
||||
// Store the image data in mock file system for testing
|
||||
mockFileSystem[tempFilePath] = data;
|
||||
|
||||
console.log('[Mock] Saved image to temp:', tempFilePath);
|
||||
logger.info('Mock saved image to temp:', tempFilePath);
|
||||
return { success: true, path: tempFilePath };
|
||||
},
|
||||
|
||||
@@ -1061,7 +1064,7 @@ const getMockElectronAPI = (): ElectronAPI => {
|
||||
// Mock Claude API
|
||||
claude: {
|
||||
getUsage: async () => {
|
||||
console.log('[Mock] Getting Claude usage');
|
||||
logger.info('Mock getting Claude usage');
|
||||
return {
|
||||
sessionTokensUsed: 0,
|
||||
sessionLimit: 0,
|
||||
@@ -1168,7 +1171,7 @@ interface SetupAPI {
|
||||
function createMockSetupAPI(): SetupAPI {
|
||||
return {
|
||||
getClaudeStatus: async () => {
|
||||
console.log('[Mock] Getting Claude status');
|
||||
logger.info('Mock Getting Claude status');
|
||||
return {
|
||||
success: true,
|
||||
status: 'not_installed',
|
||||
@@ -1185,7 +1188,7 @@ function createMockSetupAPI(): SetupAPI {
|
||||
},
|
||||
|
||||
installClaude: async () => {
|
||||
console.log('[Mock] Installing Claude CLI');
|
||||
logger.info('Mock Installing Claude CLI');
|
||||
// Simulate installation delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
return {
|
||||
@@ -1196,7 +1199,7 @@ function createMockSetupAPI(): SetupAPI {
|
||||
},
|
||||
|
||||
authClaude: async () => {
|
||||
console.log('[Mock] Auth Claude CLI');
|
||||
logger.info('Mock Auth Claude CLI');
|
||||
return {
|
||||
success: true,
|
||||
requiresManualAuth: true,
|
||||
@@ -1205,13 +1208,13 @@ function createMockSetupAPI(): SetupAPI {
|
||||
},
|
||||
|
||||
storeApiKey: async (provider: string, apiKey: string) => {
|
||||
console.log('[Mock] Storing API key for:', provider);
|
||||
logger.info('Mock Storing API key for:', provider);
|
||||
// In mock mode, we just pretend to store it (it's already in the app store)
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
getApiKeys: async () => {
|
||||
console.log('[Mock] Getting API keys');
|
||||
logger.info('Mock Getting API keys');
|
||||
return {
|
||||
success: true,
|
||||
hasAnthropicKey: false,
|
||||
@@ -1220,7 +1223,7 @@ function createMockSetupAPI(): SetupAPI {
|
||||
},
|
||||
|
||||
deleteApiKey: async (provider: string) => {
|
||||
console.log('[Mock] Deleting API key for:', provider);
|
||||
logger.info('Mock Deleting API key for:', provider);
|
||||
return { success: true, message: `API key for ${provider} deleted` };
|
||||
},
|
||||
|
||||
@@ -1237,8 +1240,8 @@ function createMockSetupAPI(): SetupAPI {
|
||||
},
|
||||
|
||||
verifyClaudeAuth: async (authMethod?: 'cli' | 'api_key', apiKey?: string) => {
|
||||
console.log(
|
||||
'[Mock] Verifying Claude auth with method:',
|
||||
logger.info(
|
||||
'Mock verifying Claude auth with method:',
|
||||
authMethod,
|
||||
apiKey ? '(with key)' : ''
|
||||
);
|
||||
@@ -1251,7 +1254,7 @@ function createMockSetupAPI(): SetupAPI {
|
||||
},
|
||||
|
||||
getGhStatus: async () => {
|
||||
console.log('[Mock] Getting GitHub CLI status');
|
||||
logger.info('Mock Getting GitHub CLI status');
|
||||
return {
|
||||
success: true,
|
||||
installed: false,
|
||||
@@ -1278,7 +1281,7 @@ function createMockSetupAPI(): SetupAPI {
|
||||
function createMockWorktreeAPI(): WorktreeAPI {
|
||||
return {
|
||||
mergeFeature: async (projectPath: string, featureId: string, options?: object) => {
|
||||
console.log('[Mock] Merging feature:', {
|
||||
logger.info('Mock Merging feature:', {
|
||||
projectPath,
|
||||
featureId,
|
||||
options,
|
||||
@@ -1287,7 +1290,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
getInfo: async (projectPath: string, featureId: string) => {
|
||||
console.log('[Mock] Getting worktree info:', { projectPath, featureId });
|
||||
logger.info('Mock Getting worktree info:', { projectPath, featureId });
|
||||
return {
|
||||
success: true,
|
||||
worktreePath: `/mock/worktrees/${featureId}`,
|
||||
@@ -1297,7 +1300,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
getStatus: async (projectPath: string, featureId: string) => {
|
||||
console.log('[Mock] Getting worktree status:', {
|
||||
logger.info('Mock Getting worktree status:', {
|
||||
projectPath,
|
||||
featureId,
|
||||
});
|
||||
@@ -1311,12 +1314,12 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
list: async (projectPath: string) => {
|
||||
console.log('[Mock] Listing worktrees:', { projectPath });
|
||||
logger.info('Mock Listing worktrees:', { projectPath });
|
||||
return { success: true, worktrees: [] };
|
||||
},
|
||||
|
||||
listAll: async (projectPath: string, includeDetails?: boolean) => {
|
||||
console.log('[Mock] Listing all worktrees:', {
|
||||
logger.info('Mock Listing all worktrees:', {
|
||||
projectPath,
|
||||
includeDetails,
|
||||
});
|
||||
@@ -1337,7 +1340,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
create: async (projectPath: string, branchName: string, baseBranch?: string) => {
|
||||
console.log('[Mock] Creating worktree:', {
|
||||
logger.info('Mock Creating worktree:', {
|
||||
projectPath,
|
||||
branchName,
|
||||
baseBranch,
|
||||
@@ -1353,7 +1356,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
delete: async (projectPath: string, worktreePath: string, deleteBranch?: boolean) => {
|
||||
console.log('[Mock] Deleting worktree:', {
|
||||
logger.info('Mock Deleting worktree:', {
|
||||
projectPath,
|
||||
worktreePath,
|
||||
deleteBranch,
|
||||
@@ -1368,7 +1371,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
commit: async (worktreePath: string, message: string) => {
|
||||
console.log('[Mock] Committing changes:', { worktreePath, message });
|
||||
logger.info('Mock Committing changes:', { worktreePath, message });
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1381,7 +1384,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
push: async (worktreePath: string, force?: boolean) => {
|
||||
console.log('[Mock] Pushing worktree:', { worktreePath, force });
|
||||
logger.info('Mock Pushing worktree:', { worktreePath, force });
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1393,7 +1396,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
createPR: async (worktreePath: string, options?: any) => {
|
||||
console.log('[Mock] Creating PR:', { worktreePath, options });
|
||||
logger.info('Mock Creating PR:', { worktreePath, options });
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1408,7 +1411,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
getDiffs: async (projectPath: string, featureId: string) => {
|
||||
console.log('[Mock] Getting file diffs:', { projectPath, featureId });
|
||||
logger.info('Mock Getting file diffs:', { projectPath, featureId });
|
||||
return {
|
||||
success: true,
|
||||
diff: "diff --git a/src/feature.ts b/src/feature.ts\n+++ new file\n@@ -0,0 +1,10 @@\n+export function feature() {\n+ return 'hello';\n+}",
|
||||
@@ -1421,7 +1424,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
getFileDiff: async (projectPath: string, featureId: string, filePath: string) => {
|
||||
console.log('[Mock] Getting file diff:', {
|
||||
logger.info('Mock Getting file diff:', {
|
||||
projectPath,
|
||||
featureId,
|
||||
filePath,
|
||||
@@ -1434,7 +1437,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
pull: async (worktreePath: string) => {
|
||||
console.log('[Mock] Pulling latest changes for:', worktreePath);
|
||||
logger.info('Mock Pulling latest changes for:', worktreePath);
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1446,7 +1449,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
checkoutBranch: async (worktreePath: string, branchName: string) => {
|
||||
console.log('[Mock] Creating and checking out branch:', {
|
||||
logger.info('Mock Creating and checking out branch:', {
|
||||
worktreePath,
|
||||
branchName,
|
||||
});
|
||||
@@ -1461,7 +1464,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
listBranches: async (worktreePath: string) => {
|
||||
console.log('[Mock] Listing branches for:', worktreePath);
|
||||
logger.info('Mock Listing branches for:', worktreePath);
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1478,7 +1481,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
switchBranch: async (worktreePath: string, branchName: string) => {
|
||||
console.log('[Mock] Switching to branch:', { worktreePath, branchName });
|
||||
logger.info('Mock Switching to branch:', { worktreePath, branchName });
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1490,7 +1493,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
openInEditor: async (worktreePath: string) => {
|
||||
console.log('[Mock] Opening in editor:', worktreePath);
|
||||
logger.info('Mock Opening in editor:', worktreePath);
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1501,7 +1504,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
getDefaultEditor: async () => {
|
||||
console.log('[Mock] Getting default editor');
|
||||
logger.info('Mock Getting default editor');
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1512,7 +1515,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
initGit: async (projectPath: string) => {
|
||||
console.log('[Mock] Initializing git:', projectPath);
|
||||
logger.info('Mock Initializing git:', projectPath);
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1523,7 +1526,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
startDevServer: async (projectPath: string, worktreePath: string) => {
|
||||
console.log('[Mock] Starting dev server:', { projectPath, worktreePath });
|
||||
logger.info('Mock Starting dev server:', { projectPath, worktreePath });
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1536,7 +1539,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
stopDevServer: async (worktreePath: string) => {
|
||||
console.log('[Mock] Stopping dev server:', worktreePath);
|
||||
logger.info('Mock Stopping dev server:', worktreePath);
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1547,7 +1550,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
listDevServers: async () => {
|
||||
console.log('[Mock] Listing dev servers');
|
||||
logger.info('Mock Listing dev servers');
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1557,7 +1560,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
},
|
||||
|
||||
getPRInfo: async (worktreePath: string, branchName: string) => {
|
||||
console.log('[Mock] Getting PR info:', { worktreePath, branchName });
|
||||
logger.info('Mock Getting PR info:', { worktreePath, branchName });
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
@@ -1573,7 +1576,7 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
||||
function createMockGitAPI(): GitAPI {
|
||||
return {
|
||||
getDiffs: async (projectPath: string) => {
|
||||
console.log('[Mock] Getting git diffs for project:', { projectPath });
|
||||
logger.info('Mock Getting git diffs for project:', { projectPath });
|
||||
return {
|
||||
success: true,
|
||||
diff: "diff --git a/src/feature.ts b/src/feature.ts\n+++ new file\n@@ -0,0 +1,10 @@\n+export function feature() {\n+ return 'hello';\n+}",
|
||||
@@ -1586,7 +1589,7 @@ function createMockGitAPI(): GitAPI {
|
||||
},
|
||||
|
||||
getFileDiff: async (projectPath: string, filePath: string) => {
|
||||
console.log('[Mock] Getting git file diff:', { projectPath, filePath });
|
||||
logger.info('Mock Getting git file diff:', { projectPath, filePath });
|
||||
return {
|
||||
success: true,
|
||||
diff: `diff --git a/${filePath} b/${filePath}\n+++ new file\n@@ -0,0 +1,5 @@\n+// New content`,
|
||||
@@ -1610,7 +1613,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
||||
}
|
||||
|
||||
mockAutoModeRunning = true;
|
||||
console.log(`[Mock] Auto mode started with maxConcurrency: ${maxConcurrency || 3}`);
|
||||
logger.info(`Mock auto mode started with maxConcurrency: ${maxConcurrency || 3}`);
|
||||
const featureId = 'auto-mode-0';
|
||||
mockRunningFeatures.add(featureId);
|
||||
|
||||
@@ -1679,8 +1682,8 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
||||
};
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[Mock] Running feature ${featureId} with useWorktrees: ${useWorktrees}, worktreePath: ${worktreePath}`
|
||||
logger.info(
|
||||
`Mock running feature ${featureId} with useWorktrees: ${useWorktrees}, worktreePath: ${worktreePath}`
|
||||
);
|
||||
mockRunningFeatures.add(featureId);
|
||||
simulateAutoModeLoop(projectPath, featureId);
|
||||
@@ -1847,7 +1850,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
||||
};
|
||||
}
|
||||
|
||||
console.log('[Mock] Follow-up feature:', {
|
||||
logger.info('Mock Follow-up feature:', {
|
||||
featureId,
|
||||
prompt,
|
||||
imagePaths,
|
||||
@@ -1864,7 +1867,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
||||
},
|
||||
|
||||
commitFeature: async (projectPath: string, featureId: string, worktreePath?: string) => {
|
||||
console.log('[Mock] Committing feature:', {
|
||||
logger.info('Mock Committing feature:', {
|
||||
projectPath,
|
||||
featureId,
|
||||
worktreePath,
|
||||
@@ -1909,7 +1912,7 @@ function createMockAutoModeAPI(): AutoModeAPI {
|
||||
editedPlan?: string,
|
||||
feedback?: string
|
||||
) => {
|
||||
console.log('[Mock] Plan approval:', {
|
||||
logger.info('Mock Plan approval:', {
|
||||
projectPath,
|
||||
featureId,
|
||||
approved,
|
||||
@@ -2070,7 +2073,7 @@ function createMockSuggestionsAPI(): SuggestionsAPI {
|
||||
}
|
||||
|
||||
mockSuggestionsRunning = true;
|
||||
console.log(`[Mock] Generating ${suggestionType} suggestions for: ${projectPath}`);
|
||||
logger.info(`Mock generating ${suggestionType} suggestions for: ${projectPath}`);
|
||||
|
||||
// Simulate async suggestion generation
|
||||
simulateSuggestionsGeneration(suggestionType);
|
||||
@@ -2294,8 +2297,8 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
||||
}
|
||||
|
||||
mockSpecRegenerationRunning = true;
|
||||
console.log(
|
||||
`[Mock] Creating initial spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
||||
logger.info(
|
||||
`Mock creating initial spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
||||
);
|
||||
|
||||
// Simulate async spec creation
|
||||
@@ -2319,8 +2322,8 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
||||
}
|
||||
|
||||
mockSpecRegenerationRunning = true;
|
||||
console.log(
|
||||
`[Mock] Regenerating spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
||||
logger.info(
|
||||
`Mock regenerating spec for: ${projectPath}, generateFeatures: ${generateFeatures}, maxFeatures: ${maxFeatures}`
|
||||
);
|
||||
|
||||
// Simulate async spec regeneration
|
||||
@@ -2338,8 +2341,8 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
||||
}
|
||||
|
||||
mockSpecRegenerationRunning = true;
|
||||
console.log(
|
||||
`[Mock] Generating features from existing spec for: ${projectPath}, maxFeatures: ${maxFeatures}`
|
||||
logger.info(
|
||||
`Mock generating features from existing spec for: ${projectPath}, maxFeatures: ${maxFeatures}`
|
||||
);
|
||||
|
||||
// Simulate async feature generation
|
||||
@@ -2604,7 +2607,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
// Store features in mock file system using features/{id}/feature.json pattern
|
||||
return {
|
||||
getAll: async (projectPath: string) => {
|
||||
console.log('[Mock] Getting all features for:', projectPath);
|
||||
logger.info('Mock Getting all features for:', projectPath);
|
||||
|
||||
// Check if test has set mock features via global variable
|
||||
const testFeatures = (window as any).__mockFeatures;
|
||||
@@ -2629,7 +2632,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
features.push(feature);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Mock] Failed to parse feature:', error);
|
||||
logger.error('Mock Failed to parse feature:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2642,7 +2645,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
},
|
||||
|
||||
get: async (projectPath: string, featureId: string) => {
|
||||
console.log('[Mock] Getting feature:', { projectPath, featureId });
|
||||
logger.info('Mock Getting feature:', { projectPath, featureId });
|
||||
const featurePath = `${projectPath}/.automaker/features/${featureId}/feature.json`;
|
||||
const content = mockFileSystem[featurePath];
|
||||
if (content) {
|
||||
@@ -2652,7 +2655,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
},
|
||||
|
||||
create: async (projectPath: string, feature: Feature) => {
|
||||
console.log('[Mock] Creating feature:', {
|
||||
logger.info('Mock Creating feature:', {
|
||||
projectPath,
|
||||
featureId: feature.id,
|
||||
});
|
||||
@@ -2662,7 +2665,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
},
|
||||
|
||||
update: async (projectPath: string, featureId: string, updates: Partial<Feature>) => {
|
||||
console.log('[Mock] Updating feature:', {
|
||||
logger.info('Mock Updating feature:', {
|
||||
projectPath,
|
||||
featureId,
|
||||
updates,
|
||||
@@ -2678,7 +2681,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
},
|
||||
|
||||
delete: async (projectPath: string, featureId: string) => {
|
||||
console.log('[Mock] Deleting feature:', { projectPath, featureId });
|
||||
logger.info('Mock Deleting feature:', { projectPath, featureId });
|
||||
const featurePath = `${projectPath}/.automaker/features/${featureId}/feature.json`;
|
||||
delete mockFileSystem[featurePath];
|
||||
// Also delete agent-output.md if it exists
|
||||
@@ -2688,14 +2691,14 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
},
|
||||
|
||||
getAgentOutput: async (projectPath: string, featureId: string) => {
|
||||
console.log('[Mock] Getting agent output:', { projectPath, featureId });
|
||||
logger.info('Mock Getting agent output:', { projectPath, featureId });
|
||||
const agentOutputPath = `${projectPath}/.automaker/features/${featureId}/agent-output.md`;
|
||||
const content = mockFileSystem[agentOutputPath];
|
||||
return { success: true, content: content || null };
|
||||
},
|
||||
|
||||
generateTitle: async (description: string) => {
|
||||
console.log('[Mock] Generating title for:', description.substring(0, 50));
|
||||
logger.info('Mock Generating title for:', description.substring(0, 50));
|
||||
// Mock title generation - just take first few words
|
||||
const words = description.split(/\s+/).slice(0, 6).join(' ');
|
||||
const title = words.length > 40 ? words.substring(0, 40) + '...' : words;
|
||||
@@ -2708,7 +2711,7 @@ function createMockFeaturesAPI(): FeaturesAPI {
|
||||
function createMockRunningAgentsAPI(): RunningAgentsAPI {
|
||||
return {
|
||||
getAll: async () => {
|
||||
console.log('[Mock] Getting all running agents');
|
||||
logger.info('Mock Getting all running agents');
|
||||
// Return running agents from mock auto mode state
|
||||
const runningAgents: RunningAgent[] = Array.from(mockRunningFeatures).map((featureId) => ({
|
||||
featureId,
|
||||
@@ -2733,7 +2736,7 @@ let mockValidationCallbacks: ((event: IssueValidationEvent) => void)[] = [];
|
||||
function createMockGitHubAPI(): GitHubAPI {
|
||||
return {
|
||||
checkRemote: async (projectPath: string) => {
|
||||
console.log('[Mock] Checking GitHub remote for:', projectPath);
|
||||
logger.info('Mock Checking GitHub remote for:', projectPath);
|
||||
return {
|
||||
success: true,
|
||||
hasGitHubRemote: false,
|
||||
@@ -2743,7 +2746,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
||||
};
|
||||
},
|
||||
listIssues: async (projectPath: string) => {
|
||||
console.log('[Mock] Listing GitHub issues for:', projectPath);
|
||||
logger.info('Mock Listing GitHub issues for:', projectPath);
|
||||
return {
|
||||
success: true,
|
||||
openIssues: [],
|
||||
@@ -2751,7 +2754,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
||||
};
|
||||
},
|
||||
listPRs: async (projectPath: string) => {
|
||||
console.log('[Mock] Listing GitHub PRs for:', projectPath);
|
||||
logger.info('Mock Listing GitHub PRs for:', projectPath);
|
||||
return {
|
||||
success: true,
|
||||
openPRs: [],
|
||||
@@ -2759,7 +2762,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
||||
};
|
||||
},
|
||||
validateIssue: async (projectPath: string, issue: IssueValidationInput, model?: ModelAlias) => {
|
||||
console.log('[Mock] Starting async validation:', { projectPath, issue, model });
|
||||
logger.info('Mock Starting async validation:', { projectPath, issue, model });
|
||||
|
||||
// Simulate async validation in background
|
||||
setTimeout(() => {
|
||||
@@ -2800,7 +2803,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
||||
};
|
||||
},
|
||||
getValidationStatus: async (projectPath: string, issueNumber?: number) => {
|
||||
console.log('[Mock] Getting validation status:', { projectPath, issueNumber });
|
||||
logger.info('Mock Getting validation status:', { projectPath, issueNumber });
|
||||
return {
|
||||
success: true,
|
||||
isRunning: false,
|
||||
@@ -2808,21 +2811,21 @@ function createMockGitHubAPI(): GitHubAPI {
|
||||
};
|
||||
},
|
||||
stopValidation: async (projectPath: string, issueNumber: number) => {
|
||||
console.log('[Mock] Stopping validation:', { projectPath, issueNumber });
|
||||
logger.info('Mock Stopping validation:', { projectPath, issueNumber });
|
||||
return {
|
||||
success: true,
|
||||
message: `Validation for issue #${issueNumber} stopped`,
|
||||
};
|
||||
},
|
||||
getValidations: async (projectPath: string, issueNumber?: number) => {
|
||||
console.log('[Mock] Getting validations:', { projectPath, issueNumber });
|
||||
logger.info('Mock Getting validations:', { projectPath, issueNumber });
|
||||
return {
|
||||
success: true,
|
||||
validations: [],
|
||||
};
|
||||
},
|
||||
markValidationViewed: async (projectPath: string, issueNumber: number) => {
|
||||
console.log('[Mock] Marking validation as viewed:', { projectPath, issueNumber });
|
||||
logger.info('Mock Marking validation as viewed:', { projectPath, issueNumber });
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
@@ -2834,7 +2837,7 @@ function createMockGitHubAPI(): GitHubAPI {
|
||||
};
|
||||
},
|
||||
getIssueComments: async (projectPath: string, issueNumber: number, cursor?: string) => {
|
||||
console.log('[Mock] Getting issue comments:', { projectPath, issueNumber, cursor });
|
||||
logger.info('Mock Getting issue comments:', { projectPath, issueNumber, cursor });
|
||||
return {
|
||||
success: true,
|
||||
comments: [],
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
* user confirmation or server-side path resolution.
|
||||
*/
|
||||
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('FilePicker');
|
||||
|
||||
/**
|
||||
* Directory picker result with structure information for server-side resolution
|
||||
*/
|
||||
@@ -65,18 +69,18 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
||||
focusTimeout = null;
|
||||
}
|
||||
|
||||
console.log('[FilePicker] Change event fired');
|
||||
logger.info('Change event fired');
|
||||
const files = input.files;
|
||||
console.log('[FilePicker] Files selected:', files?.length || 0);
|
||||
logger.info('Files selected:', files?.length || 0);
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
console.log('[FilePicker] No files selected');
|
||||
logger.info('No files selected');
|
||||
safeResolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const firstFile = files[0];
|
||||
console.log('[FilePicker] First file:', {
|
||||
logger.info('First file:', {
|
||||
name: firstFile.name,
|
||||
webkitRelativePath: firstFile.webkitRelativePath,
|
||||
// @ts-expect-error - path property is non-standard but available in some browsers
|
||||
@@ -92,12 +96,12 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
||||
if (firstFile.path) {
|
||||
// @ts-expect-error - path property is non-standard but available in some browsers
|
||||
const filePath = firstFile.path as string;
|
||||
console.log('[FilePicker] Found file.path:', filePath);
|
||||
logger.info('Found file.path:', filePath);
|
||||
// Extract directory path (remove filename)
|
||||
const lastSeparator = Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/'));
|
||||
if (lastSeparator > 0) {
|
||||
const absolutePath = filePath.substring(0, lastSeparator);
|
||||
console.log('[FilePicker] Found absolute path:', absolutePath);
|
||||
logger.info('Found absolute path:', absolutePath);
|
||||
// Return as directory name for now - server can validate it directly
|
||||
directoryName = absolutePath;
|
||||
}
|
||||
@@ -106,11 +110,11 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
||||
// Method 2: Extract directory name from webkitRelativePath
|
||||
if (directoryName === 'Selected Directory' && firstFile.webkitRelativePath) {
|
||||
const relativePath = firstFile.webkitRelativePath;
|
||||
console.log('[FilePicker] Using webkitRelativePath:', relativePath);
|
||||
logger.info('Using webkitRelativePath:', relativePath);
|
||||
const pathParts = relativePath.split('/');
|
||||
if (pathParts.length > 0) {
|
||||
directoryName = pathParts[0]; // Top-level directory name
|
||||
console.log('[FilePicker] Extracted directory name:', directoryName);
|
||||
logger.info('Extracted directory name:', directoryName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +131,7 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[FilePicker] Directory info:', {
|
||||
logger.info('Directory info:', {
|
||||
directoryName,
|
||||
fileCount: files.length,
|
||||
sampleFiles: sampleFiles.slice(0, 5), // Log first 5
|
||||
@@ -147,7 +151,7 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
||||
// Only resolve as canceled if change event hasn't fired after a delay
|
||||
focusTimeout = setTimeout(() => {
|
||||
if (!resolved && !changeEventFired && (!input.files || input.files.length === 0)) {
|
||||
console.log('[FilePicker] Dialog canceled (no files after focus and no change event)');
|
||||
logger.info('Dialog canceled (no files after focus and no change event)');
|
||||
safeResolve(null);
|
||||
}
|
||||
}, 2000); // Increased timeout for Windows - give it time
|
||||
@@ -155,19 +159,19 @@ export async function openDirectoryPicker(): Promise<DirectoryPickerResult | nul
|
||||
|
||||
// Add to DOM temporarily
|
||||
document.body.appendChild(input);
|
||||
console.log('[FilePicker] Opening directory picker...');
|
||||
logger.info('Opening directory picker...');
|
||||
|
||||
// Try to show picker programmatically
|
||||
if ('showPicker' in HTMLInputElement.prototype) {
|
||||
try {
|
||||
(input as any).showPicker();
|
||||
console.log('[FilePicker] Using showPicker()');
|
||||
logger.info('Using showPicker()');
|
||||
} catch (error) {
|
||||
console.log('[FilePicker] showPicker() failed, using click()', error);
|
||||
logger.info('showPicker() failed, using click()', error);
|
||||
input.click();
|
||||
}
|
||||
} else {
|
||||
console.log('[FilePicker] Using click()');
|
||||
logger.info('Using click()');
|
||||
input.click();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* but communicates with the backend server via HTTP/WebSocket.
|
||||
*/
|
||||
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import type {
|
||||
ElectronAPI,
|
||||
FileResult,
|
||||
@@ -32,6 +33,8 @@ import type { Feature, ClaudeUsageResponse } from '@/store/app-store';
|
||||
import type { WorktreeAPI, GitAPI, ModelDefinition, ProviderStatus } from '@/types/electron';
|
||||
import { getGlobalFileBrowser } from '@/contexts/file-browser-context';
|
||||
|
||||
const logger = createLogger('HttpClient');
|
||||
|
||||
// Cached server URL (set during initialization in Electron mode)
|
||||
let cachedServerUrl: string | null = null;
|
||||
|
||||
@@ -46,9 +49,9 @@ export const initServerUrl = async (): Promise<void> => {
|
||||
if (electron?.getServerUrl) {
|
||||
try {
|
||||
cachedServerUrl = await electron.getServerUrl();
|
||||
console.log('[HTTP Client] Server URL from Electron:', cachedServerUrl);
|
||||
logger.info('Server URL from Electron:', cachedServerUrl);
|
||||
} catch (error) {
|
||||
console.warn('[HTTP Client] Failed to get server URL from Electron:', error);
|
||||
logger.warn('Failed to get server URL from Electron:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -145,16 +148,16 @@ export const initApiKey = async (): Promise<void> => {
|
||||
try {
|
||||
cachedApiKey = await window.electronAPI.getApiKey();
|
||||
if (cachedApiKey) {
|
||||
console.log('[HTTP Client] Using API key from Electron');
|
||||
logger.info('Using API key from Electron');
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[HTTP Client] Failed to get API key from Electron:', error);
|
||||
logger.warn('Failed to get API key from Electron:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// In web mode, authentication is handled via HTTP-only cookies
|
||||
console.log('[HTTP Client] Web mode - using cookie-based authentication');
|
||||
logger.info('Web mode - using cookie-based authentication');
|
||||
} finally {
|
||||
// Mark as initialized after completion, regardless of success or failure
|
||||
apiKeyInitialized = true;
|
||||
@@ -182,7 +185,7 @@ export const checkAuthStatus = async (): Promise<{
|
||||
required: data.required ?? true,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[HTTP Client] Failed to check auth status:', error);
|
||||
logger.error('Failed to check auth status:', error);
|
||||
return { authenticated: false, required: true };
|
||||
}
|
||||
};
|
||||
@@ -207,23 +210,23 @@ export const login = async (
|
||||
// Store the session token if login succeeded
|
||||
if (data.success && data.token) {
|
||||
setSessionToken(data.token);
|
||||
console.log('[HTTP Client] Session token stored after login');
|
||||
logger.info('Session token stored after login');
|
||||
|
||||
// Verify the session is actually working by making a request to an authenticated endpoint
|
||||
const verified = await verifySession();
|
||||
if (!verified) {
|
||||
console.error('[HTTP Client] Login appeared successful but session verification failed');
|
||||
logger.error('Login appeared successful but session verification failed');
|
||||
return {
|
||||
success: false,
|
||||
error: 'Session verification failed. Please try again.',
|
||||
};
|
||||
}
|
||||
console.log('[HTTP Client] Login verified successfully');
|
||||
logger.info('Login verified successfully');
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('[HTTP Client] Login failed:', error);
|
||||
logger.error('Login failed:', error);
|
||||
return { success: false, error: 'Network error' };
|
||||
}
|
||||
};
|
||||
@@ -243,20 +246,20 @@ export const fetchSessionToken = async (): Promise<boolean> => {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.log('[HTTP Client] Failed to check auth status');
|
||||
logger.info('Failed to check auth status');
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success && data.authenticated) {
|
||||
console.log('[HTTP Client] Session cookie is valid');
|
||||
logger.info('Session cookie is valid');
|
||||
return true;
|
||||
}
|
||||
|
||||
console.log('[HTTP Client] Session cookie is not authenticated');
|
||||
logger.info('Session cookie is not authenticated');
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error('[HTTP Client] Failed to check session:', error);
|
||||
logger.error('Failed to check session:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -273,11 +276,11 @@ export const logout = async (): Promise<{ success: boolean }> => {
|
||||
|
||||
// Clear the cached session token
|
||||
clearSessionToken();
|
||||
console.log('[HTTP Client] Session token cleared on logout');
|
||||
logger.info('Session token cleared on logout');
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('[HTTP Client] Logout failed:', error);
|
||||
logger.error('Logout failed:', error);
|
||||
return { success: false };
|
||||
}
|
||||
};
|
||||
@@ -310,7 +313,7 @@ export const verifySession = async (): Promise<boolean> => {
|
||||
|
||||
// Check for authentication errors
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
console.warn('[HTTP Client] Session verification failed - session expired or invalid');
|
||||
logger.warn('Session verification failed - session expired or invalid');
|
||||
// Clear the session since it's no longer valid
|
||||
clearSessionToken();
|
||||
// Try to clear the cookie via logout (fire and forget)
|
||||
@@ -324,14 +327,14 @@ export const verifySession = async (): Promise<boolean> => {
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('[HTTP Client] Session verification failed with status:', response.status);
|
||||
logger.warn('Session verification failed with status:', response.status);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('[HTTP Client] Session verified successfully');
|
||||
logger.info('Session verified successfully');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[HTTP Client] Session verification error:', error);
|
||||
logger.error('Session verification error:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -350,14 +353,14 @@ export const checkSandboxEnvironment = async (): Promise<{
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('[HTTP Client] Failed to check sandbox environment');
|
||||
logger.warn('Failed to check sandbox environment');
|
||||
return { isContainerized: false, error: 'Failed to check environment' };
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return { isContainerized: data.isContainerized ?? false };
|
||||
} catch (error) {
|
||||
console.error('[HTTP Client] Sandbox environment check failed:', error);
|
||||
logger.error('Sandbox environment check failed:', error);
|
||||
return { isContainerized: false, error: 'Network error' };
|
||||
}
|
||||
};
|
||||
@@ -399,7 +402,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
this.connectWebSocket();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('[HttpApiClient] API key initialization failed:', error);
|
||||
logger.error('API key initialization failed:', error);
|
||||
// Still attempt WebSocket connection - it may work with cookie auth
|
||||
this.connectWebSocket();
|
||||
});
|
||||
@@ -428,7 +431,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('[HttpApiClient] Failed to fetch wsToken:', response.status);
|
||||
logger.warn('Failed to fetch wsToken:', response.status);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -439,7 +442,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('[HttpApiClient] Error fetching wsToken:', error);
|
||||
logger.error('Error fetching wsToken:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -456,9 +459,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
if (isElectronMode()) {
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) {
|
||||
console.warn(
|
||||
'[HttpApiClient] Electron mode: API key not ready, delaying WebSocket connect'
|
||||
);
|
||||
logger.warn('Electron mode: API key not ready, delaying WebSocket connect');
|
||||
this.isConnecting = false;
|
||||
if (!this.reconnectTimer) {
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
@@ -482,12 +483,12 @@ export class HttpApiClient implements ElectronAPI {
|
||||
this.establishWebSocket(`${wsUrl}?wsToken=${encodeURIComponent(wsToken)}`);
|
||||
} else {
|
||||
// Fallback: try connecting without token (will fail if not authenticated)
|
||||
console.warn('[HttpApiClient] No wsToken available, attempting connection anyway');
|
||||
logger.warn('No wsToken available, attempting connection anyway');
|
||||
this.establishWebSocket(wsUrl);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('[HttpApiClient] Failed to prepare WebSocket connection:', error);
|
||||
logger.error('Failed to prepare WebSocket connection:', error);
|
||||
this.isConnecting = false;
|
||||
});
|
||||
}
|
||||
@@ -500,7 +501,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
this.ws = new WebSocket(wsUrl);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log('[HttpApiClient] WebSocket connected');
|
||||
logger.info('WebSocket connected');
|
||||
this.isConnecting = false;
|
||||
if (this.reconnectTimer) {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
@@ -511,8 +512,8 @@ export class HttpApiClient implements ElectronAPI {
|
||||
this.ws.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log(
|
||||
'[HttpApiClient] WebSocket message:',
|
||||
logger.info(
|
||||
'WebSocket message:',
|
||||
data.type,
|
||||
'hasPayload:',
|
||||
!!data.payload,
|
||||
@@ -521,16 +522,16 @@ export class HttpApiClient implements ElectronAPI {
|
||||
);
|
||||
const callbacks = this.eventCallbacks.get(data.type);
|
||||
if (callbacks) {
|
||||
console.log('[HttpApiClient] Dispatching to', callbacks.size, 'callbacks');
|
||||
logger.info('Dispatching to', callbacks.size, 'callbacks');
|
||||
callbacks.forEach((cb) => cb(data.payload));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[HttpApiClient] Failed to parse WebSocket message:', error);
|
||||
logger.error('Failed to parse WebSocket message:', error);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = () => {
|
||||
console.log('[HttpApiClient] WebSocket disconnected');
|
||||
logger.info('WebSocket disconnected');
|
||||
this.isConnecting = false;
|
||||
this.ws = null;
|
||||
// Attempt to reconnect after 5 seconds
|
||||
@@ -543,11 +544,11 @@ export class HttpApiClient implements ElectronAPI {
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('[HttpApiClient] WebSocket error:', error);
|
||||
logger.error('WebSocket error:', error);
|
||||
this.isConnecting = false;
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[HttpApiClient] Failed to create WebSocket:', error);
|
||||
logger.error('Failed to create WebSocket:', error);
|
||||
this.isConnecting = false;
|
||||
}
|
||||
}
|
||||
@@ -747,7 +748,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
const fileBrowser = getGlobalFileBrowser();
|
||||
|
||||
if (!fileBrowser) {
|
||||
console.error('File browser not initialized');
|
||||
logger.error('File browser not initialized');
|
||||
return { canceled: true, filePaths: [] };
|
||||
}
|
||||
|
||||
@@ -769,7 +770,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
return { canceled: false, filePaths: [result.path] };
|
||||
}
|
||||
|
||||
console.error('Invalid directory:', result.error || 'Path not allowed');
|
||||
logger.error('Invalid directory:', result.error || 'Path not allowed');
|
||||
return { canceled: true, filePaths: [] };
|
||||
}
|
||||
|
||||
@@ -777,7 +778,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
const fileBrowser = getGlobalFileBrowser();
|
||||
|
||||
if (!fileBrowser) {
|
||||
console.error('File browser not initialized');
|
||||
logger.error('File browser not initialized');
|
||||
return { canceled: true, filePaths: [] };
|
||||
}
|
||||
|
||||
@@ -796,7 +797,7 @@ export class HttpApiClient implements ElectronAPI {
|
||||
return { canceled: false, filePaths: [path] };
|
||||
}
|
||||
|
||||
console.error('File not found');
|
||||
logger.error('File not found');
|
||||
return { canceled: true, filePaths: [] };
|
||||
}
|
||||
|
||||
@@ -1910,5 +1911,5 @@ export function getHttpApiClient(): HttpApiClient {
|
||||
// This ensures the init promise is created early, even before React components mount
|
||||
// The actual async work happens in the background and won't block module loading
|
||||
initApiKey().catch((error) => {
|
||||
console.error('[HTTP Client] Failed to initialize API key:', error);
|
||||
logger.error('Failed to initialize API key:', error);
|
||||
});
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
* new or existing projects.
|
||||
*/
|
||||
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getElectronAPI } from './electron';
|
||||
|
||||
const logger = createLogger('ProjectInit');
|
||||
|
||||
export interface ProjectInitResult {
|
||||
success: boolean;
|
||||
isNewProject: boolean;
|
||||
@@ -72,22 +75,22 @@ export async function initializeProject(projectPath: string): Promise<ProjectIni
|
||||
// Initialize git repository if it doesn't exist
|
||||
const gitDirExists = await api.exists(`${projectPath}/.git`);
|
||||
if (!gitDirExists) {
|
||||
console.log('[project-init] Initializing git repository...');
|
||||
logger.info('Initializing git repository...');
|
||||
try {
|
||||
// Initialize git and create an initial empty commit via server route
|
||||
const result = await api.worktree?.initGit(projectPath);
|
||||
if (result?.success && result.result?.initialized) {
|
||||
createdFiles.push('.git');
|
||||
console.log('[project-init] Git repository initialized with initial commit');
|
||||
logger.info('Git repository initialized with initial commit');
|
||||
} else if (result?.success && !result.result?.initialized) {
|
||||
// Git already existed (shouldn't happen since we checked, but handle it)
|
||||
existingFiles.push('.git');
|
||||
console.log('[project-init] Git repository already exists');
|
||||
logger.info('Git repository already exists');
|
||||
} else {
|
||||
console.warn('[project-init] Failed to initialize git repository:', result?.error);
|
||||
logger.warn('Failed to initialize git repository:', result?.error);
|
||||
}
|
||||
} catch (gitError) {
|
||||
console.warn('[project-init] Failed to initialize git repository:', gitError);
|
||||
logger.warn('Failed to initialize git repository:', gitError);
|
||||
// Don't fail the whole initialization if git init fails
|
||||
}
|
||||
} else {
|
||||
@@ -123,7 +126,7 @@ export async function initializeProject(projectPath: string): Promise<ProjectIni
|
||||
existingFiles,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[project-init] Failed to initialize project:', error);
|
||||
logger.error('Failed to initialize project:', error);
|
||||
return {
|
||||
success: false,
|
||||
isNewProject: false,
|
||||
@@ -153,7 +156,7 @@ export async function isProjectInitialized(projectPath: string): Promise<boolean
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[project-init] Error checking project initialization:', error);
|
||||
logger.error('Error checking project initialization:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -191,7 +194,7 @@ export async function getProjectInitStatus(projectPath: string): Promise<{
|
||||
existingFiles,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[project-init] Error getting project status:', error);
|
||||
logger.error('Error getting project status:', error);
|
||||
return {
|
||||
initialized: false,
|
||||
missingFiles: REQUIRED_STRUCTURE.directories,
|
||||
@@ -212,7 +215,7 @@ export async function hasAppSpec(projectPath: string): Promise<boolean> {
|
||||
const fullPath = `${projectPath}/.automaker/app_spec.txt`;
|
||||
return await api.exists(fullPath);
|
||||
} catch (error) {
|
||||
console.error('[project-init] Error checking app_spec.txt:', error);
|
||||
logger.error('Error checking app_spec.txt:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -229,7 +232,7 @@ export async function hasAutomakerDir(projectPath: string): Promise<boolean> {
|
||||
const fullPath = `${projectPath}/.automaker`;
|
||||
return await api.exists(fullPath);
|
||||
} catch (error) {
|
||||
console.error('[project-init] Error checking .automaker dir:', error);
|
||||
logger.error('Error checking .automaker dir:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
* Centralizes the logic for determining where projects should be created/opened
|
||||
*/
|
||||
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { getHttpApiClient } from './http-api-client';
|
||||
import { getElectronAPI } from './electron';
|
||||
import { getItem, setItem } from './storage';
|
||||
import path from 'path';
|
||||
|
||||
const logger = createLogger('WorkspaceConfig');
|
||||
|
||||
const LAST_PROJECT_DIR_KEY = 'automaker:lastProjectDir';
|
||||
|
||||
/**
|
||||
@@ -20,9 +23,7 @@ async function getDefaultDocumentsPath(): Promise<string | null> {
|
||||
const documentsPath = await api.getPath('documents');
|
||||
return path.join(documentsPath, 'Automaker');
|
||||
} catch (error) {
|
||||
if (typeof window !== 'undefined' && window.console) {
|
||||
window.console.error('Failed to get documents path:', error);
|
||||
}
|
||||
logger.error('Failed to get documents path:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -81,9 +82,7 @@ export async function getDefaultWorkspaceDirectory(): Promise<string | null> {
|
||||
const documentsPath = await getDefaultDocumentsPath();
|
||||
return documentsPath;
|
||||
} catch (error) {
|
||||
if (typeof window !== 'undefined' && window.console) {
|
||||
window.console.error('Failed to get default workspace directory:', error);
|
||||
}
|
||||
logger.error('Failed to get default workspace directory:', error);
|
||||
|
||||
// On error, try last used dir and Documents
|
||||
const lastUsedDir = getItem(LAST_PROJECT_DIR_KEY);
|
||||
|
||||
@@ -13,6 +13,7 @@ import crypto from 'crypto';
|
||||
import http, { Server } from 'http';
|
||||
import net from 'net';
|
||||
import { app, BrowserWindow, ipcMain, dialog, shell, screen } from 'electron';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
findNodeExecutable,
|
||||
buildEnhancedPath,
|
||||
@@ -35,6 +36,9 @@ import {
|
||||
systemPathExists,
|
||||
} from '@automaker/platform';
|
||||
|
||||
const logger = createLogger('Electron');
|
||||
const serverLogger = createLogger('Server');
|
||||
|
||||
// Development environment
|
||||
const isDev = !app.isPackaged;
|
||||
const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL;
|
||||
@@ -45,7 +49,7 @@ if (isDev) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
||||
} catch (error) {
|
||||
console.warn('[Electron] dotenv not available:', (error as Error).message);
|
||||
logger.warn('dotenv not available:', (error as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,21 +148,21 @@ function ensureApiKey(): string {
|
||||
const key = electronUserDataReadFileSync(API_KEY_FILENAME).trim();
|
||||
if (key) {
|
||||
apiKey = key;
|
||||
console.log('[Electron] Loaded existing API key');
|
||||
logger.info('Loaded existing API key');
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[Electron] Error reading API key:', error);
|
||||
logger.warn('Error reading API key:', error);
|
||||
}
|
||||
|
||||
// Generate new key
|
||||
apiKey = crypto.randomUUID();
|
||||
try {
|
||||
electronUserDataWriteFileSync(API_KEY_FILENAME, apiKey, { encoding: 'utf-8', mode: 0o600 });
|
||||
console.log('[Electron] Generated new API key');
|
||||
logger.info('Generated new API key');
|
||||
} catch (error) {
|
||||
console.error('[Electron] Failed to save API key:', error);
|
||||
logger.error('Failed to save API key:', error);
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
@@ -183,11 +187,11 @@ function getIconPath(): string | null {
|
||||
|
||||
try {
|
||||
if (!electronAppExists(iconPath)) {
|
||||
console.warn(`[Electron] Icon not found at: ${iconPath}`);
|
||||
logger.warn('Icon not found at:', iconPath);
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[Electron] Icon check failed: ${iconPath}`, error);
|
||||
logger.warn('Icon check failed:', iconPath, error);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -219,7 +223,7 @@ function loadWindowBounds(): WindowBounds | null {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[Electron] Failed to load window bounds:', (error as Error).message);
|
||||
logger.warn('Failed to load window bounds:', (error as Error).message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -231,9 +235,9 @@ function loadWindowBounds(): WindowBounds | null {
|
||||
function saveWindowBounds(bounds: WindowBounds): void {
|
||||
try {
|
||||
electronUserDataWriteFileSync(WINDOW_BOUNDS_FILENAME, JSON.stringify(bounds, null, 2));
|
||||
console.log('[Electron] Window bounds saved');
|
||||
logger.info('Window bounds saved');
|
||||
} catch (error) {
|
||||
console.warn('[Electron] Failed to save window bounds:', (error as Error).message);
|
||||
logger.warn('Failed to save window bounds:', (error as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +375,7 @@ async function startStaticServer(): Promise<void> {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
staticServer!.listen(staticPort, () => {
|
||||
console.log(`[Electron] Static server running at http://localhost:${staticPort}`);
|
||||
logger.info('Static server running at http://localhost:' + staticPort);
|
||||
resolve();
|
||||
});
|
||||
staticServer!.on('error', reject);
|
||||
@@ -386,7 +390,7 @@ async function startServer(): Promise<void> {
|
||||
// Find Node.js executable (handles desktop launcher scenarios)
|
||||
const nodeResult = findNodeExecutable({
|
||||
skipSearch: isDev,
|
||||
logger: (msg: string) => console.log(`[Electron] ${msg}`),
|
||||
logger: (msg: string) => logger.info(msg),
|
||||
});
|
||||
const command = nodeResult.nodePath;
|
||||
|
||||
@@ -470,7 +474,7 @@ async function startServer(): Promise<void> {
|
||||
// Build enhanced PATH that includes Node.js directory (cross-platform)
|
||||
const enhancedPath = buildEnhancedPath(command, process.env.PATH || '');
|
||||
if (enhancedPath !== process.env.PATH) {
|
||||
console.log(`[Electron] Enhanced PATH with Node directory: ${path.dirname(command)}`);
|
||||
logger.info('Enhanced PATH with Node directory:', path.dirname(command));
|
||||
}
|
||||
|
||||
const env = {
|
||||
@@ -488,12 +492,12 @@ async function startServer(): Promise<void> {
|
||||
}),
|
||||
};
|
||||
|
||||
console.log(`[Electron] Server will use port ${serverPort}`);
|
||||
logger.info('Server will use port', serverPort);
|
||||
|
||||
console.log('[Electron] Starting backend server...');
|
||||
console.log('[Electron] Server path:', serverPath);
|
||||
console.log('[Electron] Server root (cwd):', serverRoot);
|
||||
console.log('[Electron] NODE_PATH:', serverNodeModules);
|
||||
logger.info('Starting backend server...');
|
||||
logger.info('Server path:', serverPath);
|
||||
logger.info('Server root (cwd):', serverRoot);
|
||||
logger.info('NODE_PATH:', serverNodeModules);
|
||||
|
||||
serverProcess = spawn(command, args, {
|
||||
cwd: serverRoot,
|
||||
@@ -502,20 +506,20 @@ async function startServer(): Promise<void> {
|
||||
});
|
||||
|
||||
serverProcess.stdout?.on('data', (data) => {
|
||||
console.log(`[Server] ${data.toString().trim()}`);
|
||||
serverLogger.info(data.toString().trim());
|
||||
});
|
||||
|
||||
serverProcess.stderr?.on('data', (data) => {
|
||||
console.error(`[Server Error] ${data.toString().trim()}`);
|
||||
serverLogger.error(data.toString().trim());
|
||||
});
|
||||
|
||||
serverProcess.on('close', (code) => {
|
||||
console.log(`[Server] Process exited with code ${code}`);
|
||||
serverLogger.info('Process exited with code', code);
|
||||
serverProcess = null;
|
||||
});
|
||||
|
||||
serverProcess.on('error', (err) => {
|
||||
console.error(`[Server] Failed to start server process:`, err);
|
||||
serverLogger.error('Failed to start server process:', err);
|
||||
serverProcess = null;
|
||||
});
|
||||
|
||||
@@ -542,7 +546,7 @@ async function waitForServer(maxAttempts = 30): Promise<void> {
|
||||
reject(new Error('Timeout'));
|
||||
});
|
||||
});
|
||||
console.log('[Electron] Server is ready');
|
||||
logger.info('Server is ready');
|
||||
return;
|
||||
} catch {
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
@@ -645,10 +649,10 @@ app.whenReady().then(async () => {
|
||||
const desiredUserDataPath = path.join(app.getPath('appData'), 'Automaker');
|
||||
if (app.getPath('userData') !== desiredUserDataPath) {
|
||||
app.setPath('userData', desiredUserDataPath);
|
||||
console.log('[Electron] userData path set to:', desiredUserDataPath);
|
||||
logger.info('userData path set to:', desiredUserDataPath);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[Electron] Failed to set userData path:', (error as Error).message);
|
||||
logger.warn('Failed to set userData path:', (error as Error).message);
|
||||
}
|
||||
|
||||
// Initialize centralized path helpers for Electron
|
||||
@@ -664,7 +668,7 @@ app.whenReady().then(async () => {
|
||||
} else {
|
||||
setElectronAppPaths(__dirname, process.resourcesPath);
|
||||
}
|
||||
console.log('[Electron] Initialized path security helpers');
|
||||
logger.info('Initialized path security helpers');
|
||||
|
||||
// Initialize security settings for path validation
|
||||
// Set DATA_DIR before initializing so it's available for security checks
|
||||
@@ -679,7 +683,7 @@ app.whenReady().then(async () => {
|
||||
try {
|
||||
app.dock.setIcon(iconPath);
|
||||
} catch (error) {
|
||||
console.warn('[Electron] Failed to set dock icon:', (error as Error).message);
|
||||
logger.warn('Failed to set dock icon:', (error as Error).message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,16 +695,12 @@ app.whenReady().then(async () => {
|
||||
// Find available ports (prevents conflicts with other apps using same ports)
|
||||
serverPort = await findAvailablePort(DEFAULT_SERVER_PORT);
|
||||
if (serverPort !== DEFAULT_SERVER_PORT) {
|
||||
console.log(
|
||||
`[Electron] Default server port ${DEFAULT_SERVER_PORT} in use, using port ${serverPort}`
|
||||
);
|
||||
logger.info('Default server port', DEFAULT_SERVER_PORT, 'in use, using port', serverPort);
|
||||
}
|
||||
|
||||
staticPort = await findAvailablePort(DEFAULT_STATIC_PORT);
|
||||
if (staticPort !== DEFAULT_STATIC_PORT) {
|
||||
console.log(
|
||||
`[Electron] Default static port ${DEFAULT_STATIC_PORT} in use, using port ${staticPort}`
|
||||
);
|
||||
logger.info('Default static port', DEFAULT_STATIC_PORT, 'in use, using port', staticPort);
|
||||
}
|
||||
|
||||
// Start static file server in production
|
||||
@@ -714,7 +714,7 @@ app.whenReady().then(async () => {
|
||||
// Create window
|
||||
createWindow();
|
||||
} catch (error) {
|
||||
console.error('[Electron] Failed to start:', error);
|
||||
logger.error('Failed to start:', error);
|
||||
const errorMessage = (error as Error).message;
|
||||
const isNodeError = errorMessage.includes('Node.js');
|
||||
dialog.showErrorBox(
|
||||
@@ -740,12 +740,12 @@ app.on('window-all-closed', () => {
|
||||
// (standard macOS behavior). On other platforms, stop servers and quit.
|
||||
if (process.platform !== 'darwin') {
|
||||
if (serverProcess && serverProcess.pid) {
|
||||
console.log('[Electron] All windows closed, stopping server...');
|
||||
logger.info('All windows closed, stopping server...');
|
||||
if (process.platform === 'win32') {
|
||||
try {
|
||||
execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' });
|
||||
} catch (error) {
|
||||
console.error('[Electron] Failed to kill server process:', (error as Error).message);
|
||||
logger.error('Failed to kill server process:', (error as Error).message);
|
||||
}
|
||||
} else {
|
||||
serverProcess.kill('SIGTERM');
|
||||
@@ -754,7 +754,7 @@ app.on('window-all-closed', () => {
|
||||
}
|
||||
|
||||
if (staticServer) {
|
||||
console.log('[Electron] Stopping static server...');
|
||||
logger.info('Stopping static server...');
|
||||
staticServer.close();
|
||||
staticServer = null;
|
||||
}
|
||||
@@ -765,7 +765,7 @@ app.on('window-all-closed', () => {
|
||||
|
||||
app.on('before-quit', () => {
|
||||
if (serverProcess && serverProcess.pid) {
|
||||
console.log('[Electron] Stopping server...');
|
||||
logger.info('Stopping server...');
|
||||
if (process.platform === 'win32') {
|
||||
try {
|
||||
// Windows: use taskkill with /t to kill entire process tree
|
||||
@@ -773,7 +773,7 @@ app.on('before-quit', () => {
|
||||
// Using execSync to ensure process is killed before app exits
|
||||
execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' });
|
||||
} catch (error) {
|
||||
console.error('[Electron] Failed to kill server process:', (error as Error).message);
|
||||
logger.error('Failed to kill server process:', (error as Error).message);
|
||||
}
|
||||
} else {
|
||||
serverProcess.kill('SIGTERM');
|
||||
@@ -782,7 +782,7 @@ app.on('before-quit', () => {
|
||||
}
|
||||
|
||||
if (staticServer) {
|
||||
console.log('[Electron] Stopping static server...');
|
||||
logger.info('Stopping static server...');
|
||||
staticServer.close();
|
||||
staticServer = null;
|
||||
}
|
||||
@@ -924,6 +924,6 @@ ipcMain.handle('window:updateMinWidth', (_, _sidebarExpanded: boolean) => {
|
||||
|
||||
// Quit the application (used when user denies sandbox risk confirmation)
|
||||
ipcMain.handle('app:quit', () => {
|
||||
console.log('[Electron] Quitting application via IPC request');
|
||||
logger.info('Quitting application via IPC request');
|
||||
app.quit();
|
||||
});
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
*/
|
||||
|
||||
import { contextBridge, ipcRenderer, OpenDialogOptions, SaveDialogOptions } from 'electron';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
|
||||
const logger = createLogger('Preload');
|
||||
|
||||
// Expose minimal API for native features
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
@@ -55,4 +58,4 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
quit: (): Promise<void> => ipcRenderer.invoke('app:quit'),
|
||||
});
|
||||
|
||||
console.log('[Preload] Electron API exposed (TypeScript)');
|
||||
logger.info('Electron API exposed (TypeScript)');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createRootRoute, Outlet, useLocation, useNavigate } from '@tanstack/react-router';
|
||||
import { useEffect, useState, useCallback, useDeferredValue, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import { Sidebar } from '@/components/layout/sidebar';
|
||||
import {
|
||||
FileBrowserProvider,
|
||||
@@ -23,6 +24,8 @@ import { ThemeOption, themeOptions } from '@/config/theme-options';
|
||||
import { SandboxRiskDialog } from '@/components/dialogs/sandbox-risk-dialog';
|
||||
import { SandboxRejectionScreen } from '@/components/dialogs/sandbox-rejection-screen';
|
||||
|
||||
const logger = createLogger('RootLayout');
|
||||
|
||||
function RootLayoutContent() {
|
||||
const location = useLocation();
|
||||
const {
|
||||
@@ -120,7 +123,7 @@ function RootLayoutContent() {
|
||||
setSandboxStatus('needs-confirmation');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Sandbox] Failed to check environment:', error);
|
||||
logger.error('Failed to check environment:', error);
|
||||
// On error, assume not containerized and show warning
|
||||
if (skipSandboxWarning) {
|
||||
setSandboxStatus('confirmed');
|
||||
@@ -154,10 +157,10 @@ function RootLayoutContent() {
|
||||
if (electronAPI?.quit) {
|
||||
await electronAPI.quit();
|
||||
} else {
|
||||
console.error('[Sandbox] quit() not available on electronAPI');
|
||||
logger.error('quit() not available on electronAPI');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Sandbox] Failed to quit app:', error);
|
||||
logger.error('Failed to quit app:', error);
|
||||
}
|
||||
} else {
|
||||
// In web mode, show rejection screen
|
||||
@@ -202,7 +205,7 @@ function RootLayoutContent() {
|
||||
// Session is invalid or expired - treat as not authenticated
|
||||
useAuthStore.getState().setAuthState({ isAuthenticated: false, authChecked: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize auth:', error);
|
||||
logger.error('Failed to initialize auth:', error);
|
||||
// On error, treat as not authenticated
|
||||
useAuthStore.getState().setAuthState({ isAuthenticated: false, authChecked: true });
|
||||
} finally {
|
||||
@@ -282,7 +285,7 @@ function RootLayoutContent() {
|
||||
});
|
||||
setIpcConnected(response.ok);
|
||||
} catch (error) {
|
||||
console.error('IPC connection failed:', error);
|
||||
logger.error('IPC connection failed:', error);
|
||||
setIpcConnected(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -69,6 +69,12 @@ export default defineConfig(({ command }) => {
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
rollupOptions: {
|
||||
external: ['child_process', 'fs', 'path', 'crypto', 'http', 'net', 'os', 'util', 'stream', 'events', 'readline'],
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ['@automaker/platform'],
|
||||
},
|
||||
define: {
|
||||
__APP_VERSION__: JSON.stringify(appVersion),
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
"description": "Shared utility functions for AutoMaker",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./logger": {
|
||||
"types": "./dist/logger.d.ts",
|
||||
"default": "./dist/logger.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch",
|
||||
|
||||
@@ -40,7 +40,15 @@ export {
|
||||
} from './prompt-builder.js';
|
||||
|
||||
// Logger
|
||||
export { createLogger, getLogLevel, setLogLevel, LogLevel } from './logger.js';
|
||||
export {
|
||||
createLogger,
|
||||
getLogLevel,
|
||||
setLogLevel,
|
||||
setColorsEnabled,
|
||||
setTimestampsEnabled,
|
||||
LogLevel,
|
||||
type Logger,
|
||||
} from './logger.js';
|
||||
|
||||
// File system utilities
|
||||
export { mkdirSafe, existsSafe } from './fs-utils.js';
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/**
|
||||
* Simple logger utility with log levels
|
||||
* Configure via LOG_LEVEL environment variable: error, warn, info, debug
|
||||
* Defaults to 'info' if not set
|
||||
* Enhanced logger with colors and timestamps
|
||||
*
|
||||
* Environment Variables:
|
||||
* - LOG_LEVEL: error, warn, info, debug (default: info)
|
||||
* - LOG_COLORS: true/false (default: auto-detect TTY)
|
||||
* - LOG_TIMESTAMPS: true/false (default: false)
|
||||
*/
|
||||
|
||||
export enum LogLevel {
|
||||
@@ -18,42 +21,207 @@ const LOG_LEVEL_NAMES: Record<string, LogLevel> = {
|
||||
debug: LogLevel.DEBUG,
|
||||
};
|
||||
|
||||
// ANSI color codes for terminal output
|
||||
const ANSI = {
|
||||
reset: '\x1b[0m',
|
||||
bold: '\x1b[1m',
|
||||
dim: '\x1b[2m',
|
||||
// Foreground colors
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m',
|
||||
white: '\x1b[37m',
|
||||
gray: '\x1b[90m',
|
||||
};
|
||||
|
||||
// Browser CSS styles for console output
|
||||
const BROWSER_STYLES = {
|
||||
timestamp: 'color: #6b7280; font-size: 11px;',
|
||||
context: 'color: #3b82f6; font-weight: 600;',
|
||||
reset: 'color: inherit; font-weight: inherit;',
|
||||
levels: {
|
||||
ERROR:
|
||||
'background: #ef4444; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||
WARN: 'background: #f59e0b; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||
INFO: 'background: #3b82f6; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||
DEBUG:
|
||||
'background: #8b5cf6; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
||||
},
|
||||
};
|
||||
|
||||
// Environment detection - use globalThis for cross-platform compatibility
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const isBrowser = typeof (globalThis as any).window !== 'undefined';
|
||||
|
||||
// Configuration state
|
||||
let currentLogLevel: LogLevel = LogLevel.INFO;
|
||||
|
||||
// Detect if we're in a Node.js environment with TTY support
|
||||
function isTTY(): boolean {
|
||||
if (isBrowser) return false;
|
||||
try {
|
||||
return process?.stdout?.isTTY ?? false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get environment variable safely (works in both Node.js and browser)
|
||||
function getEnvVar(name: string): string | undefined {
|
||||
if (isBrowser) return undefined;
|
||||
try {
|
||||
return process?.env?.[name];
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize configuration from environment variables
|
||||
let colorsEnabled = isTTY() && getEnvVar('LOG_COLORS') !== 'false';
|
||||
let timestampsEnabled = getEnvVar('LOG_TIMESTAMPS') === 'true';
|
||||
|
||||
// Initialize log level from environment variable
|
||||
const envLogLevel = process.env.LOG_LEVEL?.toLowerCase();
|
||||
const envLogLevel = getEnvVar('LOG_LEVEL')?.toLowerCase();
|
||||
if (envLogLevel && LOG_LEVEL_NAMES[envLogLevel] !== undefined) {
|
||||
currentLogLevel = LOG_LEVEL_NAMES[envLogLevel];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format ISO timestamp
|
||||
*/
|
||||
function formatTimestamp(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format short time for browser (HH:mm:ss.SSS)
|
||||
*/
|
||||
function formatShortTime(): string {
|
||||
return new Date().toISOString().split('T')[1].slice(0, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a log line for Node.js terminal output
|
||||
*/
|
||||
function formatNodeLog(level: string, context: string, levelColor: string): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (timestampsEnabled) {
|
||||
parts.push(colorsEnabled ? `${ANSI.gray}${formatTimestamp()}${ANSI.reset}` : formatTimestamp());
|
||||
}
|
||||
|
||||
const levelPadded = level.padEnd(5);
|
||||
parts.push(colorsEnabled ? `${levelColor}${levelPadded}${ANSI.reset}` : levelPadded);
|
||||
parts.push(colorsEnabled ? `${ANSI.blue}[${context}]${ANSI.reset}` : `[${context}]`);
|
||||
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Logger interface returned by createLogger
|
||||
*/
|
||||
export interface Logger {
|
||||
error: (...args: unknown[]) => void;
|
||||
warn: (...args: unknown[]) => void;
|
||||
info: (...args: unknown[]) => void;
|
||||
debug: (...args: unknown[]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a logger instance with a context prefix
|
||||
*/
|
||||
export function createLogger(context: string) {
|
||||
const prefix = `[${context}]`;
|
||||
export function createLogger(context: string): Logger {
|
||||
if (isBrowser) {
|
||||
// Browser implementation with CSS styling
|
||||
return {
|
||||
error: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.ERROR) {
|
||||
console.error(
|
||||
`%cERROR%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||
BROWSER_STYLES.levels.ERROR,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.timestamp,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.context,
|
||||
BROWSER_STYLES.reset,
|
||||
...args
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
warn: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.WARN) {
|
||||
console.warn(
|
||||
`%cWARN%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||
BROWSER_STYLES.levels.WARN,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.timestamp,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.context,
|
||||
BROWSER_STYLES.reset,
|
||||
...args
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
info: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.INFO) {
|
||||
console.log(
|
||||
`%cINFO%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||
BROWSER_STYLES.levels.INFO,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.timestamp,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.context,
|
||||
BROWSER_STYLES.reset,
|
||||
...args
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
debug: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.DEBUG) {
|
||||
console.log(
|
||||
`%cDEBUG%c %c${formatShortTime()}%c %c[${context}]%c`,
|
||||
BROWSER_STYLES.levels.DEBUG,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.timestamp,
|
||||
BROWSER_STYLES.reset,
|
||||
BROWSER_STYLES.context,
|
||||
BROWSER_STYLES.reset,
|
||||
...args
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Node.js implementation with ANSI colors
|
||||
return {
|
||||
error: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.ERROR) {
|
||||
console.error(prefix, ...args);
|
||||
console.error(formatNodeLog('ERROR', context, ANSI.red), ...args);
|
||||
}
|
||||
},
|
||||
|
||||
warn: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.WARN) {
|
||||
console.warn(prefix, ...args);
|
||||
console.warn(formatNodeLog('WARN', context, ANSI.yellow), ...args);
|
||||
}
|
||||
},
|
||||
|
||||
info: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.INFO) {
|
||||
console.log(prefix, ...args);
|
||||
console.log(formatNodeLog('INFO', context, ANSI.cyan), ...args);
|
||||
}
|
||||
},
|
||||
|
||||
debug: (...args: unknown[]): void => {
|
||||
if (currentLogLevel >= LogLevel.DEBUG) {
|
||||
console.log(prefix, '[DEBUG]', ...args);
|
||||
console.log(formatNodeLog('DEBUG', context, ANSI.magenta), ...args);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -72,3 +240,17 @@ export function getLogLevel(): LogLevel {
|
||||
export function setLogLevel(level: LogLevel): void {
|
||||
currentLogLevel = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable colored output
|
||||
*/
|
||||
export function setColorsEnabled(enabled: boolean): void {
|
||||
colorsEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable timestamps in output
|
||||
*/
|
||||
export function setTimestampsEnabled(enabled: boolean): void {
|
||||
timestampsEnabled = enabled;
|
||||
}
|
||||
|
||||
104
package-lock.json
generated
104
package-lock.json
generated
@@ -28,7 +28,7 @@
|
||||
},
|
||||
"apps/server": {
|
||||
"name": "@automaker/server",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "0.1.76",
|
||||
@@ -78,7 +78,7 @@
|
||||
},
|
||||
"apps/ui": {
|
||||
"name": "@automaker/ui",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
@@ -675,6 +675,7 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -1258,6 +1259,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz",
|
||||
"integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
@@ -1300,6 +1302,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
@@ -2120,7 +2123,6 @@
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cross-dirname": "^0.1.0",
|
||||
"debug": "^4.3.4",
|
||||
@@ -2142,7 +2144,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
@@ -2159,7 +2160,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
@@ -2174,7 +2174,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
@@ -2942,7 +2941,6 @@
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -3067,7 +3065,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -3084,7 +3081,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -3101,7 +3097,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -3210,7 +3205,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3233,7 +3227,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3256,7 +3249,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3342,7 +3334,6 @@
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
@@ -3365,7 +3356,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3385,7 +3375,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -3785,8 +3774,7 @@
|
||||
"version": "16.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz",
|
||||
"integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "16.0.10",
|
||||
@@ -3800,7 +3788,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3817,7 +3804,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3834,7 +3820,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3851,7 +3836,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3868,7 +3852,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3885,7 +3868,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3902,7 +3884,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -3919,7 +3900,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
@@ -4010,6 +3990,7 @@
|
||||
"integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.57.0"
|
||||
},
|
||||
@@ -5450,7 +5431,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
@@ -5784,6 +5764,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.6.tgz",
|
||||
"integrity": "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.141.0",
|
||||
"@tanstack/react-store": "^0.8.0",
|
||||
@@ -6210,6 +6191,7 @@
|
||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
@@ -6352,6 +6334,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -6362,6 +6345,7 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -6467,6 +6451,7 @@
|
||||
"integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.50.0",
|
||||
"@typescript-eslint/types": "8.50.0",
|
||||
@@ -6960,7 +6945,8 @@
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@xyflow/react": {
|
||||
"version": "12.10.0",
|
||||
@@ -7058,6 +7044,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -7118,6 +7105,7 @@
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -7716,6 +7704,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -8247,8 +8236,7 @@
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
@@ -8553,8 +8541,7 @@
|
||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "10.1.0",
|
||||
@@ -8651,6 +8638,7 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -8952,6 +8940,7 @@
|
||||
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.0.12",
|
||||
"builder-util": "26.0.11",
|
||||
@@ -9278,7 +9267,6 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.2.1",
|
||||
"debug": "^4.1.1",
|
||||
@@ -9299,7 +9287,6 @@
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
@@ -9550,6 +9537,7 @@
|
||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -9864,6 +9852,7 @@
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
@@ -11531,7 +11520,6 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11553,7 +11541,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11575,7 +11562,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11597,7 +11583,6 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11619,7 +11604,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11641,7 +11625,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11663,7 +11646,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11685,7 +11667,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11707,7 +11688,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11729,7 +11709,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -11751,7 +11730,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
@@ -14039,7 +14017,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
@@ -14056,7 +14033,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^9.4.0"
|
||||
},
|
||||
@@ -14074,7 +14050,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
@@ -14263,6 +14238,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -14272,6 +14248,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -14630,7 +14607,6 @@
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
@@ -14819,6 +14795,7 @@
|
||||
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz",
|
||||
"integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@@ -14867,7 +14844,6 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
@@ -14918,7 +14894,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -14941,7 +14916,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -14964,7 +14938,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -14981,7 +14954,6 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -14998,7 +14970,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15015,7 +14986,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15032,7 +15002,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15049,7 +15018,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15066,7 +15034,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
@@ -15083,7 +15050,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15106,7 +15072,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15129,7 +15094,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15152,7 +15116,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15175,7 +15138,6 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15198,7 +15160,6 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
@@ -15667,7 +15628,6 @@
|
||||
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
|
||||
"integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"client-only": "0.0.1"
|
||||
},
|
||||
@@ -15837,7 +15797,6 @@
|
||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "~2.6.2"
|
||||
@@ -15901,7 +15860,6 @@
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -15999,6 +15957,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16203,6 +16162,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -16574,6 +16534,7 @@
|
||||
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -16663,7 +16624,8 @@
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
|
||||
"integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/vite/node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
@@ -16689,6 +16651,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16731,6 +16694,7 @@
|
||||
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.0.16",
|
||||
"@vitest/mocker": "4.0.16",
|
||||
@@ -16988,6 +16952,7 @@
|
||||
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
@@ -17056,6 +17021,7 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
|
||||
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user