mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 21:23:07 +00:00
* feat: add GitHub Copilot SDK provider integration Adds comprehensive GitHub Copilot SDK provider support including: - CopilotProvider class with CLI detection and OAuth authentication check - Copilot models definition with GPT-4o, Claude, and o1/o3 series models - Settings UI integration with provider tab, model configuration, and navigation - Onboarding flow integration with Copilot setup step - Model selector integration for all phase-specific model dropdowns - Persistence of enabled models and default model settings via API sync - Server route for Copilot CLI status endpoint https://claude.ai/code/session_01D26w7ZyEzP4H6Dor3ttk9d * chore: update package-lock.json https://claude.ai/code/session_01D26w7ZyEzP4H6Dor3ttk9d * refactor: rename Copilot SDK to Copilot CLI and use GitHub icon - Update all references from "GitHub Copilot SDK" to "GitHub Copilot CLI" - Change install command from @github/copilot-sdk to @github/copilot - Update CopilotIcon to use official GitHub Octocat logo - Update error codes and comments throughout codebase Co-Authored-By: Claude <noreply@anthropic.com> * fix: update Copilot model definitions and add dynamic model discovery - Update COPILOT_MODEL_MAP with correct models from CLI (claude-sonnet-4.5, claude-haiku-4.5, claude-opus-4.5, claude-sonnet-4, gpt-5.x series, gpt-4.1, gemini-3-pro-preview) - Change default Copilot model to copilot-claude-sonnet-4.5 - Add model caching methods to CopilotProvider (hasCachedModels, clearModelCache, refreshModels) - Add API routes for dynamic model discovery: - GET /api/setup/copilot/models - POST /api/setup/copilot/models/refresh - POST /api/setup/copilot/cache/clear Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: use @github/copilot-sdk instead of direct CLI calls - Install @github/copilot-sdk package for proper SDK integration - Rewrite CopilotProvider to use SDK's CopilotClient API - Use client.createSession() for session management - Handle SDK events (assistant.message, tool.execution_*, session.idle) - Auto-approve permissions for autonomous agent operation - Remove incorrect CLI flags (--mode, --output-format) - Update default model to claude-sonnet-4.5 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: add Copilot and Gemini model support to model resolver - Import isCopilotModel and isGeminiModel from types - Add explicit checks for copilot- and gemini- prefixed models - Pass through Copilot/Gemini models unchanged to their providers - Update resolver documentation to list all supported providers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: pass working directory to Copilot SDK and reduce event noise - Create CopilotClient per execution with correct cwd from options.cwd - This ensures the CLI operates in the correct project directory, not the server's current directory - Skip assistant.message_delta events (they create excessive noise) - Only yield the final assistant.message event which has complete content - Clean up client on completion and error paths Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: simplify Copilot SDK execution with sendAndWait - Use sendAndWait() instead of manual event polling for more reliable execution - Disable streaming (streaming: false) to simplify response handling - Increase timeout to 10 minutes for agentic operations - Still capture tool execution events for UI display - Add more debug logging for troubleshooting - This should fix the "invalid_request_body" error on subsequent calls Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: allow Copilot model IDs with claude-, gemini-, gpt- prefixes Copilot's bare model IDs legitimately contain prefixes like claude-, gemini-, gpt- because those are the actual model names from the Copilot CLI (e.g., claude-sonnet-4.5, gemini-3-pro-preview, gpt-5.1). The generic validateBareModelId function was incorrectly rejecting these valid model IDs. Now we only check that the copilot- prefix has been stripped by the ProviderFactory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: enable real-time streaming of tool events for Copilot - Switch back to streaming mode (streaming: true) for real-time events - Use async queue pattern to bridge SDK callbacks to async generator - Events are now yielded as they happen, not batched at the end - Tool calls (Read, Write, Edit, Bash, TodoWrite, etc.) show in real-time - Better progress visibility during agentic operations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: expand Copilot tool name and input normalization Tool name mapping additions: - view → Read (Copilot's file viewing tool) - create_file → Write - replace, patch → Edit - run_shell_command, terminal → Bash - search_file_content → Grep - list_directory → Ls - google_web_search → WebSearch - report_intent → ReportIntent (Copilot-specific planning) - think, plan → Think, Plan Input normalization improvements: - Read/Write/Edit: Map file, filename, filePath → file_path - Bash: Map cmd, script → command - Grep: Map query, search, regex → pattern Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: convert git+ssh to git+https in package-lock.json The @electron/node-gyp dependency was resolved with a git+ssh URL which fails in CI environments without SSH keys. Convert to HTTPS. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address code review feedback for Copilot SDK provider - Add guard for non-text prompts (vision not yet supported) - Clear runtime model cache on fetch failure - Fix race condition in async queue error handling - Import CopilotAuthStatus from shared types - Fix comment mismatch for default model constant - Add auth-copilot and deauth-copilot routes - Extract shared tool normalization utilities - Create base model configuration UI component - Add comprehensive unit tests for CopilotProvider - Replace magic strings with constants - Add debug logging for cleanup errors * fix: address CodeRabbit review nitpicks - Fix test mocks to include --version check for CLI detection - Add aria-label for accessibility on refresh button - Ensure default model checkbox always appears checked/enabled * fix: address CodeRabbit review feedback - Fix test mocks by creating fresh provider instances after mock setup - Extract COPILOT_DISCONNECTED_MARKER_FILE constant to common.ts - Add AUTONOMOUS MODE comment explaining auto-approval of permissions - Improve tool-normalization with union types and null guards - Handle 'canceled' (American spelling) status in todo normalization * refactor: extract copilot connection logic to service and fix test mocks - Create copilot-connection-service.ts with connect/disconnect logic - Update auth-copilot and deauth-copilot routes to use service - Fix test mocks for CLI detection: - Mock fs.existsSync for CLI path validation - Mock which/where command for CLI path detection --------- Co-authored-by: Claude <noreply@anthropic.com>
239 lines
8.6 KiB
TypeScript
239 lines
8.6 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { useSearch } from '@tanstack/react-router';
|
|
import { useAppStore } from '@/store/app-store';
|
|
|
|
import { useSettingsView, type SettingsViewId } from './settings-view/hooks';
|
|
import { NAV_ITEMS } from './settings-view/config/navigation';
|
|
import { SettingsHeader } from './settings-view/components/settings-header';
|
|
import { KeyboardMapDialog } from './settings-view/components/keyboard-map-dialog';
|
|
import { SettingsNavigation } from './settings-view/components/settings-navigation';
|
|
import { ApiKeysSection } from './settings-view/api-keys/api-keys-section';
|
|
import { ModelDefaultsSection } from './settings-view/model-defaults';
|
|
import { AppearanceSection } from './settings-view/appearance/appearance-section';
|
|
import { TerminalSection } from './settings-view/terminal/terminal-section';
|
|
import { AudioSection } from './settings-view/audio/audio-section';
|
|
import { KeyboardShortcutsSection } from './settings-view/keyboard-shortcuts/keyboard-shortcuts-section';
|
|
import { FeatureDefaultsSection } from './settings-view/feature-defaults/feature-defaults-section';
|
|
import { WorktreesSection } from './settings-view/worktrees';
|
|
import { AccountSection } from './settings-view/account';
|
|
import { SecuritySection } from './settings-view/security';
|
|
import { DeveloperSection } from './settings-view/developer/developer-section';
|
|
import {
|
|
ClaudeSettingsTab,
|
|
CursorSettingsTab,
|
|
CodexSettingsTab,
|
|
OpencodeSettingsTab,
|
|
GeminiSettingsTab,
|
|
CopilotSettingsTab,
|
|
} from './settings-view/providers';
|
|
import { MCPServersSection } from './settings-view/mcp-servers';
|
|
import { PromptCustomizationSection } from './settings-view/prompts';
|
|
import { EventHooksSection } from './settings-view/event-hooks';
|
|
import { ImportExportDialog } from './settings-view/components/import-export-dialog';
|
|
import type { Theme } from './settings-view/shared/types';
|
|
|
|
// Breakpoint constant for mobile (matches Tailwind lg breakpoint)
|
|
const LG_BREAKPOINT = 1024;
|
|
|
|
export function SettingsView() {
|
|
const {
|
|
theme,
|
|
setTheme,
|
|
defaultSkipTests,
|
|
setDefaultSkipTests,
|
|
enableDependencyBlocking,
|
|
setEnableDependencyBlocking,
|
|
skipVerificationInAutoMode,
|
|
setSkipVerificationInAutoMode,
|
|
enableAiCommitMessages,
|
|
setEnableAiCommitMessages,
|
|
useWorktrees,
|
|
setUseWorktrees,
|
|
muteDoneSound,
|
|
setMuteDoneSound,
|
|
currentProject,
|
|
defaultPlanningMode,
|
|
setDefaultPlanningMode,
|
|
defaultRequirePlanApproval,
|
|
setDefaultRequirePlanApproval,
|
|
defaultFeatureModel,
|
|
setDefaultFeatureModel,
|
|
autoLoadClaudeMd,
|
|
setAutoLoadClaudeMd,
|
|
promptCustomization,
|
|
setPromptCustomization,
|
|
skipSandboxWarning,
|
|
setSkipSandboxWarning,
|
|
} = useAppStore();
|
|
|
|
// Global theme (project-specific themes are managed in Project Settings)
|
|
const globalTheme = theme as Theme;
|
|
|
|
// Get initial view from URL search params
|
|
const { view: initialView } = useSearch({ from: '/settings' });
|
|
|
|
// Use settings view navigation hook
|
|
const { activeView, navigateTo } = useSettingsView({ initialView });
|
|
|
|
// Handle navigation - if navigating to 'providers', default to 'claude-provider'
|
|
const handleNavigate = (viewId: SettingsViewId) => {
|
|
if (viewId === 'providers') {
|
|
navigateTo('claude-provider');
|
|
} else {
|
|
navigateTo(viewId);
|
|
}
|
|
};
|
|
|
|
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
|
|
const [showImportExportDialog, setShowImportExportDialog] = useState(false);
|
|
|
|
// Mobile navigation state - default to showing on desktop, hidden on mobile
|
|
const [showNavigation, setShowNavigation] = useState(() => {
|
|
if (typeof window !== 'undefined') {
|
|
return window.innerWidth >= LG_BREAKPOINT;
|
|
}
|
|
return true; // Default to showing on SSR
|
|
});
|
|
|
|
// Auto-close navigation on mobile when a section is selected
|
|
useEffect(() => {
|
|
if (typeof window !== 'undefined' && window.innerWidth < LG_BREAKPOINT) {
|
|
setShowNavigation(false);
|
|
}
|
|
}, [activeView]);
|
|
|
|
// Handle window resize to show/hide navigation appropriately
|
|
useEffect(() => {
|
|
const handleResize = () => {
|
|
if (window.innerWidth >= LG_BREAKPOINT) {
|
|
setShowNavigation(true);
|
|
}
|
|
};
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
return () => window.removeEventListener('resize', handleResize);
|
|
}, []);
|
|
|
|
// Render the active section based on current view
|
|
const renderActiveSection = () => {
|
|
switch (activeView) {
|
|
case 'claude-provider':
|
|
return <ClaudeSettingsTab />;
|
|
case 'cursor-provider':
|
|
return <CursorSettingsTab />;
|
|
case 'codex-provider':
|
|
return <CodexSettingsTab />;
|
|
case 'opencode-provider':
|
|
return <OpencodeSettingsTab />;
|
|
case 'gemini-provider':
|
|
return <GeminiSettingsTab />;
|
|
case 'copilot-provider':
|
|
return <CopilotSettingsTab />;
|
|
case 'providers':
|
|
case 'claude': // Backwards compatibility - redirect to claude-provider
|
|
return <ClaudeSettingsTab />;
|
|
case 'mcp-servers':
|
|
return <MCPServersSection />;
|
|
case 'prompts':
|
|
return (
|
|
<PromptCustomizationSection
|
|
promptCustomization={promptCustomization}
|
|
onPromptCustomizationChange={setPromptCustomization}
|
|
/>
|
|
);
|
|
case 'model-defaults':
|
|
return <ModelDefaultsSection />;
|
|
case 'appearance':
|
|
return (
|
|
<AppearanceSection
|
|
effectiveTheme={globalTheme}
|
|
onThemeChange={(newTheme) => setTheme(newTheme as typeof theme)}
|
|
/>
|
|
);
|
|
case 'terminal':
|
|
return <TerminalSection />;
|
|
case 'keyboard':
|
|
return (
|
|
<KeyboardShortcutsSection onOpenKeyboardMap={() => setShowKeyboardMapDialog(true)} />
|
|
);
|
|
case 'audio':
|
|
return (
|
|
<AudioSection muteDoneSound={muteDoneSound} onMuteDoneSoundChange={setMuteDoneSound} />
|
|
);
|
|
case 'event-hooks':
|
|
return <EventHooksSection />;
|
|
case 'defaults':
|
|
return (
|
|
<FeatureDefaultsSection
|
|
defaultSkipTests={defaultSkipTests}
|
|
enableDependencyBlocking={enableDependencyBlocking}
|
|
skipVerificationInAutoMode={skipVerificationInAutoMode}
|
|
defaultPlanningMode={defaultPlanningMode}
|
|
defaultRequirePlanApproval={defaultRequirePlanApproval}
|
|
enableAiCommitMessages={enableAiCommitMessages}
|
|
defaultFeatureModel={defaultFeatureModel}
|
|
onDefaultSkipTestsChange={setDefaultSkipTests}
|
|
onEnableDependencyBlockingChange={setEnableDependencyBlocking}
|
|
onSkipVerificationInAutoModeChange={setSkipVerificationInAutoMode}
|
|
onDefaultPlanningModeChange={setDefaultPlanningMode}
|
|
onDefaultRequirePlanApprovalChange={setDefaultRequirePlanApproval}
|
|
onEnableAiCommitMessagesChange={setEnableAiCommitMessages}
|
|
onDefaultFeatureModelChange={setDefaultFeatureModel}
|
|
/>
|
|
);
|
|
case 'worktrees':
|
|
return (
|
|
<WorktreesSection useWorktrees={useWorktrees} onUseWorktreesChange={setUseWorktrees} />
|
|
);
|
|
case 'account':
|
|
return <AccountSection />;
|
|
case 'security':
|
|
return (
|
|
<SecuritySection
|
|
skipSandboxWarning={skipSandboxWarning}
|
|
onSkipSandboxWarningChange={setSkipSandboxWarning}
|
|
/>
|
|
);
|
|
case 'developer':
|
|
return <DeveloperSection />;
|
|
default:
|
|
return <ApiKeysSection />;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="flex-1 flex flex-col overflow-hidden content-bg" data-testid="settings-view">
|
|
{/* Header Section */}
|
|
<SettingsHeader
|
|
showNavigation={showNavigation}
|
|
onToggleNavigation={() => setShowNavigation(!showNavigation)}
|
|
onImportExportClick={() => setShowImportExportDialog(true)}
|
|
/>
|
|
|
|
{/* Content Area with Sidebar */}
|
|
<div className="flex-1 flex overflow-hidden">
|
|
{/* Side Navigation - Overlay on mobile, sidebar on desktop */}
|
|
<SettingsNavigation
|
|
navItems={NAV_ITEMS}
|
|
activeSection={activeView}
|
|
currentProject={currentProject}
|
|
onNavigate={handleNavigate}
|
|
isOpen={showNavigation}
|
|
onClose={() => setShowNavigation(false)}
|
|
/>
|
|
|
|
{/* Content Panel - Shows only the active section */}
|
|
<div className="flex-1 overflow-y-auto p-4 lg:p-8">
|
|
<div className="max-w-4xl mx-auto">{renderActiveSection()}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Keyboard Map Dialog */}
|
|
<KeyboardMapDialog open={showKeyboardMapDialog} onOpenChange={setShowKeyboardMapDialog} />
|
|
|
|
{/* Import/Export Settings Dialog */}
|
|
<ImportExportDialog open={showImportExportDialog} onOpenChange={setShowImportExportDialog} />
|
|
</div>
|
|
);
|
|
}
|