feat(settings): réglages Claude CLI (binaire + ~/.claude) & notif « disponible »
- Réglages → Claude CLI : override du chemin du binaire `claude` (effet à la prochaine session, fallback `which claude`), diagnostic de détection, et override de la racine ~/.claude (effet au redémarrage). Validateurs stricts. - Push : notifie aussi sur le front busy→idle (session redevenue disponible). - Renomme l'état affiché idle → « disponible » / « available » (web EN/FR + VS Code) ; l'enum SessionActivity du protocole reste inchangé.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { accessSync, constants } from 'node:fs';
|
||||
|
||||
export interface SpawnSpec {
|
||||
file: string;
|
||||
@@ -12,22 +13,74 @@ export interface SpawnOptions {
|
||||
resume?: { claudeSessionId: string; fork?: boolean };
|
||||
/** répertoires supplémentaires à relier dans une seule session (P6) : `--add-dir <path>` répété. */
|
||||
addDirs?: string[];
|
||||
/** chemin explicite du binaire `claude` (réglage UI) ; sinon résolution via PATH (`which`). */
|
||||
claudeBinPath?: string | null;
|
||||
}
|
||||
|
||||
/** Diagnostic de résolution du binaire `claude` (exposé en lecture dans Réglages). */
|
||||
export interface ClaudeBinDiagnostic {
|
||||
/** chemin résolu du binaire, ou null si introuvable. */
|
||||
path: string | null;
|
||||
/** 'configured' = réglage explicite ; 'path' = trouvé via PATH ; null = introuvable. */
|
||||
source: 'configured' | 'path' | null;
|
||||
/** true si le binaire est présent et exécutable. */
|
||||
ok: boolean;
|
||||
}
|
||||
|
||||
let cachedClaudeBin: string | null = null;
|
||||
|
||||
export function resolveClaudeBin(): string {
|
||||
if (cachedClaudeBin) return cachedClaudeBin;
|
||||
/** Recherche `claude` dans le PATH (sans throw). null si absent. */
|
||||
function findClaudeOnPath(): string | null {
|
||||
try {
|
||||
cachedClaudeBin = execFileSync('which', ['claude'], { encoding: 'utf8' }).trim();
|
||||
return execFileSync('which', ['claude'], { encoding: 'utf8' }).trim() || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isExecutable(path: string): boolean {
|
||||
try {
|
||||
accessSync(path, constants.X_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Résout le binaire `claude`. Si `configuredPath` est fourni (réglage UI), il est utilisé tel quel
|
||||
* — validé exécutable, message clair sinon — et JAMAIS mis en cache (modifiable à chaud). Sinon :
|
||||
* `which claude`, mis en cache. Un service systemd/launchd démarre avec un PATH minimal sans
|
||||
* ~/.local/bin → `which claude` y échoue ; d'où le réglage de chemin explicite (et le PATH figé par
|
||||
* `arboretum install`).
|
||||
*/
|
||||
export function resolveClaudeBin(configuredPath?: string | null): string {
|
||||
if (configuredPath) {
|
||||
if (!isExecutable(configuredPath)) {
|
||||
throw new Error(`Configured Claude CLI path is not executable: ${configuredPath}`);
|
||||
}
|
||||
return configuredPath;
|
||||
}
|
||||
if (cachedClaudeBin) return cachedClaudeBin;
|
||||
const found = findClaudeOnPath();
|
||||
if (!found) {
|
||||
throw new Error(
|
||||
'Claude Code CLI not found in PATH. Install it first: https://code.claude.com/docs/en/quickstart',
|
||||
);
|
||||
}
|
||||
cachedClaudeBin = found;
|
||||
return cachedClaudeBin;
|
||||
}
|
||||
|
||||
/** Diagnostic non-throwing pour l'UI (Réglages) : recalculé à chaque appel, jamais caché. */
|
||||
export function diagnoseClaudeBin(configuredPath?: string | null): ClaudeBinDiagnostic {
|
||||
if (configuredPath) {
|
||||
return { path: configuredPath, source: 'configured', ok: isExecutable(configuredPath) };
|
||||
}
|
||||
const found = findClaudeOnPath();
|
||||
return found ? { path: found, source: 'path', ok: true } : { path: null, source: null, ok: false };
|
||||
}
|
||||
|
||||
/** Module volontairement abstrait : le plan B « BYO API key / Agent SDK » se brancherait ici. */
|
||||
export function buildSpawnSpec(opts: SpawnOptions): SpawnSpec {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
@@ -46,5 +99,5 @@ export function buildSpawnSpec(opts: SpawnOptions): SpawnSpec {
|
||||
}
|
||||
// Session de groupe : relie plusieurs repos/worktrees dans une seule session (P6).
|
||||
for (const dir of opts.addDirs ?? []) args.push('--add-dir', dir);
|
||||
return { file: resolveClaudeBin(), args, env };
|
||||
return { file: resolveClaudeBin(opts.claudeBinPath), args, env };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user