import { parse, resolve, sep } from 'node:path'; /** * Plus long ancêtre commun d'un ensemble de chemins absolus (par segments, jamais par préfixe * de chaîne : `/a/bc` n'est PAS un ancêtre de `/a/bcd`). Pour un seul chemin, renvoie ce chemin. */ export function commonAncestorDir(paths: string[]): string { const first = paths[0]; if (first === undefined) throw new Error('commonAncestorDir: empty input'); const firstSegs = resolve(first).split(sep); let common = firstSegs.length; for (let i = 1; i < paths.length; i++) { const segs = resolve(paths[i]!).split(sep); let k = 0; while (k < common && k < segs.length && segs[k] === firstSegs[k]) k++; common = k; } const joined = firstSegs.slice(0, common).join(sep); // `['', 'a', 'b'].slice(0,1).join('/')` === '' → racine POSIX ; rétablir le séparateur racine. return joined === '' ? parse(resolve(first)).root : joined; } /** * Politique de répertoire de travail d'une session de groupe (P6) : le `cwd` est le PARENT COMMUN * des répertoires couverts (racine neutre, chemins relatifs cross-repo naturels), chaque répertoire * étant ensuite relié via `--add-dir` (le dédoublonnage + filtrage du cwd est fait par `PtyManager`). * Garde-fou : si le parent commun est la racine du FS (repos éparpillés sur des racines différentes), * on retombe sur le premier répertoire pour ne pas accorder à Claude la permission sur tout le disque. */ export function resolveGroupCwd(dirs: string[]): { cwd: string; addDirs: string[] } { const first = dirs[0]; if (first === undefined) throw new Error('resolveGroupCwd: empty input'); const ancestor = commonAncestorDir(dirs); const root = parse(resolve(first)).root; const cwd = ancestor === root ? first : ancestor; return { cwd, addDirs: dirs }; }