Aller au contenu

Nettoyer les worktrees des agents IA

Codex, Cursor, Conductor et Claude Code peuvent exécuter chaque tâche dans son propre worktree git. Un worktree est une copie de travail complète de votre dépôt, avec ses propres node_modules, ios/Pods, ios/build et build Android. Pour une application React Native, cela représente un à plusieurs gigaoctets par tâche. Les outils suppriment rarement ces worktrees une fois la tâche terminée.

lu-cleaner retrouve ces worktrees où qu'ils se trouvent. Il indique quel outil a créé chacun d'eux et dans quel état se trouve son travail git. Il supprime les worktrees terminés avec git worktree remove, et la branche est toujours conservée.

Fenêtre de terminal
lu-cleaner worktrees # sélecteur interactif (alias : lu-cleaner wt)
lu-cleaner worktrees --list # la même liste sous forme de tableau
lu-cleaner scan -c worktrees --all # avec le risque, les recommandations ★ et les avertissements

$ lu-cleaner worktrees --list NAME TOOL BRANCH STATUS SIZE AGE PATH my-app · feature/login codex feature/login merged 2.40 GB 9d /.codex/worktrees/a1b2/my-app my-app · c3d4 codex (detached) clean 1.90 GB 3w /.codex/worktrees/c3d4/my-app shop-app · fix-cart conductor fix-cart dirty 1.20 GB 2d /conductor/workspaces/shop-app/fix-cart ! 3 uncommitted changes · active Conductor workspace — archive it in Conductor instead my-app · worktree-onboarding claude worktree-onboarding unpushed 850 MB 5d /dev/my-app/.claude/worktrees/onboarding ! 2 unpushed commits (branch worktree-onboarding is kept) my-app · prune 1 stale worktree… ? — ~/dev/my-app   Total 6.35 GB in 5 items · ★ 4.30 GB recommended (3 items) Disk: 38.2 GB free of 494 GB (92% used) → run: lu-cleaner clean --smart -c worktrees

Chaque avertissement est affiché sous sa ligne, avec des détails comme les dépôts imbriqués dans un worktree. Le sélecteur affiche les mêmes informations dans le panneau de détails (enter). La taille de l'élément de purge est ?, car ce que libère git worktree prune n'est connu qu'une fois la commande exécutée.

Outil Emplacement de ses worktrees Type d'élément
Codex ~/.codex/worktrees/<id>/<repo> codex-worktree
Cursor ~/.cursor/worktrees/… cursor-worktree
Conductor ~/conductor/workspaces/<repo>/<workspace> conductor-worktree
Claude Code (CLI) <repo>/.claude/worktrees/<name> (créés par claude --worktree et par l'isolation des sous-agents), ~/.claude/worktrees, ~/.claude-worktrees claude-worktree
Claude desktop ~/Library/Application Support/Claude/worktrees, ainsi que les entrées de <repo>/.claude/worktrees sur des branches claude/… claude-desktop-worktree
Créés à la main N'importe où : chaque worktree que git worktree list signale pour vos dépôts, plus <repo>/.worktrees, <repo>/worktrees, <repo>-worktrees et ~/.worktrees manual-worktree

lu-cleaner identifie aussi les worktrees de Superset (~/.superset/worktrees), Claude Squad, vibe-kanban et Multica. Vous pouvez passer le type d'élément à --kind, par exemple -k codex-worktree.

lu-cleaner cherche les worktrees de quatre façons, ce qui lui permet de trouver aussi ceux qui ne sont pas dans les dossiers habituels :

  1. Il parcourt les dossiers listés ci-dessus, jusqu'à quatre niveaux de profondeur, ainsi que tout dossier indiqué dans worktree_roots dans votre configuration.
  2. Il parcourt vos racines de projets pour trouver les dépôts. Il s'agit des roots de votre configuration, ou de dossiers détectés automatiquement comme ~/dev, ~/Projects et ~/conductor/repos.
  3. Il lance git worktree list dans chaque dépôt trouvé. Cela révèle les worktrees de ce dépôt, où qu'ils se trouvent.
  4. Il regarde dans les dossiers imbriqués habituels (.claude/worktrees, .worktrees, worktrees, <repo>-worktrees).

Si plusieurs de ces méthodes aboutissent au même worktree, il n'est compté qu'une fois : lu-cleaner compare le périphérique et l'inode du dossier. Les sous-modules, et les worktrees de sous-modules, ne sont pas des worktrees liés de vos dépôts et sont laissés de côté.

Certains worktrees sont listés mais ne peuvent pas être sélectionnés :

  • Sur un volume externe. Le supprimer ne libère rien sur votre disque interne.
  • Dépôt principal sur un volume non monté. Son statut reste unknown tant que le volume n'est pas branché.
  • Données git illisibles, à cause des permissions ou de la protection de la vie privée de macOS. Son état est inconnu, il est donc seulement signalé. Rétablissez l'accès (par exemple en accordant l'accès complet au disque à votre terminal), puis relancez le scan.
  • Dépôt principal déplacé ou renommé. Git suit toujours le worktree, ce n'est donc pas un orphelin. L'avertissement donne la commande git -C <new location> worktree repair <path> qui le rattache.
  • En dehors de votre dossier personnel, par exemple dans /tmp. Le garde-fou de sécurité ne supprime que dans votre dossier personnel et dans votre dossier temporaire utilisateur. L'élément affiche la commande git -C … worktree remove … exacte à lancer vous-même.
  • Votre répertoire courant. Lancez lu-cleaner depuis un autre dossier.

