mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
Merge remote-tracking branch 'upstream/v0.11.0rc' into patchcraft
This commit is contained in:
@@ -102,6 +102,12 @@ RUN curl https://cursor.com/install -fsS | bash && \
|
|||||||
ls -la /home/automaker/.local/bin/ && \
|
ls -la /home/automaker/.local/bin/ && \
|
||||||
echo "=== PATH is: $PATH ===" && \
|
echo "=== PATH is: $PATH ===" && \
|
||||||
(which cursor-agent && cursor-agent --version) || echo "cursor-agent installed (may need auth setup)"
|
(which cursor-agent && cursor-agent --version) || echo "cursor-agent installed (may need auth setup)"
|
||||||
|
|
||||||
|
# Install OpenCode CLI (for multi-provider AI model access)
|
||||||
|
RUN curl -fsSL https://opencode.ai/install | bash && \
|
||||||
|
echo "=== Checking OpenCode CLI installation ===" && \
|
||||||
|
ls -la /home/automaker/.local/bin/ && \
|
||||||
|
(which opencode && opencode --version) || echo "opencode installed (may need auth setup)"
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
# Add PATH to profile so it's available in all interactive shells (for login shells)
|
# Add PATH to profile so it's available in all interactive shells (for login shells)
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ import {
|
|||||||
FeatureTextFilePath as DescriptionTextFilePath,
|
FeatureTextFilePath as DescriptionTextFilePath,
|
||||||
ImagePreviewMap,
|
ImagePreviewMap,
|
||||||
} from '@/components/ui/description-image-dropzone';
|
} from '@/components/ui/description-image-dropzone';
|
||||||
import { Play, Cpu, FolderKanban } from 'lucide-react';
|
import { Play, Cpu, FolderKanban, Settings2 } from 'lucide-react';
|
||||||
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { modelSupportsThinking } from '@/lib/utils';
|
import { modelSupportsThinking } from '@/lib/utils';
|
||||||
@@ -33,7 +34,7 @@ import {
|
|||||||
PlanningMode,
|
PlanningMode,
|
||||||
Feature,
|
Feature,
|
||||||
} from '@/store/app-store';
|
} from '@/store/app-store';
|
||||||
import type { ReasoningEffort, PhaseModelEntry } from '@automaker/types';
|
import type { ReasoningEffort, PhaseModelEntry, AgentModel } from '@automaker/types';
|
||||||
import { supportsReasoningEffort, isClaudeModel } from '@automaker/types';
|
import { supportsReasoningEffort, isClaudeModel } from '@automaker/types';
|
||||||
import {
|
import {
|
||||||
TestingTabContent,
|
TestingTabContent,
|
||||||
@@ -152,6 +153,7 @@ export function AddFeatureDialog({
|
|||||||
forceCurrentBranchMode,
|
forceCurrentBranchMode,
|
||||||
}: AddFeatureDialogProps) {
|
}: AddFeatureDialogProps) {
|
||||||
const isSpawnMode = !!parentFeature;
|
const isSpawnMode = !!parentFeature;
|
||||||
|
const navigate = useNavigate();
|
||||||
const [workMode, setWorkMode] = useState<WorkMode>('current');
|
const [workMode, setWorkMode] = useState<WorkMode>('current');
|
||||||
|
|
||||||
// Form state
|
// Form state
|
||||||
@@ -187,7 +189,8 @@ export function AddFeatureDialog({
|
|||||||
const [selectedAncestorIds, setSelectedAncestorIds] = useState<Set<string>>(new Set());
|
const [selectedAncestorIds, setSelectedAncestorIds] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
// Get defaults from store
|
// Get defaults from store
|
||||||
const { defaultPlanningMode, defaultRequirePlanApproval, useWorktrees } = useAppStore();
|
const { defaultPlanningMode, defaultRequirePlanApproval, useWorktrees, defaultFeatureModel } =
|
||||||
|
useAppStore();
|
||||||
|
|
||||||
// Track previous open state to detect when dialog opens
|
// Track previous open state to detect when dialog opens
|
||||||
const wasOpenRef = useRef(false);
|
const wasOpenRef = useRef(false);
|
||||||
@@ -207,7 +210,7 @@ export function AddFeatureDialog({
|
|||||||
);
|
);
|
||||||
setPlanningMode(defaultPlanningMode);
|
setPlanningMode(defaultPlanningMode);
|
||||||
setRequirePlanApproval(defaultRequirePlanApproval);
|
setRequirePlanApproval(defaultRequirePlanApproval);
|
||||||
setModelEntry({ model: 'opus' });
|
setModelEntry(defaultFeatureModel);
|
||||||
|
|
||||||
// Initialize description history (empty for new feature)
|
// Initialize description history (empty for new feature)
|
||||||
setDescriptionHistory([]);
|
setDescriptionHistory([]);
|
||||||
@@ -228,6 +231,7 @@ export function AddFeatureDialog({
|
|||||||
defaultBranch,
|
defaultBranch,
|
||||||
defaultPlanningMode,
|
defaultPlanningMode,
|
||||||
defaultRequirePlanApproval,
|
defaultRequirePlanApproval,
|
||||||
|
defaultFeatureModel,
|
||||||
useWorktrees,
|
useWorktrees,
|
||||||
selectedNonMainWorktreeBranch,
|
selectedNonMainWorktreeBranch,
|
||||||
forceCurrentBranchMode,
|
forceCurrentBranchMode,
|
||||||
@@ -318,7 +322,7 @@ export function AddFeatureDialog({
|
|||||||
// When a non-main worktree is selected, use its branch name for custom mode
|
// When a non-main worktree is selected, use its branch name for custom mode
|
||||||
setBranchName(selectedNonMainWorktreeBranch || '');
|
setBranchName(selectedNonMainWorktreeBranch || '');
|
||||||
setPriority(2);
|
setPriority(2);
|
||||||
setModelEntry({ model: 'opus' });
|
setModelEntry(defaultFeatureModel);
|
||||||
setWorkMode(
|
setWorkMode(
|
||||||
getDefaultWorkMode(useWorktrees, selectedNonMainWorktreeBranch, forceCurrentBranchMode)
|
getDefaultWorkMode(useWorktrees, selectedNonMainWorktreeBranch, forceCurrentBranchMode)
|
||||||
);
|
);
|
||||||
@@ -473,9 +477,31 @@ export function AddFeatureDialog({
|
|||||||
|
|
||||||
{/* AI & Execution Section */}
|
{/* AI & Execution Section */}
|
||||||
<div className={cardClass}>
|
<div className={cardClass}>
|
||||||
<div className={sectionHeaderClass}>
|
<div className="flex items-center justify-between">
|
||||||
<Cpu className="w-4 h-4 text-muted-foreground" />
|
<div className={sectionHeaderClass}>
|
||||||
<span>AI & Execution</span>
|
<Cpu className="w-4 h-4 text-muted-foreground" />
|
||||||
|
<span>AI & Execution</span>
|
||||||
|
</div>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
onOpenChange(false);
|
||||||
|
navigate({ to: '/settings', search: { view: 'defaults' } });
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
<Settings2 className="w-3.5 h-3.5" />
|
||||||
|
<span>Edit Defaults</span>
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Change default model and planning settings for new features</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ import {
|
|||||||
FeatureTextFilePath as DescriptionTextFilePath,
|
FeatureTextFilePath as DescriptionTextFilePath,
|
||||||
ImagePreviewMap,
|
ImagePreviewMap,
|
||||||
} from '@/components/ui/description-image-dropzone';
|
} from '@/components/ui/description-image-dropzone';
|
||||||
import { GitBranch, Cpu, FolderKanban } from 'lucide-react';
|
import { GitBranch, Cpu, FolderKanban, Settings2 } from 'lucide-react';
|
||||||
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { cn, modelSupportsThinking } from '@/lib/utils';
|
import { cn, modelSupportsThinking } from '@/lib/utils';
|
||||||
import { Feature, ModelAlias, ThinkingLevel, useAppStore, PlanningMode } from '@/store/app-store';
|
import { Feature, ModelAlias, ThinkingLevel, useAppStore, PlanningMode } from '@/store/app-store';
|
||||||
@@ -86,6 +87,7 @@ export function EditFeatureDialog({
|
|||||||
isMaximized,
|
isMaximized,
|
||||||
allFeatures,
|
allFeatures,
|
||||||
}: EditFeatureDialogProps) {
|
}: EditFeatureDialogProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
const [editingFeature, setEditingFeature] = useState<Feature | null>(feature);
|
const [editingFeature, setEditingFeature] = useState<Feature | null>(feature);
|
||||||
// Derive initial workMode from feature's branchName
|
// Derive initial workMode from feature's branchName
|
||||||
const [workMode, setWorkMode] = useState<WorkMode>(() => {
|
const [workMode, setWorkMode] = useState<WorkMode>(() => {
|
||||||
@@ -363,9 +365,31 @@ export function EditFeatureDialog({
|
|||||||
|
|
||||||
{/* AI & Execution Section */}
|
{/* AI & Execution Section */}
|
||||||
<div className={cardClass}>
|
<div className={cardClass}>
|
||||||
<div className={sectionHeaderClass}>
|
<div className="flex items-center justify-between">
|
||||||
<Cpu className="w-4 h-4 text-muted-foreground" />
|
<div className={sectionHeaderClass}>
|
||||||
<span>AI & Execution</span>
|
<Cpu className="w-4 h-4 text-muted-foreground" />
|
||||||
|
<span>AI & Execution</span>
|
||||||
|
</div>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
onClose();
|
||||||
|
navigate({ to: '/settings', search: { view: 'defaults' } });
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
<Settings2 className="w-3.5 h-3.5" />
|
||||||
|
<span>Edit Defaults</span>
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Change default model and planning settings for new features</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { useSearch } from '@tanstack/react-router';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
|
||||||
|
|
||||||
import { useSettingsView, type SettingsViewId } from './settings-view/hooks';
|
import { useSettingsView, type SettingsViewId } from './settings-view/hooks';
|
||||||
import { NAV_ITEMS } from './settings-view/config/navigation';
|
import { NAV_ITEMS } from './settings-view/config/navigation';
|
||||||
@@ -51,6 +51,8 @@ export function SettingsView() {
|
|||||||
setDefaultPlanningMode,
|
setDefaultPlanningMode,
|
||||||
defaultRequirePlanApproval,
|
defaultRequirePlanApproval,
|
||||||
setDefaultRequirePlanApproval,
|
setDefaultRequirePlanApproval,
|
||||||
|
defaultFeatureModel,
|
||||||
|
setDefaultFeatureModel,
|
||||||
autoLoadClaudeMd,
|
autoLoadClaudeMd,
|
||||||
setAutoLoadClaudeMd,
|
setAutoLoadClaudeMd,
|
||||||
promptCustomization,
|
promptCustomization,
|
||||||
@@ -86,8 +88,11 @@ export function SettingsView() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get initial view from URL search params
|
||||||
|
const { view: initialView } = useSearch({ from: '/settings' });
|
||||||
|
|
||||||
// Use settings view navigation hook
|
// Use settings view navigation hook
|
||||||
const { activeView, navigateTo } = useSettingsView();
|
const { activeView, navigateTo } = useSettingsView({ initialView });
|
||||||
|
|
||||||
// Handle navigation - if navigating to 'providers', default to 'claude-provider'
|
// Handle navigation - if navigating to 'providers', default to 'claude-provider'
|
||||||
const handleNavigate = (viewId: SettingsViewId) => {
|
const handleNavigate = (viewId: SettingsViewId) => {
|
||||||
@@ -152,11 +157,13 @@ export function SettingsView() {
|
|||||||
skipVerificationInAutoMode={skipVerificationInAutoMode}
|
skipVerificationInAutoMode={skipVerificationInAutoMode}
|
||||||
defaultPlanningMode={defaultPlanningMode}
|
defaultPlanningMode={defaultPlanningMode}
|
||||||
defaultRequirePlanApproval={defaultRequirePlanApproval}
|
defaultRequirePlanApproval={defaultRequirePlanApproval}
|
||||||
|
defaultFeatureModel={defaultFeatureModel}
|
||||||
onDefaultSkipTestsChange={setDefaultSkipTests}
|
onDefaultSkipTestsChange={setDefaultSkipTests}
|
||||||
onEnableDependencyBlockingChange={setEnableDependencyBlocking}
|
onEnableDependencyBlockingChange={setEnableDependencyBlocking}
|
||||||
onSkipVerificationInAutoModeChange={setSkipVerificationInAutoMode}
|
onSkipVerificationInAutoModeChange={setSkipVerificationInAutoMode}
|
||||||
onDefaultPlanningModeChange={setDefaultPlanningMode}
|
onDefaultPlanningModeChange={setDefaultPlanningMode}
|
||||||
onDefaultRequirePlanApprovalChange={setDefaultRequirePlanApproval}
|
onDefaultRequirePlanApprovalChange={setDefaultRequirePlanApproval}
|
||||||
|
onDefaultFeatureModelChange={setDefaultFeatureModel}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'worktrees':
|
case 'worktrees':
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ export const GLOBAL_NAV_GROUPS: NavigationGroup[] = [
|
|||||||
{
|
{
|
||||||
label: 'Model & Prompts',
|
label: 'Model & Prompts',
|
||||||
items: [
|
items: [
|
||||||
{ id: 'model-defaults', label: 'Model Defaults', icon: Workflow },
|
|
||||||
{ id: 'defaults', label: 'Feature Defaults', icon: FlaskConical },
|
{ id: 'defaults', label: 'Feature Defaults', icon: FlaskConical },
|
||||||
|
{ id: 'model-defaults', label: 'Model Defaults', icon: Workflow },
|
||||||
{ id: 'worktrees', label: 'Worktrees', icon: GitBranch },
|
{ id: 'worktrees', label: 'Worktrees', icon: GitBranch },
|
||||||
{ id: 'prompts', label: 'Prompt Customization', icon: MessageSquareText },
|
{ id: 'prompts', label: 'Prompt Customization', icon: MessageSquareText },
|
||||||
{ id: 'api-keys', label: 'API Keys', icon: Key },
|
{ id: 'api-keys', label: 'API Keys', icon: Key },
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ScrollText,
|
ScrollText,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
FastForward,
|
FastForward,
|
||||||
|
Cpu,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
@@ -19,6 +20,8 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
|
import type { PhaseModelEntry } from '@automaker/types';
|
||||||
|
import { PhaseModelSelector } from '../model-defaults/phase-model-selector';
|
||||||
|
|
||||||
type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
|
type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
|
||||||
|
|
||||||
@@ -28,11 +31,13 @@ interface FeatureDefaultsSectionProps {
|
|||||||
skipVerificationInAutoMode: boolean;
|
skipVerificationInAutoMode: boolean;
|
||||||
defaultPlanningMode: PlanningMode;
|
defaultPlanningMode: PlanningMode;
|
||||||
defaultRequirePlanApproval: boolean;
|
defaultRequirePlanApproval: boolean;
|
||||||
|
defaultFeatureModel: PhaseModelEntry;
|
||||||
onDefaultSkipTestsChange: (value: boolean) => void;
|
onDefaultSkipTestsChange: (value: boolean) => void;
|
||||||
onEnableDependencyBlockingChange: (value: boolean) => void;
|
onEnableDependencyBlockingChange: (value: boolean) => void;
|
||||||
onSkipVerificationInAutoModeChange: (value: boolean) => void;
|
onSkipVerificationInAutoModeChange: (value: boolean) => void;
|
||||||
onDefaultPlanningModeChange: (value: PlanningMode) => void;
|
onDefaultPlanningModeChange: (value: PlanningMode) => void;
|
||||||
onDefaultRequirePlanApprovalChange: (value: boolean) => void;
|
onDefaultRequirePlanApprovalChange: (value: boolean) => void;
|
||||||
|
onDefaultFeatureModelChange: (value: PhaseModelEntry) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeatureDefaultsSection({
|
export function FeatureDefaultsSection({
|
||||||
@@ -41,11 +46,13 @@ export function FeatureDefaultsSection({
|
|||||||
skipVerificationInAutoMode,
|
skipVerificationInAutoMode,
|
||||||
defaultPlanningMode,
|
defaultPlanningMode,
|
||||||
defaultRequirePlanApproval,
|
defaultRequirePlanApproval,
|
||||||
|
defaultFeatureModel,
|
||||||
onDefaultSkipTestsChange,
|
onDefaultSkipTestsChange,
|
||||||
onEnableDependencyBlockingChange,
|
onEnableDependencyBlockingChange,
|
||||||
onSkipVerificationInAutoModeChange,
|
onSkipVerificationInAutoModeChange,
|
||||||
onDefaultPlanningModeChange,
|
onDefaultPlanningModeChange,
|
||||||
onDefaultRequirePlanApprovalChange,
|
onDefaultRequirePlanApprovalChange,
|
||||||
|
onDefaultFeatureModelChange,
|
||||||
}: FeatureDefaultsSectionProps) {
|
}: FeatureDefaultsSectionProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -68,6 +75,30 @@ export function FeatureDefaultsSection({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-6 space-y-5">
|
<div className="p-6 space-y-5">
|
||||||
|
{/* Default Feature Model Setting */}
|
||||||
|
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
|
||||||
|
<div className="w-10 h-10 mt-0.5 rounded-xl flex items-center justify-center shrink-0 bg-brand-500/10">
|
||||||
|
<Cpu className="w-5 h-5 text-brand-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Label className="text-foreground font-medium">Default Model</Label>
|
||||||
|
<PhaseModelSelector
|
||||||
|
value={defaultFeatureModel}
|
||||||
|
onChange={onDefaultFeatureModelChange}
|
||||||
|
compact
|
||||||
|
align="end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground/80 leading-relaxed">
|
||||||
|
The default AI model and thinking level used when creating new feature cards.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
|
<div className="border-t border-border/30" />
|
||||||
|
|
||||||
{/* Planning Mode Default */}
|
{/* Planning Mode Default */}
|
||||||
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
|
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
|
||||||
<div
|
<div
|
||||||
@@ -165,12 +196,11 @@ export function FeatureDefaultsSection({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-border/30" />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Separator */}
|
{/* Separator */}
|
||||||
{defaultPlanningMode === 'skip' && <div className="border-t border-border/30" />}
|
<div className="border-t border-border/30" />
|
||||||
|
|
||||||
{/* Automated Testing Setting */}
|
{/* Automated Testing Setting */}
|
||||||
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
|
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
|
||||||
|
|||||||
@@ -562,6 +562,7 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
|
|||||||
useWorktrees: settings.useWorktrees ?? true,
|
useWorktrees: settings.useWorktrees ?? true,
|
||||||
defaultPlanningMode: settings.defaultPlanningMode ?? 'skip',
|
defaultPlanningMode: settings.defaultPlanningMode ?? 'skip',
|
||||||
defaultRequirePlanApproval: settings.defaultRequirePlanApproval ?? false,
|
defaultRequirePlanApproval: settings.defaultRequirePlanApproval ?? false,
|
||||||
|
defaultFeatureModel: settings.defaultFeatureModel ?? { model: 'opus' },
|
||||||
muteDoneSound: settings.muteDoneSound ?? false,
|
muteDoneSound: settings.muteDoneSound ?? false,
|
||||||
enhancementModel: settings.enhancementModel ?? 'sonnet',
|
enhancementModel: settings.enhancementModel ?? 'sonnet',
|
||||||
validationModel: settings.validationModel ?? 'opus',
|
validationModel: settings.validationModel ?? 'opus',
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const SETTINGS_FIELDS_TO_SYNC = [
|
|||||||
'useWorktrees',
|
'useWorktrees',
|
||||||
'defaultPlanningMode',
|
'defaultPlanningMode',
|
||||||
'defaultRequirePlanApproval',
|
'defaultRequirePlanApproval',
|
||||||
|
'defaultFeatureModel',
|
||||||
'muteDoneSound',
|
'muteDoneSound',
|
||||||
'enhancementModel',
|
'enhancementModel',
|
||||||
'validationModel',
|
'validationModel',
|
||||||
@@ -466,6 +467,7 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
|
|||||||
useWorktrees: serverSettings.useWorktrees,
|
useWorktrees: serverSettings.useWorktrees,
|
||||||
defaultPlanningMode: serverSettings.defaultPlanningMode,
|
defaultPlanningMode: serverSettings.defaultPlanningMode,
|
||||||
defaultRequirePlanApproval: serverSettings.defaultRequirePlanApproval,
|
defaultRequirePlanApproval: serverSettings.defaultRequirePlanApproval,
|
||||||
|
defaultFeatureModel: serverSettings.defaultFeatureModel ?? { model: 'opus' },
|
||||||
muteDoneSound: serverSettings.muteDoneSound,
|
muteDoneSound: serverSettings.muteDoneSound,
|
||||||
enhancementModel: serverSettings.enhancementModel,
|
enhancementModel: serverSettings.enhancementModel,
|
||||||
validationModel: serverSettings.validationModel,
|
validationModel: serverSettings.validationModel,
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import { SettingsView } from '@/components/views/settings-view';
|
import { SettingsView } from '@/components/views/settings-view';
|
||||||
|
import type { SettingsViewId } from '@/components/views/settings-view/hooks';
|
||||||
|
|
||||||
|
interface SettingsSearchParams {
|
||||||
|
view?: SettingsViewId;
|
||||||
|
}
|
||||||
|
|
||||||
export const Route = createFileRoute('/settings')({
|
export const Route = createFileRoute('/settings')({
|
||||||
component: SettingsView,
|
component: SettingsView,
|
||||||
|
validateSearch: (search: Record<string, unknown>): SettingsSearchParams => {
|
||||||
|
return {
|
||||||
|
view: search.view as SettingsViewId | undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -657,6 +657,7 @@ export interface AppState {
|
|||||||
|
|
||||||
defaultPlanningMode: PlanningMode;
|
defaultPlanningMode: PlanningMode;
|
||||||
defaultRequirePlanApproval: boolean;
|
defaultRequirePlanApproval: boolean;
|
||||||
|
defaultFeatureModel: PhaseModelEntry;
|
||||||
|
|
||||||
// Plan Approval State
|
// Plan Approval State
|
||||||
// When a plan requires user approval, this holds the pending approval details
|
// When a plan requires user approval, this holds the pending approval details
|
||||||
@@ -1104,6 +1105,7 @@ export interface AppActions {
|
|||||||
|
|
||||||
setDefaultPlanningMode: (mode: PlanningMode) => void;
|
setDefaultPlanningMode: (mode: PlanningMode) => void;
|
||||||
setDefaultRequirePlanApproval: (require: boolean) => void;
|
setDefaultRequirePlanApproval: (require: boolean) => void;
|
||||||
|
setDefaultFeatureModel: (entry: PhaseModelEntry) => void;
|
||||||
|
|
||||||
// Plan Approval actions
|
// Plan Approval actions
|
||||||
setPendingPlanApproval: (
|
setPendingPlanApproval: (
|
||||||
@@ -1277,6 +1279,7 @@ const initialState: AppState = {
|
|||||||
specCreatingForProject: null,
|
specCreatingForProject: null,
|
||||||
defaultPlanningMode: 'skip' as PlanningMode,
|
defaultPlanningMode: 'skip' as PlanningMode,
|
||||||
defaultRequirePlanApproval: false,
|
defaultRequirePlanApproval: false,
|
||||||
|
defaultFeatureModel: { model: 'opus' } as PhaseModelEntry,
|
||||||
pendingPlanApproval: null,
|
pendingPlanApproval: null,
|
||||||
claudeRefreshInterval: 60,
|
claudeRefreshInterval: 60,
|
||||||
claudeUsage: null,
|
claudeUsage: null,
|
||||||
@@ -3093,6 +3096,7 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
|
|
||||||
setDefaultPlanningMode: (mode) => set({ defaultPlanningMode: mode }),
|
setDefaultPlanningMode: (mode) => set({ defaultPlanningMode: mode }),
|
||||||
setDefaultRequirePlanApproval: (require) => set({ defaultRequirePlanApproval: require }),
|
setDefaultRequirePlanApproval: (require) => set({ defaultRequirePlanApproval: require }),
|
||||||
|
setDefaultFeatureModel: (entry) => set({ defaultFeatureModel: entry }),
|
||||||
|
|
||||||
// Plan Approval actions
|
// Plan Approval actions
|
||||||
setPendingPlanApproval: (approval) => set({ pendingPlanApproval: approval }),
|
setPendingPlanApproval: (approval) => set({ pendingPlanApproval: approval }),
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ services:
|
|||||||
# This shares your 'cursor-agent login' OAuth session with the container
|
# This shares your 'cursor-agent login' OAuth session with the container
|
||||||
# - ~/.cursor:/home/automaker/.cursor
|
# - ~/.cursor:/home/automaker/.cursor
|
||||||
|
|
||||||
|
# OpenCode CLI - mount your ~/.local/share/opencode directory
|
||||||
|
# This shares your 'opencode auth login' session with the container
|
||||||
|
# - ~/.local/share/opencode:/home/automaker/.local/share/opencode
|
||||||
|
# - ~/.config/opencode:/home/automaker/.config/opencode
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
# Set root directory for all projects and file operations
|
# Set root directory for all projects and file operations
|
||||||
# Users can only create/open projects within this directory
|
# Users can only create/open projects within this directory
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ services:
|
|||||||
# This allows 'cursor-agent login' authentication to persist between restarts
|
# This allows 'cursor-agent login' authentication to persist between restarts
|
||||||
- automaker-cursor-config:/home/automaker/.cursor
|
- automaker-cursor-config:/home/automaker/.cursor
|
||||||
|
|
||||||
|
# Persist OpenCode CLI configuration and authentication across container restarts
|
||||||
|
# This allows 'opencode auth login' authentication to persist between restarts
|
||||||
|
- automaker-opencode-data:/home/automaker/.local/share/opencode
|
||||||
|
|
||||||
|
# Persist OpenCode user configuration across container restarts
|
||||||
|
- automaker-opencode-config:/home/automaker/.config/opencode
|
||||||
|
|
||||||
# NO host directory mounts - container cannot access your laptop files
|
# NO host directory mounts - container cannot access your laptop files
|
||||||
# If you need to work on a project, create it INSIDE the container
|
# If you need to work on a project, create it INSIDE the container
|
||||||
# or use a separate docker-compose override file
|
# or use a separate docker-compose override file
|
||||||
@@ -106,3 +113,13 @@ volumes:
|
|||||||
name: automaker-cursor-config
|
name: automaker-cursor-config
|
||||||
# Named volume for Cursor CLI configuration and authentication
|
# Named volume for Cursor CLI configuration and authentication
|
||||||
# Persists cursor-agent login authentication across container restarts
|
# Persists cursor-agent login authentication across container restarts
|
||||||
|
|
||||||
|
automaker-opencode-data:
|
||||||
|
name: automaker-opencode-data
|
||||||
|
# Named volume for OpenCode CLI data and authentication (~/.local/share/opencode)
|
||||||
|
# Persists opencode auth login authentication across container restarts
|
||||||
|
|
||||||
|
automaker-opencode-config:
|
||||||
|
name: automaker-opencode-config
|
||||||
|
# Named volume for OpenCode user configuration (~/.config/opencode)
|
||||||
|
# Persists user configuration across container restarts
|
||||||
|
|||||||
@@ -25,6 +25,21 @@ fi
|
|||||||
chown -R automaker:automaker /home/automaker/.cursor
|
chown -R automaker:automaker /home/automaker/.cursor
|
||||||
chmod -R 700 /home/automaker/.cursor
|
chmod -R 700 /home/automaker/.cursor
|
||||||
|
|
||||||
|
# Ensure OpenCode CLI config directory exists with correct permissions
|
||||||
|
# OpenCode stores config and auth in ~/.local/share/opencode/
|
||||||
|
if [ ! -d "/home/automaker/.local/share/opencode" ]; then
|
||||||
|
mkdir -p /home/automaker/.local/share/opencode
|
||||||
|
fi
|
||||||
|
chown -R automaker:automaker /home/automaker/.local/share/opencode
|
||||||
|
chmod -R 700 /home/automaker/.local/share/opencode
|
||||||
|
|
||||||
|
# OpenCode also uses ~/.config/opencode for configuration
|
||||||
|
if [ ! -d "/home/automaker/.config/opencode" ]; then
|
||||||
|
mkdir -p /home/automaker/.config/opencode
|
||||||
|
fi
|
||||||
|
chown -R automaker:automaker /home/automaker/.config/opencode
|
||||||
|
chmod -R 700 /home/automaker/.config/opencode
|
||||||
|
|
||||||
# If CURSOR_AUTH_TOKEN is set, write it to the cursor auth file
|
# If CURSOR_AUTH_TOKEN is set, write it to the cursor auth file
|
||||||
# On Linux, cursor-agent uses ~/.config/cursor/auth.json for file-based credential storage
|
# On Linux, cursor-agent uses ~/.config/cursor/auth.json for file-based credential storage
|
||||||
# The env var CURSOR_AUTH_TOKEN is also checked directly by cursor-agent
|
# The env var CURSOR_AUTH_TOKEN is also checked directly by cursor-agent
|
||||||
|
|||||||
@@ -94,6 +94,16 @@ echo "CURSOR_AUTH_TOKEN=$(./scripts/get-cursor-token.sh)" >> .env
|
|||||||
- **macOS**: Tokens are stored in Keychain (service: `cursor-access-token`)
|
- **macOS**: Tokens are stored in Keychain (service: `cursor-access-token`)
|
||||||
- **Linux**: Tokens are stored in `~/.config/cursor/auth.json` (not `~/.cursor`)
|
- **Linux**: Tokens are stored in `~/.config/cursor/auth.json` (not `~/.cursor`)
|
||||||
|
|
||||||
|
### OpenCode CLI
|
||||||
|
|
||||||
|
OpenCode stores its configuration and auth at `~/.local/share/opencode/`. To share your host authentication with the container:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# In docker-compose.override.yml
|
||||||
|
volumes:
|
||||||
|
- ~/.local/share/opencode:/home/automaker/.local/share/opencode
|
||||||
|
```
|
||||||
|
|
||||||
### Apply to container
|
### Apply to container
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -121,6 +131,7 @@ echo "CURSOR_AUTH_TOKEN=$(jq -r '.accessToken' ~/.config/cursor/auth.json)" >> .
|
|||||||
volumes:
|
volumes:
|
||||||
- ~/.claude:/home/automaker/.claude
|
- ~/.claude:/home/automaker/.claude
|
||||||
- ~/.config/cursor:/home/automaker/.config/cursor
|
- ~/.config/cursor:/home/automaker/.config/cursor
|
||||||
|
- ~/.local/share/opencode:/home/automaker/.local/share/opencode
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
@@ -131,4 +142,6 @@ volumes:
|
|||||||
| Can't access web UI | Verify container is running with `docker ps \| grep automaker` |
|
| Can't access web UI | Verify container is running with `docker ps \| grep automaker` |
|
||||||
| Need a fresh start | Run `docker-compose down && docker volume rm automaker-data && docker-compose up -d --build` |
|
| Need a fresh start | Run `docker-compose down && docker volume rm automaker-data && docker-compose up -d --build` |
|
||||||
| Cursor auth fails | Re-extract token with `./scripts/get-cursor-token.sh` - tokens expire periodically. Make sure you've run `cursor-agent login` on your host first. |
|
| Cursor auth fails | Re-extract token with `./scripts/get-cursor-token.sh` - tokens expire periodically. Make sure you've run `cursor-agent login` on your host first. |
|
||||||
|
| OpenCode not detected | Mount `~/.local/share/opencode` to `/home/automaker/.local/share/opencode`. Make sure you've run `opencode auth login` on your host first. |
|
||||||
| File permission errors | Rebuild with `UID=$(id -u) GID=$(id -g) docker-compose build` to match container user to your host user. See [Fixing File Permission Issues](#fixing-file-permission-issues). |
|
| File permission errors | Rebuild with `UID=$(id -u) GID=$(id -g) docker-compose build` to match container user to your host user. See [Fixing File Permission Issues](#fixing-file-permission-issues). |
|
||||||
|
|
||||||
|
|||||||
@@ -375,6 +375,8 @@ export interface GlobalSettings {
|
|||||||
defaultPlanningMode: PlanningMode;
|
defaultPlanningMode: PlanningMode;
|
||||||
/** Default: require manual approval before generating */
|
/** Default: require manual approval before generating */
|
||||||
defaultRequirePlanApproval: boolean;
|
defaultRequirePlanApproval: boolean;
|
||||||
|
/** Default model and thinking level for new feature cards */
|
||||||
|
defaultFeatureModel: PhaseModelEntry;
|
||||||
|
|
||||||
// Audio Preferences
|
// Audio Preferences
|
||||||
/** Mute completion notification sound */
|
/** Mute completion notification sound */
|
||||||
@@ -698,6 +700,7 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
|
|||||||
useWorktrees: true,
|
useWorktrees: true,
|
||||||
defaultPlanningMode: 'skip',
|
defaultPlanningMode: 'skip',
|
||||||
defaultRequirePlanApproval: false,
|
defaultRequirePlanApproval: false,
|
||||||
|
defaultFeatureModel: { model: 'opus' },
|
||||||
muteDoneSound: false,
|
muteDoneSound: false,
|
||||||
phaseModels: DEFAULT_PHASE_MODELS,
|
phaseModels: DEFAULT_PHASE_MODELS,
|
||||||
enhancementModel: 'sonnet',
|
enhancementModel: 'sonnet',
|
||||||
|
|||||||
Reference in New Issue
Block a user