P3-A: worktrees multi-repo & cycle de vie (backend)

Enregistrement de repos et gestion de leurs worktrees git, source de vérité
= git (worktrees dérivés + cache court par repo), corrélation worktree ↔
sessions par cwd, mutations sérialisées par repo.

- shared: RepoSummary, PostCreateHook, WorktreeSummary, WorktreeGitStatus ;
  messages WS repo_update/worktree_update/*_removed ; topic sub 'worktrees'
  (+ parseClientMessage) ; DTOs REST repos/worktrees.
- db: migration id:3 (table repos).
- core/git.ts: couche git sûre (execFile, jamais de shell, -- avant chemins,
  GIT_OPTIONAL_LOCKS=0), parseWorktreePorcelain, list/add/remove/prune/status/
  ahead-behind, validation branche + chemin.
- core/claude-trust.ts: pré-trust atomique de ~/.claude.json (spike S3).
- core/worktree-manager.ts: repos CRUD, create (worktree add + pré-trust +
  hooks post-create + startSession optionnel), adopt, delete (garde-fous 409
  dirty / 400 main / 409 session live), prune ; events.
- routes/repos.ts + routes/worktrees.ts, câblage app.ts, gateway topic worktrees.
- tests: git (repos tmp réels), claude-trust, worktree-manager (146 verts).
This commit is contained in:
2026-06-12 18:31:06 +02:00
parent 92c48f0978
commit 08320e166e
16 changed files with 1208 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
// Types REST partagés (préfixe /api/v1) — sous-ensemble P1.
import type { SessionSummary } from './protocol.js';
// Types REST partagés (préfixe /api/v1).
import type { PostCreateHook, RepoSummary, SessionSummary, WorktreeSummary } from './protocol.js';
export interface ApiError {
error: { code: string; message: string; details?: unknown };
@@ -34,3 +34,59 @@ export interface SessionResponse {
// sont TOUJOURS dérivés du :id (lu sur disque), jamais fournis par le client (cf. spike S1).
export type ResumeSessionRequest = Record<string, never>;
export type ForkSessionRequest = Record<string, never>;
// ---- Repos & worktrees (P3) ----
export interface ReposListResponse {
repos: RepoSummary[];
}
export interface RepoResponse {
repo: RepoSummary;
}
export interface CreateRepoRequest {
path: string;
label?: string;
postCreateHooks?: PostCreateHook[];
preTrust?: boolean;
}
export interface UpdateRepoRequest {
label?: string;
postCreateHooks?: PostCreateHook[];
preTrust?: boolean;
}
export interface WorktreesListResponse {
worktrees: WorktreeSummary[];
}
export interface WorktreeResponse {
worktree: WorktreeSummary;
}
export interface HookRunResult {
hookId: string;
label: string;
exitCode: number | null;
output: string;
durationMs: number;
}
export interface CreateWorktreeRequest {
branch: string;
/** true : créer la branche (`-b`) ; false : checkout d'une branche existante. */
newBranch: boolean;
/** point de départ de la nouvelle branche (défaut : HEAD courant du repo). */
baseRef?: string;
/** chemin cible du worktree (défaut : `<parent>/<repo>-wt-<branch>`). */
path?: string;
runHooks?: boolean;
preTrust?: boolean;
/** lancer une session dans le worktree créé (défaut : aucune). */
startSession?: 'claude' | 'bash' | null;
}
export interface CreateWorktreeResponse {
worktree: WorktreeSummary;
hookResults: HookRunResult[];
session: SessionSummary | null;
}
export interface AdoptWorktreeRequest {
path: string;
runHooks?: boolean;
preTrust?: boolean;
}