Chaque worktree reçoit un seul statut. Lorsque plusieurs statuts s'appliquent, celui qui figure le plus haut dans ce tableau l'emporte :

Statut Signification Risque Supprimé sans --force ?
orphan Git ne suit plus le dossier. Son dépôt principal a été supprimé, son entrée a été purgée, ou le dossier est une copie d'un autre worktree. caution Non. Avec --force, il est supprimé comme un simple dossier : git ne peut pas l'inspecter.
locked Verrouillé avec git worktree lock. Les outils le font souvent pendant qu'un agent tourne. caution Non
dirty Contient des modifications non commitées, fichiers non suivis compris caution Non
unpushed Contient des commits qu'aucun remote ne contient : en avance sur sa branche amont, ou sans branche amont moderate sur une branche, caution sur une HEAD détachée Sur une branche, oui : la branche conserve les commits. Sur une HEAD détachée, seulement si une autre branche, un tag ou une branche de suivi distante les contient.
merged Tout est déjà dans la branche par défaut (origin/HEAD, origin/main, origin/master, main ou master), y compris les commits rebasés ou cherry-pickés dessus moderate Oui
clean Aucune modification locale et tout est poussé, mais pas encore fusionné moderate Oui
unknown git status a échoué ou expiré ; le worktree se trouve sur un autre volume ; ses données git ou son dépôt principal sont illisibles ; son dépôt principal est sur un volume non monté ou a été déplacé caution ou rapport seul Seulement si git peut le lire au moment du nettoyage

Quelques précisions sur ces statuts :

  • Orphelin signifie disparu de façon vérifiée. Un worktree n'est orphelin que si ses données git (et, pour un dépôt principal supprimé, le dépôt lui-même) sont absentes et que le dossier parent est lisible. Quand il ne l'est pas (permissions, protection de la vie privée de macOS, volume non monté), le worktree est unknown et seulement signalé. git worktree prune n'est jamais lancé non plus pour une telle entrée.
  • La HEAD détachée est normale avec Codex. Codex extrait souvent une HEAD détachée. Ce n'est pas un problème tant que le commit se trouve aussi sur une branche, un tag ou un remote : le worktree est alors clean ou merged. Cela ne pose problème que lorsque l'agent a créé des commits qui n'existent nulle part ailleurs.
  • Les pull requests fusionnées en squash apparaissent généralement comme unpushed. Prenons une branche de plusieurs commits fusionnée en squash. Ses commits ne correspondent pas un à un à ceux de la branche par défaut, et une fois la branche de la PR supprimée sur le remote, l'avertissement indique upstream branch deleted. La suppression reste sans risque, car la branche locale est conservée. Vérifiez que la PR est bien fusionnée, puis sélectionnez le worktree à la main.

