mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
refactor(06-04): delete auto-mode-service.ts monolith
- Delete the 2705-line auto-mode-service.ts monolith - Create AutoModeServiceCompat as compatibility layer for routes - Create GlobalAutoModeService for cross-project operations - Update all routes to use AutoModeServiceCompat type - Add SharedServices interface for state sharing across facades - Add getActiveProjects/getActiveWorktrees to AutoLoopCoordinator - Delete obsolete monolith test files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -363,6 +363,36 @@ export class AutoLoopCoordinator {
|
||||
return projectState?.config ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active auto loop worktrees with their project paths and branch names
|
||||
*/
|
||||
getActiveWorktrees(): Array<{ projectPath: string; branchName: string | null }> {
|
||||
const activeWorktrees: Array<{ projectPath: string; branchName: string | null }> = [];
|
||||
for (const [, state] of this.autoLoopsByProject) {
|
||||
if (state.isRunning) {
|
||||
activeWorktrees.push({
|
||||
projectPath: state.config.projectPath,
|
||||
branchName: state.branchName,
|
||||
});
|
||||
}
|
||||
}
|
||||
return activeWorktrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all projects that have auto mode running (returns unique project paths)
|
||||
* @deprecated Use getActiveWorktrees instead for full worktree information
|
||||
*/
|
||||
getActiveProjects(): string[] {
|
||||
const activeProjects = new Set<string>();
|
||||
for (const [, state] of this.autoLoopsByProject) {
|
||||
if (state.isRunning) {
|
||||
activeProjects.add(state.config.projectPath);
|
||||
}
|
||||
}
|
||||
return Array.from(activeProjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of running features for a specific worktree
|
||||
* Delegates to ConcurrencyManager.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
225
apps/server/src/services/auto-mode/compat.ts
Normal file
225
apps/server/src/services/auto-mode/compat.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Compatibility Shim - Provides AutoModeService-like interface using the new architecture
|
||||
*
|
||||
* This allows existing routes to work without major changes during the transition.
|
||||
* Routes receive this shim which delegates to GlobalAutoModeService and facades.
|
||||
*
|
||||
* This is a TEMPORARY shim - routes should be updated to use the new interface directly.
|
||||
*/
|
||||
|
||||
import type { Feature } from '@automaker/types';
|
||||
import type { EventEmitter } from '../../lib/events.js';
|
||||
import { GlobalAutoModeService } from './global-service.js';
|
||||
import { AutoModeServiceFacade } from './facade.js';
|
||||
import type { SettingsService } from '../settings-service.js';
|
||||
import type { FeatureLoader } from '../feature-loader.js';
|
||||
import type { FacadeOptions, AutoModeStatus, RunningAgentInfo } from './types.js';
|
||||
|
||||
/**
|
||||
* AutoModeServiceCompat wraps GlobalAutoModeService and facades to provide
|
||||
* the old AutoModeService interface that routes expect.
|
||||
*/
|
||||
export class AutoModeServiceCompat {
|
||||
private readonly globalService: GlobalAutoModeService;
|
||||
private readonly facadeOptions: FacadeOptions;
|
||||
|
||||
constructor(
|
||||
events: EventEmitter,
|
||||
settingsService: SettingsService | null,
|
||||
featureLoader: FeatureLoader
|
||||
) {
|
||||
this.globalService = new GlobalAutoModeService(events, settingsService, featureLoader);
|
||||
const sharedServices = this.globalService.getSharedServices();
|
||||
|
||||
this.facadeOptions = {
|
||||
events,
|
||||
settingsService,
|
||||
featureLoader,
|
||||
sharedServices,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global service for direct access
|
||||
*/
|
||||
getGlobalService(): GlobalAutoModeService {
|
||||
return this.globalService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a facade for a specific project
|
||||
*/
|
||||
createFacade(projectPath: string): AutoModeServiceFacade {
|
||||
return AutoModeServiceFacade.create(projectPath, this.facadeOptions);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// GLOBAL OPERATIONS (delegated to GlobalAutoModeService)
|
||||
// ===========================================================================
|
||||
|
||||
getStatus(): AutoModeStatus {
|
||||
return this.globalService.getStatus();
|
||||
}
|
||||
|
||||
getActiveAutoLoopProjects(): string[] {
|
||||
return this.globalService.getActiveAutoLoopProjects();
|
||||
}
|
||||
|
||||
getActiveAutoLoopWorktrees(): Array<{ projectPath: string; branchName: string | null }> {
|
||||
return this.globalService.getActiveAutoLoopWorktrees();
|
||||
}
|
||||
|
||||
async getRunningAgents(): Promise<RunningAgentInfo[]> {
|
||||
return this.globalService.getRunningAgents();
|
||||
}
|
||||
|
||||
async markAllRunningFeaturesInterrupted(reason?: string): Promise<void> {
|
||||
return this.globalService.markAllRunningFeaturesInterrupted(reason);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// PER-PROJECT OPERATIONS (delegated to facades)
|
||||
// ===========================================================================
|
||||
|
||||
getStatusForProject(
|
||||
projectPath: string,
|
||||
branchName: string | null = null
|
||||
): {
|
||||
isAutoLoopRunning: boolean;
|
||||
runningFeatures: string[];
|
||||
runningCount: number;
|
||||
maxConcurrency: number;
|
||||
branchName: string | null;
|
||||
} {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.getStatusForProject(branchName);
|
||||
}
|
||||
|
||||
isAutoLoopRunningForProject(projectPath: string, branchName: string | null = null): boolean {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.isAutoLoopRunning(branchName);
|
||||
}
|
||||
|
||||
async startAutoLoopForProject(
|
||||
projectPath: string,
|
||||
branchName: string | null = null,
|
||||
maxConcurrency?: number
|
||||
): Promise<number> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.startAutoLoop(branchName, maxConcurrency);
|
||||
}
|
||||
|
||||
async stopAutoLoopForProject(
|
||||
projectPath: string,
|
||||
branchName: string | null = null
|
||||
): Promise<number> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.stopAutoLoop(branchName);
|
||||
}
|
||||
|
||||
async executeFeature(
|
||||
projectPath: string,
|
||||
featureId: string,
|
||||
useWorktrees = false,
|
||||
isAutoMode = false,
|
||||
providedWorktreePath?: string,
|
||||
options?: { continuationPrompt?: string; _calledInternally?: boolean }
|
||||
): Promise<void> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.executeFeature(
|
||||
featureId,
|
||||
useWorktrees,
|
||||
isAutoMode,
|
||||
providedWorktreePath,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
async stopFeature(featureId: string): Promise<boolean> {
|
||||
// Stop feature is tricky - we need to find which project the feature is running in
|
||||
// The concurrency manager tracks this
|
||||
const runningAgents = await this.getRunningAgents();
|
||||
const agent = runningAgents.find((a) => a.featureId === featureId);
|
||||
if (agent) {
|
||||
const facade = this.createFacade(agent.projectPath);
|
||||
return facade.stopFeature(featureId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async resumeFeature(projectPath: string, featureId: string, useWorktrees = false): Promise<void> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.resumeFeature(featureId, useWorktrees);
|
||||
}
|
||||
|
||||
async followUpFeature(
|
||||
projectPath: string,
|
||||
featureId: string,
|
||||
prompt: string,
|
||||
imagePaths?: string[],
|
||||
useWorktrees = true
|
||||
): Promise<void> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.followUpFeature(featureId, prompt, imagePaths, useWorktrees);
|
||||
}
|
||||
|
||||
async verifyFeature(projectPath: string, featureId: string): Promise<boolean> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.verifyFeature(featureId);
|
||||
}
|
||||
|
||||
async commitFeature(
|
||||
projectPath: string,
|
||||
featureId: string,
|
||||
providedWorktreePath?: string
|
||||
): Promise<string | null> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.commitFeature(featureId, providedWorktreePath);
|
||||
}
|
||||
|
||||
async contextExists(projectPath: string, featureId: string): Promise<boolean> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.contextExists(featureId);
|
||||
}
|
||||
|
||||
async analyzeProject(projectPath: string): Promise<void> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.analyzeProject();
|
||||
}
|
||||
|
||||
async resolvePlanApproval(
|
||||
projectPath: string,
|
||||
featureId: string,
|
||||
approved: boolean,
|
||||
editedPlan?: string,
|
||||
feedback?: string
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.resolvePlanApproval(featureId, approved, editedPlan, feedback);
|
||||
}
|
||||
|
||||
async resumeInterruptedFeatures(projectPath: string): Promise<void> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.resumeInterruptedFeatures();
|
||||
}
|
||||
|
||||
async checkWorktreeCapacity(
|
||||
projectPath: string,
|
||||
featureId: string
|
||||
): Promise<{
|
||||
hasCapacity: boolean;
|
||||
currentAgents: number;
|
||||
maxAgents: number;
|
||||
branchName: string | null;
|
||||
}> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.checkWorktreeCapacity(featureId);
|
||||
}
|
||||
|
||||
async detectOrphanedFeatures(
|
||||
projectPath: string
|
||||
): Promise<Array<{ feature: Feature; missingBranch: string }>> {
|
||||
const facade = this.createFacade(projectPath);
|
||||
return facade.detectOrphanedFeatures();
|
||||
}
|
||||
}
|
||||
@@ -86,12 +86,20 @@ export class AutoModeServiceFacade {
|
||||
* @param options - Configuration options including events, settingsService, featureLoader
|
||||
*/
|
||||
static create(projectPath: string, options: FacadeOptions): AutoModeServiceFacade {
|
||||
const { events, settingsService = null, featureLoader = new FeatureLoader() } = options;
|
||||
const {
|
||||
events,
|
||||
settingsService = null,
|
||||
featureLoader = new FeatureLoader(),
|
||||
sharedServices,
|
||||
} = options;
|
||||
|
||||
// Create core services
|
||||
const eventBus = new TypedEventBus(events);
|
||||
const worktreeResolver = new WorktreeResolver();
|
||||
const concurrencyManager = new ConcurrencyManager((p) => worktreeResolver.getCurrentBranch(p));
|
||||
// Use shared services if provided, otherwise create new ones
|
||||
// Shared services allow multiple facades to share state (e.g., running features, auto loops)
|
||||
const eventBus = sharedServices?.eventBus ?? new TypedEventBus(events);
|
||||
const worktreeResolver = sharedServices?.worktreeResolver ?? new WorktreeResolver();
|
||||
const concurrencyManager =
|
||||
sharedServices?.concurrencyManager ??
|
||||
new ConcurrencyManager((p) => worktreeResolver.getCurrentBranch(p));
|
||||
const featureStateManager = new FeatureStateManager(events, featureLoader);
|
||||
const planApprovalService = new PlanApprovalService(
|
||||
eventBus,
|
||||
@@ -151,36 +159,39 @@ export class AutoModeServiceFacade {
|
||||
}
|
||||
);
|
||||
|
||||
// AutoLoopCoordinator
|
||||
const autoLoopCoordinator = new AutoLoopCoordinator(
|
||||
eventBus,
|
||||
concurrencyManager,
|
||||
settingsService,
|
||||
// Callbacks
|
||||
(pPath, featureId, useWorktrees, isAutoMode) =>
|
||||
facadeInstance!.executeFeature(featureId, useWorktrees, isAutoMode),
|
||||
(pPath, branchName) =>
|
||||
featureLoader
|
||||
.getAll(pPath)
|
||||
.then((features) =>
|
||||
features.filter(
|
||||
(f) =>
|
||||
(f.status === 'backlog' || f.status === 'ready') &&
|
||||
(branchName === null
|
||||
? !f.branchName || f.branchName === 'main'
|
||||
: f.branchName === branchName)
|
||||
)
|
||||
),
|
||||
(pPath, branchName, maxConcurrency) =>
|
||||
facadeInstance!.saveExecutionStateForProject(branchName, maxConcurrency),
|
||||
(pPath, branchName) => facadeInstance!.clearExecutionState(branchName),
|
||||
(pPath) => featureStateManager.resetStuckFeatures(pPath),
|
||||
(feature) =>
|
||||
feature.status === 'completed' ||
|
||||
feature.status === 'verified' ||
|
||||
feature.status === 'waiting_approval',
|
||||
(featureId) => concurrencyManager.isRunning(featureId)
|
||||
);
|
||||
// AutoLoopCoordinator - use shared if provided, otherwise create new
|
||||
// Note: When using shared autoLoopCoordinator, callbacks are already set up by the global service
|
||||
const autoLoopCoordinator =
|
||||
sharedServices?.autoLoopCoordinator ??
|
||||
new AutoLoopCoordinator(
|
||||
eventBus,
|
||||
concurrencyManager,
|
||||
settingsService,
|
||||
// Callbacks
|
||||
(pPath, featureId, useWorktrees, isAutoMode) =>
|
||||
facadeInstance!.executeFeature(featureId, useWorktrees, isAutoMode),
|
||||
(pPath, branchName) =>
|
||||
featureLoader
|
||||
.getAll(pPath)
|
||||
.then((features) =>
|
||||
features.filter(
|
||||
(f) =>
|
||||
(f.status === 'backlog' || f.status === 'ready') &&
|
||||
(branchName === null
|
||||
? !f.branchName || f.branchName === 'main'
|
||||
: f.branchName === branchName)
|
||||
)
|
||||
),
|
||||
(pPath, branchName, maxConcurrency) =>
|
||||
facadeInstance!.saveExecutionStateForProject(branchName, maxConcurrency),
|
||||
(pPath, branchName) => facadeInstance!.clearExecutionState(branchName),
|
||||
(pPath) => featureStateManager.resetStuckFeatures(pPath),
|
||||
(feature) =>
|
||||
feature.status === 'completed' ||
|
||||
feature.status === 'verified' ||
|
||||
feature.status === 'waiting_approval',
|
||||
(featureId) => concurrencyManager.isRunning(featureId)
|
||||
);
|
||||
|
||||
// ExecutionService - runAgentFn is a stub
|
||||
const executionService = new ExecutionService(
|
||||
|
||||
200
apps/server/src/services/auto-mode/global-service.ts
Normal file
200
apps/server/src/services/auto-mode/global-service.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* GlobalAutoModeService - Global operations for auto-mode that span across all projects
|
||||
*
|
||||
* This service manages global state and operations that are not project-specific:
|
||||
* - Overall status (all running features across all projects)
|
||||
* - Active auto loop projects and worktrees
|
||||
* - Graceful shutdown (mark all features as interrupted)
|
||||
*
|
||||
* Per-project operations should use AutoModeServiceFacade instead.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { Feature } from '@automaker/types';
|
||||
import { createLogger } from '@automaker/utils';
|
||||
import type { EventEmitter } from '../../lib/events.js';
|
||||
import { TypedEventBus } from '../typed-event-bus.js';
|
||||
import { ConcurrencyManager } from '../concurrency-manager.js';
|
||||
import { WorktreeResolver } from '../worktree-resolver.js';
|
||||
import { AutoLoopCoordinator } from '../auto-loop-coordinator.js';
|
||||
import { FeatureStateManager } from '../feature-state-manager.js';
|
||||
import { FeatureLoader } from '../feature-loader.js';
|
||||
import type { SettingsService } from '../settings-service.js';
|
||||
import type { SharedServices, AutoModeStatus, RunningAgentInfo } from './types.js';
|
||||
|
||||
const logger = createLogger('GlobalAutoModeService');
|
||||
|
||||
/**
|
||||
* GlobalAutoModeService provides global operations for auto-mode.
|
||||
*
|
||||
* Created once at server startup, shared across all facades.
|
||||
*/
|
||||
export class GlobalAutoModeService {
|
||||
private readonly eventBus: TypedEventBus;
|
||||
private readonly concurrencyManager: ConcurrencyManager;
|
||||
private readonly autoLoopCoordinator: AutoLoopCoordinator;
|
||||
private readonly worktreeResolver: WorktreeResolver;
|
||||
private readonly featureStateManager: FeatureStateManager;
|
||||
private readonly featureLoader: FeatureLoader;
|
||||
|
||||
constructor(
|
||||
events: EventEmitter,
|
||||
settingsService: SettingsService | null,
|
||||
featureLoader: FeatureLoader = new FeatureLoader()
|
||||
) {
|
||||
this.featureLoader = featureLoader;
|
||||
this.eventBus = new TypedEventBus(events);
|
||||
this.worktreeResolver = new WorktreeResolver();
|
||||
this.concurrencyManager = new ConcurrencyManager((p) =>
|
||||
this.worktreeResolver.getCurrentBranch(p)
|
||||
);
|
||||
this.featureStateManager = new FeatureStateManager(events, featureLoader);
|
||||
|
||||
// Create AutoLoopCoordinator with callbacks
|
||||
// These callbacks use placeholders since GlobalAutoModeService doesn't execute features
|
||||
// Feature execution is done via facades
|
||||
this.autoLoopCoordinator = new AutoLoopCoordinator(
|
||||
this.eventBus,
|
||||
this.concurrencyManager,
|
||||
settingsService,
|
||||
// executeFeatureFn - not used by global service, routes handle execution
|
||||
async () => {
|
||||
throw new Error('executeFeatureFn not available in GlobalAutoModeService');
|
||||
},
|
||||
// getBacklogFeaturesFn
|
||||
(pPath, branchName) =>
|
||||
featureLoader
|
||||
.getAll(pPath)
|
||||
.then((features) =>
|
||||
features.filter(
|
||||
(f) =>
|
||||
(f.status === 'backlog' || f.status === 'ready') &&
|
||||
(branchName === null
|
||||
? !f.branchName || f.branchName === 'main'
|
||||
: f.branchName === branchName)
|
||||
)
|
||||
),
|
||||
// saveExecutionStateFn - placeholder
|
||||
async () => {},
|
||||
// clearExecutionStateFn - placeholder
|
||||
async () => {},
|
||||
// resetStuckFeaturesFn
|
||||
(pPath) => this.featureStateManager.resetStuckFeatures(pPath),
|
||||
// isFeatureDoneFn
|
||||
(feature) =>
|
||||
feature.status === 'completed' ||
|
||||
feature.status === 'verified' ||
|
||||
feature.status === 'waiting_approval',
|
||||
// isFeatureRunningFn
|
||||
(featureId) => this.concurrencyManager.isRunning(featureId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shared services for use by facades.
|
||||
* This allows facades to share state with the global service.
|
||||
*/
|
||||
getSharedServices(): SharedServices {
|
||||
return {
|
||||
eventBus: this.eventBus,
|
||||
concurrencyManager: this.concurrencyManager,
|
||||
autoLoopCoordinator: this.autoLoopCoordinator,
|
||||
worktreeResolver: this.worktreeResolver,
|
||||
};
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// GLOBAL STATUS (3 methods)
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Get global status (all projects combined)
|
||||
*/
|
||||
getStatus(): AutoModeStatus {
|
||||
const allRunning = this.concurrencyManager.getAllRunning();
|
||||
return {
|
||||
isRunning: allRunning.length > 0,
|
||||
runningFeatures: allRunning.map((rf) => rf.featureId),
|
||||
runningCount: allRunning.length,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active auto loop projects (unique project paths)
|
||||
*/
|
||||
getActiveAutoLoopProjects(): string[] {
|
||||
return this.autoLoopCoordinator.getActiveProjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active auto loop worktrees
|
||||
*/
|
||||
getActiveAutoLoopWorktrees(): Array<{ projectPath: string; branchName: string | null }> {
|
||||
return this.autoLoopCoordinator.getActiveWorktrees();
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// RUNNING AGENTS (1 method)
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Get detailed info about all running agents
|
||||
*/
|
||||
async getRunningAgents(): Promise<RunningAgentInfo[]> {
|
||||
const agents = await Promise.all(
|
||||
this.concurrencyManager.getAllRunning().map(async (rf) => {
|
||||
let title: string | undefined;
|
||||
let description: string | undefined;
|
||||
let branchName: string | undefined;
|
||||
|
||||
try {
|
||||
const feature = await this.featureLoader.get(rf.projectPath, rf.featureId);
|
||||
if (feature) {
|
||||
title = feature.title;
|
||||
description = feature.description;
|
||||
branchName = feature.branchName;
|
||||
}
|
||||
} catch {
|
||||
// Silently ignore
|
||||
}
|
||||
|
||||
return {
|
||||
featureId: rf.featureId,
|
||||
projectPath: rf.projectPath,
|
||||
projectName: path.basename(rf.projectPath),
|
||||
isAutoMode: rf.isAutoMode,
|
||||
model: rf.model,
|
||||
provider: rf.provider,
|
||||
title,
|
||||
description,
|
||||
branchName,
|
||||
};
|
||||
})
|
||||
);
|
||||
return agents;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// LIFECYCLE (1 method)
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Mark all running features as interrupted.
|
||||
* Called during graceful shutdown.
|
||||
*
|
||||
* @param reason - Optional reason for the interruption
|
||||
*/
|
||||
async markAllRunningFeaturesInterrupted(reason?: string): Promise<void> {
|
||||
const allRunning = this.concurrencyManager.getAllRunning();
|
||||
|
||||
for (const rf of allRunning) {
|
||||
await this.featureStateManager.markFeatureInterrupted(rf.projectPath, rf.featureId, reason);
|
||||
}
|
||||
|
||||
if (allRunning.length > 0) {
|
||||
logger.info(
|
||||
`Marked ${allRunning.length} running feature(s) as interrupted: ${reason || 'no reason provided'}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,16 @@
|
||||
* Auto Mode Service Module
|
||||
*
|
||||
* Entry point for auto-mode functionality. Exports:
|
||||
* - AutoModeServiceFacade: Clean facade for auto-mode operations
|
||||
* - GlobalAutoModeService: Global operations that span all projects
|
||||
* - AutoModeServiceFacade: Per-project facade for auto-mode operations
|
||||
* - createAutoModeFacade: Convenience factory function
|
||||
* - Types for route consumption
|
||||
*/
|
||||
|
||||
// Main facade export
|
||||
// Main exports
|
||||
export { GlobalAutoModeService } from './global-service.js';
|
||||
export { AutoModeServiceFacade } from './facade.js';
|
||||
export { AutoModeServiceCompat } from './compat.js';
|
||||
|
||||
// Convenience factory function
|
||||
import { AutoModeServiceFacade } from './facade.js';
|
||||
@@ -49,11 +52,13 @@ export function createAutoModeFacade(
|
||||
// Type exports from types.ts
|
||||
export type {
|
||||
FacadeOptions,
|
||||
SharedServices,
|
||||
AutoModeStatus,
|
||||
ProjectAutoModeStatus,
|
||||
WorktreeCapacityInfo,
|
||||
RunningAgentInfo,
|
||||
OrphanedFeatureInfo,
|
||||
GlobalAutoModeOperations,
|
||||
} from './types.js';
|
||||
|
||||
// Re-export types from extracted services for route convenience
|
||||
|
||||
@@ -11,6 +11,10 @@ import type { EventEmitter } from '../../lib/events.js';
|
||||
import type { Feature, ModelProvider } from '@automaker/types';
|
||||
import type { SettingsService } from '../settings-service.js';
|
||||
import type { FeatureLoader } from '../feature-loader.js';
|
||||
import type { ConcurrencyManager } from '../concurrency-manager.js';
|
||||
import type { AutoLoopCoordinator } from '../auto-loop-coordinator.js';
|
||||
import type { WorktreeResolver } from '../worktree-resolver.js';
|
||||
import type { TypedEventBus } from '../typed-event-bus.js';
|
||||
|
||||
// Re-export types from extracted services for route consumption
|
||||
export type { AutoModeConfig, ProjectAutoLoopState } from '../auto-loop-coordinator.js';
|
||||
@@ -25,6 +29,20 @@ export type { PlanApprovalResult, ResolveApprovalResult } from '../plan-approval
|
||||
|
||||
export type { ExecutionState } from '../recovery-service.js';
|
||||
|
||||
/**
|
||||
* Shared services that can be passed to facades to enable state sharing
|
||||
*/
|
||||
export interface SharedServices {
|
||||
/** TypedEventBus for typed event emission */
|
||||
eventBus: TypedEventBus;
|
||||
/** ConcurrencyManager for tracking running features across all projects */
|
||||
concurrencyManager: ConcurrencyManager;
|
||||
/** AutoLoopCoordinator for managing auto loop state across all projects */
|
||||
autoLoopCoordinator: AutoLoopCoordinator;
|
||||
/** WorktreeResolver for git worktree operations */
|
||||
worktreeResolver: WorktreeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for creating an AutoModeServiceFacade instance
|
||||
*/
|
||||
@@ -35,6 +53,8 @@ export interface FacadeOptions {
|
||||
settingsService?: SettingsService | null;
|
||||
/** FeatureLoader for loading feature data (optional, defaults to new FeatureLoader()) */
|
||||
featureLoader?: FeatureLoader;
|
||||
/** Shared services for state sharing across facades (optional) */
|
||||
sharedServices?: SharedServices;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,3 +109,20 @@ export interface OrphanedFeatureInfo {
|
||||
feature: Feature;
|
||||
missingBranch: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface describing global auto-mode operations (not project-specific).
|
||||
* Used by routes that need global state access.
|
||||
*/
|
||||
export interface GlobalAutoModeOperations {
|
||||
/** Get global status (all projects combined) */
|
||||
getStatus(): AutoModeStatus;
|
||||
/** Get all active auto loop projects (unique project paths) */
|
||||
getActiveAutoLoopProjects(): string[];
|
||||
/** Get all active auto loop worktrees */
|
||||
getActiveAutoLoopWorktrees(): Array<{ projectPath: string; branchName: string | null }>;
|
||||
/** Get detailed info about all running agents */
|
||||
getRunningAgents(): Promise<RunningAgentInfo[]>;
|
||||
/** Mark all running features as interrupted (for graceful shutdown) */
|
||||
markAllRunningFeaturesInterrupted(reason?: string): Promise<void>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user