mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-21 11:23:07 +00:00
feat: Improve callback safety and remove unnecessary formatting in auto-mode facade
This commit is contained in:
@@ -126,9 +126,7 @@ export class AgentExecutor {
|
|||||||
const appendRawEvent = (event: unknown): void => {
|
const appendRawEvent = (event: unknown): void => {
|
||||||
if (!enableRawOutput) return;
|
if (!enableRawOutput) return;
|
||||||
try {
|
try {
|
||||||
rawOutputLines.push(
|
rawOutputLines.push(JSON.stringify({ timestamp: new Date().toISOString(), event }));
|
||||||
JSON.stringify({ timestamp: new Date().toISOString(), event }, null, 4)
|
|
||||||
);
|
|
||||||
if (rawWriteTimeout) clearTimeout(rawWriteTimeout);
|
if (rawWriteTimeout) clearTimeout(rawWriteTimeout);
|
||||||
rawWriteTimeout = setTimeout(async () => {
|
rawWriteTimeout = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -552,7 +550,7 @@ export class AgentExecutor {
|
|||||||
});
|
});
|
||||||
let revText = '';
|
let revText = '';
|
||||||
for await (const msg of provider.executeQuery(
|
for await (const msg of provider.executeQuery(
|
||||||
this.buildExecOpts(options, revPrompt, sdkOptions?.maxTurns || 100)
|
this.buildExecOpts(options, revPrompt, sdkOptions?.maxTurns ?? 100)
|
||||||
)) {
|
)) {
|
||||||
if (msg.type === 'assistant' && msg.message?.content)
|
if (msg.type === 'assistant' && msg.message?.content)
|
||||||
for (const b of msg.message.content)
|
for (const b of msg.message.content)
|
||||||
|
|||||||
@@ -143,9 +143,20 @@ export class AutoModeServiceFacade {
|
|||||||
return prompt;
|
return prompt;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create placeholder callbacks - will be bound to facade methods after creation
|
// Create placeholder callbacks - will be bound to facade methods after creation.
|
||||||
// These use closures to capture the facade instance once created
|
// These use closures to capture the facade instance once created.
|
||||||
|
// INVARIANT: All callbacks passed to PipelineOrchestrator, AutoLoopCoordinator,
|
||||||
|
// and ExecutionService are invoked asynchronously (never during construction),
|
||||||
|
// so facadeInstance is guaranteed to be assigned before any callback runs.
|
||||||
let facadeInstance: AutoModeServiceFacade | null = null;
|
let facadeInstance: AutoModeServiceFacade | null = null;
|
||||||
|
const getFacade = (): AutoModeServiceFacade => {
|
||||||
|
if (!facadeInstance) {
|
||||||
|
throw new Error(
|
||||||
|
'AutoModeServiceFacade not yet initialized — callback invoked during construction'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return facadeInstance;
|
||||||
|
};
|
||||||
|
|
||||||
// PipelineOrchestrator - runAgentFn is a stub; routes use AutoModeService directly
|
// PipelineOrchestrator - runAgentFn is a stub; routes use AutoModeService directly
|
||||||
const pipelineOrchestrator = new PipelineOrchestrator(
|
const pipelineOrchestrator = new PipelineOrchestrator(
|
||||||
@@ -162,7 +173,7 @@ export class AutoModeServiceFacade {
|
|||||||
loadContextFiles,
|
loadContextFiles,
|
||||||
buildFeaturePrompt,
|
buildFeaturePrompt,
|
||||||
(pPath, featureId, useWorktrees, _isAutoMode, _model, opts) =>
|
(pPath, featureId, useWorktrees, _isAutoMode, _model, opts) =>
|
||||||
facadeInstance!.executeFeature(featureId, useWorktrees, false, undefined, opts),
|
getFacade().executeFeature(featureId, useWorktrees, false, undefined, opts),
|
||||||
// runAgentFn - delegates to AgentExecutor
|
// runAgentFn - delegates to AgentExecutor
|
||||||
async (
|
async (
|
||||||
workDir: string,
|
workDir: string,
|
||||||
@@ -227,7 +238,7 @@ export class AutoModeServiceFacade {
|
|||||||
.replace(/\{\{taskName\}\}/g, task.description)
|
.replace(/\{\{taskName\}\}/g, task.description)
|
||||||
.replace(/\{\{taskIndex\}\}/g, String(taskIndex + 1))
|
.replace(/\{\{taskIndex\}\}/g, String(taskIndex + 1))
|
||||||
.replace(/\{\{totalTasks\}\}/g, String(allTasks.length))
|
.replace(/\{\{totalTasks\}\}/g, String(allTasks.length))
|
||||||
.replace(/\{\{taskDescription\}\}/g, task.description || task.name);
|
.replace(/\{\{taskDescription\}\}/g, task.description || `Task ${task.id}`);
|
||||||
if (feedback) {
|
if (feedback) {
|
||||||
taskPrompt = taskPrompt.replace(/\{\{userFeedback\}\}/g, feedback);
|
taskPrompt = taskPrompt.replace(/\{\{userFeedback\}\}/g, feedback);
|
||||||
}
|
}
|
||||||
@@ -248,7 +259,7 @@ export class AutoModeServiceFacade {
|
|||||||
settingsService,
|
settingsService,
|
||||||
// Callbacks
|
// Callbacks
|
||||||
(pPath, featureId, useWorktrees, isAutoMode) =>
|
(pPath, featureId, useWorktrees, isAutoMode) =>
|
||||||
facadeInstance!.executeFeature(featureId, useWorktrees, isAutoMode),
|
getFacade().executeFeature(featureId, useWorktrees, isAutoMode),
|
||||||
async (pPath, branchName) => {
|
async (pPath, branchName) => {
|
||||||
const features = await featureLoader.getAll(pPath);
|
const features = await featureLoader.getAll(pPath);
|
||||||
// For main worktree (branchName === null), resolve the actual primary branch name
|
// For main worktree (branchName === null), resolve the actual primary branch name
|
||||||
@@ -266,8 +277,8 @@ export class AutoModeServiceFacade {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
(pPath, branchName, maxConcurrency) =>
|
(pPath, branchName, maxConcurrency) =>
|
||||||
facadeInstance!.saveExecutionStateForProject(branchName, maxConcurrency),
|
getFacade().saveExecutionStateForProject(branchName, maxConcurrency),
|
||||||
(pPath, branchName) => facadeInstance!.clearExecutionState(branchName),
|
(pPath, branchName) => getFacade().clearExecutionState(branchName),
|
||||||
(pPath) => featureStateManager.resetStuckFeatures(pPath),
|
(pPath) => featureStateManager.resetStuckFeatures(pPath),
|
||||||
(feature) =>
|
(feature) =>
|
||||||
feature.status === 'completed' ||
|
feature.status === 'completed' ||
|
||||||
@@ -375,16 +386,16 @@ export class AutoModeServiceFacade {
|
|||||||
async () => {
|
async () => {
|
||||||
/* recordLearnings - stub */
|
/* recordLearnings - stub */
|
||||||
},
|
},
|
||||||
(pPath, featureId) => facadeInstance!.contextExists(featureId),
|
(pPath, featureId) => getFacade().contextExists(featureId),
|
||||||
(pPath, featureId, useWorktrees, _calledInternally) =>
|
(pPath, featureId, useWorktrees, _calledInternally) =>
|
||||||
facadeInstance!.resumeFeature(featureId, useWorktrees, _calledInternally),
|
getFacade().resumeFeature(featureId, useWorktrees, _calledInternally),
|
||||||
(errorInfo) =>
|
(errorInfo) =>
|
||||||
autoLoopCoordinator.trackFailureAndCheckPauseForProject(projectPath, null, errorInfo),
|
autoLoopCoordinator.trackFailureAndCheckPauseForProject(projectPath, null, errorInfo),
|
||||||
(errorInfo) => autoLoopCoordinator.signalShouldPauseForProject(projectPath, null, errorInfo),
|
(errorInfo) => autoLoopCoordinator.signalShouldPauseForProject(projectPath, null, errorInfo),
|
||||||
() => {
|
() => {
|
||||||
/* recordSuccess - no-op */
|
/* recordSuccess - no-op */
|
||||||
},
|
},
|
||||||
(_pPath) => facadeInstance!.saveExecutionState(),
|
(_pPath) => getFacade().saveExecutionState(),
|
||||||
loadContextFiles
|
loadContextFiles
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -395,13 +406,7 @@ export class AutoModeServiceFacade {
|
|||||||
settingsService,
|
settingsService,
|
||||||
// Callbacks
|
// Callbacks
|
||||||
(pPath, featureId, useWorktrees, isAutoMode, providedWorktreePath, opts) =>
|
(pPath, featureId, useWorktrees, isAutoMode, providedWorktreePath, opts) =>
|
||||||
facadeInstance!.executeFeature(
|
getFacade().executeFeature(featureId, useWorktrees, isAutoMode, providedWorktreePath, opts),
|
||||||
featureId,
|
|
||||||
useWorktrees,
|
|
||||||
isAutoMode,
|
|
||||||
providedWorktreePath,
|
|
||||||
opts
|
|
||||||
),
|
|
||||||
(pPath, featureId) => featureStateManager.loadFeature(pPath, featureId),
|
(pPath, featureId) => featureStateManager.loadFeature(pPath, featureId),
|
||||||
(pPath, featureId, status) =>
|
(pPath, featureId, status) =>
|
||||||
pipelineOrchestrator.detectPipelineStatus(pPath, featureId, status),
|
pipelineOrchestrator.detectPipelineStatus(pPath, featureId, status),
|
||||||
@@ -547,7 +552,9 @@ export class AutoModeServiceFacade {
|
|||||||
imagePaths?: string[],
|
imagePaths?: string[],
|
||||||
useWorktrees = true
|
useWorktrees = true
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// This method contains substantial logic - delegates most work to AgentExecutor
|
// Stub: acquire concurrency slot then immediately throw.
|
||||||
|
// Heavy I/O (loadFeature, worktree resolution, context reading, prompt building)
|
||||||
|
// is deferred to the real AutoModeService.followUpFeature implementation.
|
||||||
validateWorkingDirectory(this.projectPath);
|
validateWorkingDirectory(this.projectPath);
|
||||||
|
|
||||||
const runningEntry = this.concurrencyManager.acquire({
|
const runningEntry = this.concurrencyManager.acquire({
|
||||||
@@ -555,56 +562,6 @@ export class AutoModeServiceFacade {
|
|||||||
projectPath: this.projectPath,
|
projectPath: this.projectPath,
|
||||||
isAutoMode: false,
|
isAutoMode: false,
|
||||||
});
|
});
|
||||||
const abortController = runningEntry.abortController;
|
|
||||||
|
|
||||||
const feature = await this.featureStateManager.loadFeature(this.projectPath, featureId);
|
|
||||||
let workDir = path.resolve(this.projectPath);
|
|
||||||
let worktreePath: string | null = null;
|
|
||||||
const branchName = feature?.branchName || `feature/${featureId}`;
|
|
||||||
|
|
||||||
if (useWorktrees && branchName) {
|
|
||||||
worktreePath = await this.worktreeResolver.findWorktreeForBranch(
|
|
||||||
this.projectPath,
|
|
||||||
branchName
|
|
||||||
);
|
|
||||||
if (worktreePath) {
|
|
||||||
workDir = worktreePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load previous context
|
|
||||||
const featureDir = getFeatureDir(this.projectPath, featureId);
|
|
||||||
const contextPath = path.join(featureDir, 'agent-output.md');
|
|
||||||
let previousContext = '';
|
|
||||||
try {
|
|
||||||
previousContext = (await secureFs.readFile(contextPath, 'utf-8')) as string;
|
|
||||||
} catch {
|
|
||||||
// No previous context
|
|
||||||
}
|
|
||||||
|
|
||||||
const prompts = await getPromptCustomization(this.settingsService, '[Facade]');
|
|
||||||
|
|
||||||
// Build follow-up prompt inline (no template in TaskExecutionPrompts)
|
|
||||||
let fullPrompt = `## Follow-up on Feature Implementation
|
|
||||||
|
|
||||||
${feature ? `**Feature ID:** ${feature.id}\n**Title:** ${feature.title || 'Untitled'}\n**Description:** ${feature.description}` : `**Feature ID:** ${featureId}`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (previousContext) {
|
|
||||||
fullPrompt += `
|
|
||||||
## Previous Agent Work
|
|
||||||
The following is the output from the previous implementation attempt:
|
|
||||||
|
|
||||||
${previousContext}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
fullPrompt += `
|
|
||||||
## Follow-up Instructions
|
|
||||||
${prompt}
|
|
||||||
|
|
||||||
## Task
|
|
||||||
Address the follow-up instructions above. Review the previous work and make the requested changes or fixes.`;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// NOTE: Facade does not have runAgent - this method requires AutoModeService
|
// NOTE: Facade does not have runAgent - this method requires AutoModeService
|
||||||
@@ -617,8 +574,8 @@ Address the follow-up instructions above. Review the previous work and make the
|
|||||||
if (!errorInfo.isAbort) {
|
if (!errorInfo.isAbort) {
|
||||||
this.eventBus.emitAutoModeEvent('auto_mode_error', {
|
this.eventBus.emitAutoModeEvent('auto_mode_error', {
|
||||||
featureId,
|
featureId,
|
||||||
featureName: feature?.title,
|
featureName: undefined,
|
||||||
branchName: feature?.branchName ?? null,
|
branchName: null,
|
||||||
error: errorInfo.message,
|
error: errorInfo.message,
|
||||||
errorType: errorInfo.type,
|
errorType: errorInfo.type,
|
||||||
projectPath: this.projectPath,
|
projectPath: this.projectPath,
|
||||||
@@ -854,7 +811,9 @@ Address the follow-up instructions above. Review the previous work and make the
|
|||||||
async checkWorktreeCapacity(featureId: string): Promise<WorktreeCapacityInfo> {
|
async checkWorktreeCapacity(featureId: string): Promise<WorktreeCapacityInfo> {
|
||||||
const feature = await this.featureStateManager.loadFeature(this.projectPath, featureId);
|
const feature = await this.featureStateManager.loadFeature(this.projectPath, featureId);
|
||||||
const rawBranchName = feature?.branchName ?? null;
|
const rawBranchName = feature?.branchName ?? null;
|
||||||
const branchName = rawBranchName === 'main' ? null : rawBranchName;
|
// Normalize primary branch to null (works for main, master, or any default branch)
|
||||||
|
const primaryBranch = await this.worktreeResolver.getCurrentBranch(this.projectPath);
|
||||||
|
const branchName = rawBranchName === primaryBranch ? null : rawBranchName;
|
||||||
|
|
||||||
const maxAgents = await this.autoLoopCoordinator.resolveMaxConcurrency(
|
const maxAgents = await this.autoLoopCoordinator.resolveMaxConcurrency(
|
||||||
this.projectPath,
|
this.projectPath,
|
||||||
|
|||||||
@@ -123,23 +123,28 @@ export class FeatureStateManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Create notifications for important status changes
|
// Create notifications for important status changes
|
||||||
const notificationService = getNotificationService();
|
// Wrapped in try-catch so failures don't block syncFeatureToAppSpec below
|
||||||
if (status === 'waiting_approval') {
|
try {
|
||||||
await notificationService.createNotification({
|
const notificationService = getNotificationService();
|
||||||
type: 'feature_waiting_approval',
|
if (status === 'waiting_approval') {
|
||||||
title: 'Feature Ready for Review',
|
await notificationService.createNotification({
|
||||||
message: `"${feature.name || featureId}" is ready for your review and approval.`,
|
type: 'feature_waiting_approval',
|
||||||
featureId,
|
title: 'Feature Ready for Review',
|
||||||
projectPath,
|
message: `"${feature.name || featureId}" is ready for your review and approval.`,
|
||||||
});
|
featureId,
|
||||||
} else if (status === 'verified') {
|
projectPath,
|
||||||
await notificationService.createNotification({
|
});
|
||||||
type: 'feature_verified',
|
} else if (status === 'verified') {
|
||||||
title: 'Feature Verified',
|
await notificationService.createNotification({
|
||||||
message: `"${feature.name || featureId}" has been verified and is complete.`,
|
type: 'feature_verified',
|
||||||
featureId,
|
title: 'Feature Verified',
|
||||||
projectPath,
|
message: `"${feature.name || featureId}" has been verified and is complete.`,
|
||||||
});
|
featureId,
|
||||||
|
projectPath,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (notificationError) {
|
||||||
|
logger.warn(`Failed to create notification for feature ${featureId}:`, notificationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync completed/verified features to app_spec.txt
|
// Sync completed/verified features to app_spec.txt
|
||||||
@@ -334,7 +339,7 @@ export class FeatureStateManager {
|
|||||||
Object.assign(feature.planSpec, updates);
|
Object.assign(feature.planSpec, updates);
|
||||||
|
|
||||||
// If content is being updated and it's different from old content, increment version
|
// If content is being updated and it's different from old content, increment version
|
||||||
if (updates.content && updates.content !== oldContent) {
|
if (updates.content !== undefined && updates.content !== oldContent) {
|
||||||
feature.planSpec.version = (feature.planSpec.version || 0) + 1;
|
feature.planSpec.version = (feature.planSpec.version || 0) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,6 +451,11 @@ export class FeatureStateManager {
|
|||||||
status,
|
status,
|
||||||
tasks: feature.planSpec.tasks,
|
tasks: feature.planSpec.tasks,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
const availableIds = feature.planSpec.tasks.map((t) => t.id).join(', ');
|
||||||
|
logger.warn(
|
||||||
|
`[updateTaskStatus] Task ${taskId} not found in feature ${featureId} (${projectPath}). Available task IDs: [${availableIds}]`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to update task ${taskId} status for ${featureId}:`, error);
|
logger.error(`Failed to update task ${taskId} status for ${featureId}:`, error);
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ export class PipelineOrchestrator {
|
|||||||
await this.executePipeline(context);
|
await this.executePipeline(context);
|
||||||
|
|
||||||
// Re-fetch feature to check if executePipeline set a terminal status (e.g., merge_conflict)
|
// Re-fetch feature to check if executePipeline set a terminal status (e.g., merge_conflict)
|
||||||
const reloadedFeature = await this.featureLoader.getById(projectPath, featureId);
|
const reloadedFeature = await this.featureStateManager.loadFeature(projectPath, featureId);
|
||||||
const finalStatus = feature.skipTests ? 'waiting_approval' : 'verified';
|
const finalStatus = feature.skipTests ? 'waiting_approval' : 'verified';
|
||||||
|
|
||||||
// Only update status if not already in a terminal state
|
// Only update status if not already in a terminal state
|
||||||
@@ -516,7 +516,7 @@ export class PipelineOrchestrator {
|
|||||||
projectPath,
|
projectPath,
|
||||||
branchName,
|
branchName,
|
||||||
worktreePath || projectPath,
|
worktreePath || projectPath,
|
||||||
targetBranch,
|
targetBranch || 'main',
|
||||||
{
|
{
|
||||||
deleteWorktreeAndBranch: false,
|
deleteWorktreeAndBranch: false,
|
||||||
}
|
}
|
||||||
@@ -562,22 +562,33 @@ export class PipelineOrchestrator {
|
|||||||
let passCount = 0;
|
let passCount = 0;
|
||||||
let failCount = 0;
|
let failCount = 0;
|
||||||
|
|
||||||
|
let inFailureContext = false;
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
if (trimmed.includes('FAIL') || trimmed.includes('FAILED')) {
|
if (trimmed.includes('FAIL') || trimmed.includes('FAILED')) {
|
||||||
const match = trimmed.match(/(?:FAIL|FAILED)\s+(.+)/);
|
const match = trimmed.match(/(?:FAIL|FAILED)\s+(.+)/);
|
||||||
if (match) failedTests.push(match[1].trim());
|
if (match) failedTests.push(match[1].trim());
|
||||||
failCount++;
|
failCount++;
|
||||||
|
inFailureContext = true;
|
||||||
} else if (trimmed.includes('PASS') || trimmed.includes('PASSED')) {
|
} else if (trimmed.includes('PASS') || trimmed.includes('PASSED')) {
|
||||||
passCount++;
|
passCount++;
|
||||||
|
inFailureContext = false;
|
||||||
}
|
}
|
||||||
if (trimmed.match(/^>\s+.*\.(test|spec)\./)) {
|
if (trimmed.match(/^>\s+.*\.(test|spec)\./)) {
|
||||||
failedTests.push(trimmed.replace(/^>\s+/, ''));
|
failedTests.push(trimmed.replace(/^>\s+/, ''));
|
||||||
}
|
}
|
||||||
if (
|
// Only capture assertion details when they appear in failure context
|
||||||
trimmed.includes('AssertionError') ||
|
// or match explicit assertion error / expect patterns
|
||||||
trimmed.includes('toBe') ||
|
if (trimmed.includes('AssertionError') || trimmed.includes('AssertionError')) {
|
||||||
trimmed.includes('toEqual')
|
failedTests.push(trimmed);
|
||||||
|
} else if (
|
||||||
|
inFailureContext &&
|
||||||
|
/expect\(.+\)\.(toBe|toEqual|toMatch|toThrow|toContain)\s*\(/.test(trimmed)
|
||||||
|
) {
|
||||||
|
failedTests.push(trimmed);
|
||||||
|
} else if (
|
||||||
|
inFailureContext &&
|
||||||
|
(trimmed.startsWith('Expected') || trimmed.startsWith('Received'))
|
||||||
) {
|
) {
|
||||||
failedTests.push(trimmed);
|
failedTests.push(trimmed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ const eslintConfig = defineConfig([
|
|||||||
setInterval: 'readonly',
|
setInterval: 'readonly',
|
||||||
clearTimeout: 'readonly',
|
clearTimeout: 'readonly',
|
||||||
clearInterval: 'readonly',
|
clearInterval: 'readonly',
|
||||||
|
queueMicrotask: 'readonly',
|
||||||
// Node.js (for scripts and Electron)
|
// Node.js (for scripts and Electron)
|
||||||
process: 'readonly',
|
process: 'readonly',
|
||||||
require: 'readonly',
|
require: 'readonly',
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { createLogger } from '@automaker/utils/logger';
|
|||||||
// Note: setItem/getItem moved to ./utils/theme-utils.ts
|
// Note: setItem/getItem moved to ./utils/theme-utils.ts
|
||||||
import { UI_SANS_FONT_OPTIONS, UI_MONO_FONT_OPTIONS } from '@/config/ui-font-options';
|
import { UI_SANS_FONT_OPTIONS, UI_MONO_FONT_OPTIONS } from '@/config/ui-font-options';
|
||||||
import type {
|
import type {
|
||||||
Feature as BaseFeature,
|
|
||||||
FeatureImagePath,
|
FeatureImagePath,
|
||||||
FeatureTextFilePath,
|
FeatureTextFilePath,
|
||||||
ModelAlias,
|
ModelAlias,
|
||||||
@@ -15,25 +14,11 @@ import type {
|
|||||||
ThinkingLevel,
|
ThinkingLevel,
|
||||||
ReasoningEffort,
|
ReasoningEffort,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
CursorModelId,
|
|
||||||
CodexModelId,
|
|
||||||
OpencodeModelId,
|
|
||||||
GeminiModelId,
|
|
||||||
CopilotModelId,
|
|
||||||
PhaseModelConfig,
|
|
||||||
PhaseModelKey,
|
PhaseModelKey,
|
||||||
PhaseModelEntry,
|
PhaseModelEntry,
|
||||||
MCPServerConfig,
|
|
||||||
FeatureStatusWithPipeline,
|
|
||||||
PipelineConfig,
|
|
||||||
PipelineStep,
|
PipelineStep,
|
||||||
PromptCustomization,
|
|
||||||
ModelDefinition,
|
ModelDefinition,
|
||||||
ServerLogLevel,
|
ServerLogLevel,
|
||||||
EventHook,
|
|
||||||
ClaudeApiProfile,
|
|
||||||
ClaudeCompatibleProvider,
|
|
||||||
SidebarStyle,
|
|
||||||
ParsedTask,
|
ParsedTask,
|
||||||
PlanSpec,
|
PlanSpec,
|
||||||
} from '@automaker/types';
|
} from '@automaker/types';
|
||||||
@@ -2131,7 +2116,7 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
const updateSizes = (layout: TerminalPanelContent): TerminalPanelContent => {
|
const updateSizes = (layout: TerminalPanelContent): TerminalPanelContent => {
|
||||||
if (layout.type === 'split') {
|
if (layout.type === 'split') {
|
||||||
// Find matching panels and update sizes
|
// Find matching panels and update sizes
|
||||||
const updatedPanels = layout.panels.map((panel, index) => {
|
const updatedPanels = layout.panels.map((panel, _index) => {
|
||||||
// Generate key for this panel
|
// Generate key for this panel
|
||||||
const panelKey =
|
const panelKey =
|
||||||
panel.type === 'split'
|
panel.type === 'split'
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import type { Project, TrashedProject } from '@/lib/electron';
|
|||||||
import type {
|
import type {
|
||||||
ModelAlias,
|
ModelAlias,
|
||||||
PlanningMode,
|
PlanningMode,
|
||||||
ThinkingLevel,
|
|
||||||
ReasoningEffort,
|
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
CursorModelId,
|
CursorModelId,
|
||||||
CodexModelId,
|
CodexModelId,
|
||||||
@@ -33,7 +31,7 @@ import type {
|
|||||||
BackgroundSettings,
|
BackgroundSettings,
|
||||||
} from './ui-types';
|
} from './ui-types';
|
||||||
import type { ApiKeys } from './settings-types';
|
import type { ApiKeys } from './settings-types';
|
||||||
import type { ChatMessage, ChatSession, FeatureImage } from './chat-types';
|
import type { ChatMessage, ChatSession } from './chat-types';
|
||||||
import type { TerminalState, TerminalPanelContent, PersistedTerminalState } from './terminal-types';
|
import type { TerminalState, TerminalPanelContent, PersistedTerminalState } from './terminal-types';
|
||||||
import type { Feature, ProjectAnalysis } from './project-types';
|
import type { Feature, ProjectAnalysis } from './project-types';
|
||||||
import type { ClaudeUsage, CodexUsage } from './usage-types';
|
import type { ClaudeUsage, CodexUsage } from './usage-types';
|
||||||
|
|||||||
Reference in New Issue
Block a user