Certaines conditions rendent un worktree caution quel que soit son statut. D'autres l'excluent de la sélection intelligente :

Avertissement Effet
in use by … : un processus a son répertoire courant à l'intérieur (shell, serveur de développement, agent) caution
open in Cursor / open in VS Code : une fenêtre d'éditeur affiche ce dossier caution
N active Codex threads, active Claude desktop session, active Conductor workspace — archive it in Conductor instead caution
moved: git records it at … : le dossier a été déplacé à la main caution. La suppression est refusée tant que vous n'avez pas lancé le git worktree repair suggéré.
ignored secret/local files would be lost: … caution. Git ignore ces fichiers, donc git worktree remove les supprime : .env et .env.*, .envrc, .npmrc, keystores, clés de signature et certificats, profils de provisionnement, google-services.json, GoogleService-Info.plist, surcharges *.local.*, settings.local.json, CLAUDE.local.md… Un fichier identique au même fichier dans la copie de travail principale ne compte pas, pas plus que les modèles comme .env.example.
contains another git repository or worktree: … caution. La suppression est refusée sans --force, car git supprimerait aussi la copie de travail imbriquée.
N unpushed commits (branch … is kept) Reste moderate mais n'est jamais retenu par la sélection intelligente
current directory Ne peut pas être sélectionné du tout

La sélection intelligente ne retient un worktree que si toutes ces conditions sont réunies. La sélection intelligente correspond à la touche a dans le sélecteur et à --smart en ligne de commande.

  • Son statut est clean ou merged, donc son risque est moderate.
  • Il n'a aucun avertissement : pas de commits non poussés, pas de fichiers secrets ignorés, pas de dépôt imbriqué, et aucun processus, fenêtre d'éditeur ou session d'agent active qui l'utilise.
  • Il est inactif depuis au moins 7 jours, ou 1 jour s'il est fusionné dans la branche par défaut. Si votre réglage stale_after est inférieur à 7 jours, c'est lui qui s'applique.

« Inactif » désigne le temps écoulé depuis la dernière activité du worktree. lu-cleaner retient le plus récent de ces événements : la création du worktree, son dernier commit, les modifications de son index et de sa HEAD, les dates de modification de ses fichiers modifiés, et la dernière activité du fil Codex ou de la session Claude desktop qui l'utilise. Il ignore la date de modification du dossier lui-même, car Expo, Xcode et les indexeurs la mettent à jour même quand personne n'y travaille.

Les entrées de worktree obsolètes (voir plus bas) sont toujours recommandées. Les orphelins ne le sont jamais.

Consultez Niveaux de risque et sélection intelligente pour les règles qui s'appliquent aux autres catégories.

Supprimer le worktree, ou seulement ses artefacts ?

Section intitulée « Supprimer le worktree, ou seulement ses artefacts ? »

Supprimer un worktree libère tout, mais la copie de travail de la tâche disparaît. Supprimer seulement ses artefacts (node_modules, ios/Pods, builds natifs) libère en général l'essentiel de l'espace et conserve les sources, les modifications non commitées, les fichiers .env et le lien de l'outil vers le dossier.

Pour savoir ce que libère chaque option, consultez les détails du worktree. Dans le sélecteur, appuyez sur enter. Avec --json, lisez meta. La taille y est répartie entre node_modules, ios/Pods, ios/build, android/app/build, android/.gradle, android/.cxx, .expo, .next, .turbo, dist, leur total artifacts, et la copie de travail elle-même (checkout).

Situation Que faire Pourquoi
La tâche est terminée : branche fusionnée ou poussée, rien d'ouvert dedans Supprimer le worktree Libère la copie de travail et ses artefacts. La branche reste dans le dépôt.
Vous reviendrez sur la tâche, ou son fil d'agent ou son espace de travail est encore actif Supprimer seulement les artefacts Conserve les sources et l'état de l'outil. Une réinstallation ramène les dépendances.
Le worktree est dirty Supprimer seulement les artefacts, ou commiter ou faire un stash d'abord La suppression du worktree serait de toute façon refusée
Il contient des fichiers .env ou des clés ignorés par git dont vous avez encore besoin Les copier ailleurs d'abord, ou supprimer seulement les artefacts git worktree remove supprime les fichiers ignorés
HEAD détachée avec des commits présents sur aucune branche Lancer git branch <name> à l'intérieur, puis le supprimer Sinon ces commits deviennent inaccessibles
C'est un espace de travail Conductor L'archiver dans Conductor Conductor conserve son commit d'archive et sa propre vue reste cohérente
orphan Regarder d'abord à l'intérieur. Le supprimer avec --force si rien ne mérite d'être récupéré Git ne peut pas savoir s'il contient du travail unique

