refactor: Improve all git operations, add stash support, add improved pull request flow, add worktree file copy options, address code review comments, add cherry pick options

This commit is contained in:
gsxdsm
2026-02-17 22:02:58 -08:00
parent f4e87d4c25
commit 9af63bc1ef
89 changed files with 6811 additions and 351 deletions

View File

@@ -4,6 +4,7 @@
import type { Feature } from '@automaker/types';
import { createLogger, classifyError } from '@automaker/utils';
import { areDependenciesSatisfied } from '@automaker/dependency-resolver';
import type { TypedEventBus } from './typed-event-bus.js';
import type { ConcurrencyManager } from './concurrency-manager.js';
import type { SettingsService } from './settings-service.js';
@@ -64,6 +65,7 @@ export type ClearExecutionStateFn = (
) => Promise<void>;
export type ResetStuckFeaturesFn = (projectPath: string) => Promise<void>;
export type IsFeatureFinishedFn = (feature: Feature) => boolean;
export type LoadAllFeaturesFn = (projectPath: string) => Promise<Feature[]>;
export class AutoLoopCoordinator {
private autoLoopsByProject = new Map<string, ProjectAutoLoopState>();
@@ -78,7 +80,8 @@ export class AutoLoopCoordinator {
private clearExecutionStateFn: ClearExecutionStateFn,
private resetStuckFeaturesFn: ResetStuckFeaturesFn,
private isFeatureFinishedFn: IsFeatureFinishedFn,
private isFeatureRunningFn: (featureId: string) => boolean
private isFeatureRunningFn: (featureId: string) => boolean,
private loadAllFeaturesFn?: LoadAllFeaturesFn
) {}
/**
@@ -178,9 +181,31 @@ export class AutoLoopCoordinator {
await this.sleep(10000, projectState.abortController.signal);
continue;
}
const nextFeature = pendingFeatures.find(
(f) => !this.isFeatureRunningFn(f.id) && !this.isFeatureFinishedFn(f)
// Load all features for dependency checking (if callback provided)
const allFeatures = this.loadAllFeaturesFn
? await this.loadAllFeaturesFn(projectPath)
: pendingFeatures;
// Filter to eligible features: not running, not finished, and dependencies satisfied
const eligibleFeatures = pendingFeatures.filter(
(f) =>
!this.isFeatureRunningFn(f.id) &&
!this.isFeatureFinishedFn(f) &&
areDependenciesSatisfied(f, allFeatures)
);
// Sort eligible features by priority (lower number = higher priority, default 2)
eligibleFeatures.sort((a, b) => (a.priority ?? 2) - (b.priority ?? 2));
const nextFeature = eligibleFeatures[0] ?? null;
if (nextFeature) {
logger.info(
`Auto-loop selected feature "${nextFeature.title || nextFeature.id}" ` +
`(priority=${nextFeature.priority ?? 2}) from ${eligibleFeatures.length} eligible features`
);
}
if (nextFeature) {
projectState.hasEmittedIdleEvent = false;
this.executeFeatureFn(

View File

@@ -324,7 +324,8 @@ export class AutoModeServiceFacade {
feature.status === 'completed' ||
feature.status === 'verified' ||
feature.status === 'waiting_approval',
(featureId) => concurrencyManager.isRunning(featureId)
(featureId) => concurrencyManager.isRunning(featureId),
async (pPath) => featureLoader.getAll(pPath)
);
// ExecutionService - runAgentFn calls AgentExecutor.execute

View File

@@ -729,6 +729,7 @@ export class SettingsService {
anthropic: { configured: boolean; masked: string };
google: { configured: boolean; masked: string };
openai: { configured: boolean; masked: string };
zai: { configured: boolean; masked: string };
}> {
const credentials = await this.getCredentials();
@@ -750,6 +751,10 @@ export class SettingsService {
configured: !!credentials.apiKeys.openai,
masked: maskKey(credentials.apiKeys.openai),
},
zai: {
configured: !!credentials.apiKeys.zai,
masked: maskKey(credentials.apiKeys.zai),
},
};
}

View File

@@ -171,7 +171,11 @@ export class ZaiUsageService {
*/
getApiHost(): string {
// Priority: 1. Instance host, 2. Z_AI_API_HOST env, 3. Default
return process.env.Z_AI_API_HOST ? `https://${process.env.Z_AI_API_HOST}` : this.apiHost;
if (process.env.Z_AI_API_HOST) {
const envHost = process.env.Z_AI_API_HOST.trim();
return envHost.startsWith('http') ? envHost : `https://${envHost}`;
}
return this.apiHost;
}
/**
@@ -242,8 +246,7 @@ export class ZaiUsageService {
}
const quotaUrl =
process.env.Z_AI_QUOTA_URL ||
`${process.env.Z_AI_API_HOST ? `https://${process.env.Z_AI_API_HOST}` : 'https://api.z.ai'}/api/monitor/usage/quota/limit`;
process.env.Z_AI_QUOTA_URL || `${this.getApiHost()}/api/monitor/usage/quota/limit`;
logger.info(`[verify] Testing API key against: ${quotaUrl}`);