/** * Project Clone Management * * Ensures project repositories are cloned into the repos/ directory. * These base clones are used as the source for git worktrees. */ import { join } from 'node:path'; import { access } from 'node:fs/promises'; import { cloneProject } from './clone.js'; import { createModuleLogger } from '../logger/index.js'; const log = createModuleLogger('git'); /** * Derive the canonical clone directory for a project (relative to workspace root). * Convention: repos/-/ */ export function getProjectCloneDir(projectName: string, projectId: string): string { const sanitized = projectName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); return join('repos', `${sanitized}-${projectId}`); } /** * Ensure a project's git repository is cloned to the workspace. * Uses the canonical path: /repos/-/ * * @param project - Project with id, name, and url * @param workspaceRoot - Absolute path to the workspace root * @returns Absolute path to the clone directory */ export async function ensureProjectClone( project: { id: string; name: string; url: string }, workspaceRoot: string, ): Promise { const relPath = getProjectCloneDir(project.name, project.id); const clonePath = join(workspaceRoot, relPath); try { await access(clonePath); log.debug({ project: project.name, clonePath }, 'project clone already exists'); return clonePath; } catch { log.info({ project: project.name, url: project.url, clonePath }, 'cloning project for first time'); await cloneProject(project.url, clonePath); return clonePath; } }