Le scanner d'artefacts parcourt aussi chaque dossier de worktree. Le nom de chaque artefact commence par l'outil dont le worktree le contient, par exemple codex:my-app › node_modules. Pour n'afficher que les artefacts situés dans les worktrees d'un outil, ouvrez le sélecteur d'artefacts et filtrez sur ce préfixe :

Fenêtre de terminal
lu-cleaner artifacts

Appuyez ensuite sur / et tapez le préfixe :

Filtre Affiche les artefacts situés dans
codex: les worktrees Codex
cursor: les worktrees Cursor
conductor: les espaces de travail Conductor
claude: les dossiers .claude/worktrees (Claude Code, et les worktrees Claude desktop qui y sont stockés)
claude-desktop: ~/Library/Application Support/Claude/worktrees
worktree: les autres worktrees liés : dossiers .worktrees et copies de travail créées à la main sous vos racines

Appuyez sur a pour les sélectionner avec la sélection intelligente, ou sur space pour choisir les éléments un par un. Appuyez ensuite sur d pour nettoyer. La sélection intelligente ne retient jamais les artefacts d'un projet actif au cours des dernières 24 heures. Toutes les touches sont décrites dans Raccourcis clavier.

Vous pouvez sélectionner en même temps un worktree et certains artefacts qu'il contient. Les artefacts sont alors retirés du plan et rien n'est compté deux fois.

  1. Tout est revérifié. Votre scan peut dater de plusieurs minutes, donc lu-cleaner revérifie le worktree juste avant d'agir :

    • Le dossier doit être le même que lors du scan (même inode).
    • Il doit passer le garde-fou de sécurité.
    • Il ne doit contenir ni votre répertoire courant, ni un volume monté ou une image disque.
    • Aucun autre processus ne doit y avoir son répertoire courant ni y exécuter un programme.
    • Aucun fil Codex, aucune session Claude desktop ni aucun espace de travail Conductor ne doit avoir commencé à l'utiliser depuis le scan.
    • Git doit toujours enregistrer le worktree à ce chemin. Un dossier déplacé ou copié à la main est refusé, avec la commande git worktree repair à lancer.

    Si une vérification échoue, l'élément est ignoré et la raison est indiquée.

  2. Les vérifications git propres à lu-cleaner s'exécutent. Sauf si vous passez --force, la suppression est refusée dans ces cas :

    • Le worktree est verrouillé.
    • Il contient un autre worktree du même dépôt, ou n'importe quel autre dépôt git (un dossier avec une entrée .git, ou un dépôt bare), même dans un dossier ignoré. Git le supprimerait avec lui. Les dossiers de dépendances comme node_modules et Pods ne sont pas fouillés.
    • La HEAD est détachée et certains de ses commits ne sont sur aucune branche, aucun tag ni aucune référence de suivi distante.
    • git status signale une modification, suivie ou non suivie. lu-cleaner passe des options explicites, donc un réglage status.showUntrackedFiles = no dans votre configuration git ne peut pas masquer les fichiers non suivis.
    • Son dépôt principal n'existe plus, donc git ne peut pas chercher de travail non commité.
    • lu-cleaner ne peut pas déterminer si des commits seraient perdus, ou ne peut pas lire le statut git.

    --dry-run exécute ces mêmes vérifications et signale les mêmes refus.

  3. Git supprime la copie de travail. lu-cleaner lance git worktree remove <path> depuis le dépôt principal, sans --force. Git applique ses propres refus comme seconde ligne de défense : fichiers modifiés ou non suivis, verrous, sous-modules. Les fichiers ignorés comme node_modules, Pods et .env ne bloquent pas la suppression, et ils sont supprimés avec la copie de travail. Git supprime aussi l'entrée du worktree dans le .git/worktrees/ du dépôt principal.

  4. La branche est conservée. Ses commits restent dans le dépôt principal, qu'ils soient poussés ou non. C'est pourquoi les commits non poussés sur une branche nommée ne risquent rien. Quand vous n'avez plus besoin de la branche, supprimez-la vous-même, par exemple avec git branch -d feature/login.

  5. Le dossier de tâche vide est supprimé. Codex range chaque worktree dans un dossier par tâche, comme ~/.codex/worktrees/a1b2/. S'il ne reste que ses fichiers marqueurs (.codex-worktree-name, .DS_Store), lu-cleaner supprime aussi ce dossier.

  6. La suppression est enregistrée. Les worktrees sont supprimés un par un, après les suppressions de fichiers exécutées en parallèle. Chaque suppression est ajoutée à l'historique.

