feat: Add remote sync for project clones
Fetch remote changes before agents start working so they build on up-to-date code. Adds ProjectSyncManager with git fetch + ff-only merge of defaultBranch, integrated into phase dispatch to sync before branch creation. - Schema: lastFetchedAt column on projects table (migration 0029) - Events: project:synced, project:sync_failed - Phase dispatch: sync all linked projects before creating branches - tRPC: syncProject, syncAllProjects, getProjectSyncStatus - CLI: cw project sync [name] --all, cw project status [name] - Frontend: sync button + ahead/behind badge on projects settings
This commit is contained in:
@@ -25,6 +25,7 @@ import type { CoordinationManager } from '../coordination/types.js';
|
||||
import type { BranchManager } from '../git/branch-manager.js';
|
||||
import type { ExecutionOrchestrator } from '../execution/orchestrator.js';
|
||||
import type { PreviewManager } from '../preview/index.js';
|
||||
import type { ProjectSyncManager } from '../git/remote-sync.js';
|
||||
|
||||
// Re-export for convenience
|
||||
export type { EventBus, DomainEvent };
|
||||
@@ -79,6 +80,8 @@ export interface TRPCContext {
|
||||
chatSessionRepository?: ChatSessionRepository;
|
||||
/** Review comment repository for inline review comments on phase diffs */
|
||||
reviewCommentRepository?: ReviewCommentRepository;
|
||||
/** Project sync manager for remote fetch/sync operations */
|
||||
projectSyncManager?: ProjectSyncManager;
|
||||
/** Absolute path to the workspace root (.cwrc directory) */
|
||||
workspaceRoot?: string;
|
||||
}
|
||||
@@ -110,6 +113,7 @@ export interface CreateContextOptions {
|
||||
conversationRepository?: ConversationRepository;
|
||||
chatSessionRepository?: ChatSessionRepository;
|
||||
reviewCommentRepository?: ReviewCommentRepository;
|
||||
projectSyncManager?: ProjectSyncManager;
|
||||
workspaceRoot?: string;
|
||||
}
|
||||
|
||||
@@ -144,6 +148,7 @@ export function createContext(options: CreateContextOptions): TRPCContext {
|
||||
conversationRepository: options.conversationRepository,
|
||||
chatSessionRepository: options.chatSessionRepository,
|
||||
reviewCommentRepository: options.reviewCommentRepository,
|
||||
projectSyncManager: options.projectSyncManager,
|
||||
workspaceRoot: options.workspaceRoot,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import type { CoordinationManager } from '../../coordination/types.js';
|
||||
import type { BranchManager } from '../../git/branch-manager.js';
|
||||
import type { ExecutionOrchestrator } from '../../execution/orchestrator.js';
|
||||
import type { PreviewManager } from '../../preview/index.js';
|
||||
import type { ProjectSyncManager } from '../../git/remote-sync.js';
|
||||
|
||||
export function requireAgentManager(ctx: TRPCContext) {
|
||||
if (!ctx.agentManager) {
|
||||
@@ -214,3 +215,13 @@ export function requireReviewCommentRepository(ctx: TRPCContext): ReviewCommentR
|
||||
}
|
||||
return ctx.reviewCommentRepository;
|
||||
}
|
||||
|
||||
export function requireProjectSyncManager(ctx: TRPCContext): ProjectSyncManager {
|
||||
if (!ctx.projectSyncManager) {
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Project sync manager not available',
|
||||
});
|
||||
}
|
||||
return ctx.projectSyncManager;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { z } from 'zod';
|
||||
import { join } from 'node:path';
|
||||
import { rm } from 'node:fs/promises';
|
||||
import type { ProcedureBuilder } from '../trpc.js';
|
||||
import { requireProjectRepository } from './_helpers.js';
|
||||
import { requireProjectRepository, requireProjectSyncManager } from './_helpers.js';
|
||||
import { cloneProject } from '../../git/clone.js';
|
||||
import { getProjectCloneDir } from '../../git/project-clones.js';
|
||||
|
||||
@@ -153,5 +153,25 @@ export function projectProcedures(publicProcedure: ProcedureBuilder) {
|
||||
await repo.setInitiativeProjects(input.initiativeId, input.projectIds);
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
syncProject: publicProcedure
|
||||
.input(z.object({ id: z.string().min(1) }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const syncManager = requireProjectSyncManager(ctx);
|
||||
return syncManager.syncProject(input.id);
|
||||
}),
|
||||
|
||||
syncAllProjects: publicProcedure
|
||||
.mutation(async ({ ctx }) => {
|
||||
const syncManager = requireProjectSyncManager(ctx);
|
||||
return syncManager.syncAllProjects();
|
||||
}),
|
||||
|
||||
getProjectSyncStatus: publicProcedure
|
||||
.input(z.object({ id: z.string().min(1) }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const syncManager = requireProjectSyncManager(ctx);
|
||||
return syncManager.getSyncStatus(input.id);
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user