Quand vous sélectionnez un worktree et un autre worktree imbriqué à l'intérieur, les deux restent dans le plan et le worktree intérieur est supprimé en premier, avec ses propres vérifications. Si le worktree intérieur n'est pas supprimé, le worktree extérieur est conservé lui aussi.

Les worktrees ne sont jamais déplacés dans la Corbeille : en mode Corbeille (--trash, use_trash, ou t dans le sélecteur), ils sont ignorés avec not possible in Trash mode (it would delete permanently).

Il arrive qu'un dossier de worktree soit supprimé avec rm, par Cursor ou Conductor, ou par macOS lorsqu'il fait le ménage dans /tmp. Git conserve alors une entrée pour lui dans le dépôt principal et, tant que cette entrée n'est pas purgée, git refuse d'extraire ou de supprimer sa branche ailleurs. lu-cleaner affiche un élément par dépôt contenant de telles entrées, par exemple my-app · prune 2 stale worktree entries :

  • Le risque est safe, et l'élément est toujours recommandé.
  • Son nettoyage lance git -C ~/dev/my-app worktree prune.
  • Une entrée n'est considérée comme obsolète que si sa copie de travail n'existe plus, de façon vérifiée. Certaines entrées peuvent appartenir à une copie de travail située sur un volume non monté, à une copie de travail illisible, ou à une copie de travail déplacée. git worktree prune les supprimerait elles aussi. Dans ce cas, lu-cleaner se contente de supprimer les dossiers de métadonnées des entrées vérifiées au lieu de lancer la commande.
  • Les deux variantes sont revérifiées juste avant le nettoyage.

Un orphelin est un dossier qui contient encore un fichier .git, mais que git ne connaît plus. Git ne peut pas le supprimer, ni savoir s'il contient du travail qui n'existe nulle part ailleurs. Un orphelin est donc :

  • caution et jamais recommandé ;
  • supprimé comme un simple dossier, après le garde-fou de sécurité, seulement avec --force. Sans cette option, l'élément est ignoré avec needs --force: orphaned: …, y compris lors d'une simulation. En mode Corbeille, il peut être déplacé dans la Corbeille à la place, ce qui est réversible ;
  • revérifié juste avant la suppression : même fichier .git, données git toujours absentes, et aucun autre dépôt ni worktree à l'intérieur. Un orphelin qui contient un autre dépôt est seulement signalé : rm -rf emporterait ce dépôt avec lui.

La note de l'élément indique comment récupérer le code : cloner à nouveau le remote, copier les modifications dans un nouveau worktree lorsque seule son entrée a été purgée, ou rattacher le dossier avec git worktree repair lorsque vous avez déplacé le dépôt principal. Dans le sélecteur, vous devez taper yes pour confirmer une sélection qui contient des éléments caution ou des éléments qui nécessitent --force.

lu-cleaner lit l'état de chaque outil pour vous avertir avant que vous ne supprimiez un worktree que l'outil utilise encore. Il ne modifie jamais cet état. Voici ce que voit chaque outil après une suppression :

Outil Après la suppression du worktree Meilleure première étape
Codex lu-cleaner ne touche pas à l'historique du fil, mais le fil désigne toujours le dossier disparu comme répertoire de travail : un fil repris n'a donc plus de copie de travail dans laquelle travailler. Les worktrees ayant des fils actifs sont caution. Quand l'application Codex a déjà mis un worktree en file d'attente pour archivage, la note l'indique : l'application en fait un instantané et le supprime elle-même. Archiver le fil dans Codex, qui fait d'abord un instantané du worktree
Cursor Les modifications de l'agent pas encore appliquées n'existaient que dans le worktree : elles sont perdues. Cursor crée de nouveaux worktrees quand il en a besoin. Appliquer ou abandonner d'abord les modifications de l'agent
Conductor Conductor affiche l'espace de travail comme manquant. Les worktrees des espaces de travail non archivés sont caution. Quand un espace de travail est déjà archivé dans Conductor, la note l'indique et la suppression se passe sans incident. Archiver l'espace de travail dans Conductor, qui conserve son commit d'archive
Claude desktop L'application conserve les worktrees après l'archivage d'une session. Les supprimer est le ménage qu'elle vous laisse. Les worktrees d'une session active sont caution. Archiver d'abord la session
Claude Code (CLI) Le travail commité reste sur la branche (par exemple worktree-onboarding). Les fichiers ignorés (.env, node_modules) sont perdus. Commiter ce que vous voulez garder
Créés à la main La branche reste. Les fichiers ignorés et les builds natifs sont perdus. Aucune

lu-cleaner worktrees --yes nettoie sans le sélecteur, avec les mêmes règles que clean --yes :

Fenêtre de terminal
# Simulation : worktrees recommandés uniquement, rien n'est supprimé
lu-cleaner worktrees --yes --smart --dry-run
# Les supprimer
lu-cleaner worktrees --yes --smart
# Worktrees Codex inactifs depuis un mois (simulation)
lu-cleaner clean --yes -k codex-worktree --older-than 30d --dry-run

Les worktrees caution (modifiés, verrouillés, orphelins, en cours d'utilisation, avec une session active, avec des fichiers secrets ou un dépôt imbriqué) sont écartés, sauf si vous passez --risk caution. C'est aussi le cas des worktrees qui ont un avertissement, comme des commits non poussés sur une branche : le plan les liste sous Held back. Même avec --risk caution, les worktrees modifiés, verrouillés, imbriqués ou avec des commits sur une HEAD détachée sont toujours refusés au moment du nettoyage, et les orphelins ne sont pas supprimés, sauf si vous passez aussi --force.

Un élément ignoré n'est pas un échec : la commande se termine quand même avec le code 0. Le récapitulatif liste chaque élément ignoré avec sa raison :

Message Que faire
N uncommitted change(s) — commit them or use --force Commiter, mettre en stash ou abandonner les modifications, ou supprimer seulement les artefacts
detached HEAD with N commit(s) on no branch — they would be lost; … Lancer git branch <name> dans le worktree, puis relancer la suppression
worktree is locked (git worktree lock) — use --force Si l'outil qui l'a verrouillé a terminé, lancer git worktree unlock <path>
contains another worktree, … ou contains the git repository … Supprimer ou déplacer d'abord la copie de travail imbriquée
needs --force: orphaned: … Regarder dans le dossier, puis relancer avec --force si rien ne mérite d'être conservé
git records this worktree at …, not here: … Lancer le git worktree repair suggéré (dossier déplacé), ou supprimer la copie vous-même
in use: process 1234 is working inside … Quitter le shell, le serveur de développement ou l'agent qui y tourne
1 active Codex thread since the scan — rescan (ou une session Claude desktop, un espace de travail Conductor) Un agent a commencé à l'utiliser : archivez d'abord le fil ou la session
current directory is inside … Lancer lu-cleaner depuis un autre dossier
… changed since the scan (different inode) — rescan Relancer un scan : le dossier a été remplacé
not possible in Trash mode (it would delete permanently) Relancer sans --trash (ou avec --trash=false quand use_trash est activé)
git refused to remove it (…) — use --force Lire le message de git, qui a ses propres raisons