Commit Graph

41 Commits

Author SHA1 Message Date
6741b2d47c feat(web): panneaux IDE Git / Sessions / Groupes
Some checks failed
CI / Build & test (Node 22) (push) Successful in 10m15s
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 24) (push) Has been cancelled
components/ide/GitPanel.vue : CommitPanel ciblant le worktree actif (ide.activeContext), chargement des changements + abonnement disque (watchWorktree) ré-abonné au changement de worktree ; sélection d'un fichier ouvre un onglet diff. SessionsPanel : sessions vivantes -> ouverture dans le dock. GroupsPanel : groupes (getters) avec passerelle vers la vue Groupes.

PrimarySidebar aiguille désormais Explorer/Git/Sessions/Groupes vers ces panneaux (fin des placeholders). vue-tsc + build web verts.
2026-07-17 17:17:27 +02:00
5302bf29ae feat(web): dock terminaux en bas (multi-sessions attachées)
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
components/ide/TerminalDock.vue + TerminalDockTabs.vue : dock à onglets, une TerminalView par session du dock, toutes montées en v-show pour préserver l'attache WS (seule l'active visible). Fermer un onglet démonte réellement la vue -> detach() propre, pas de fuite. DialogPrompt au-dessus du terminal actif en attente. Repli via la barre de statut ou le chevron.

Branché dans IdeShell (section dock redimensionnable, splitter inversé). 427 tests.
2026-07-17 17:14:40 +02:00
f62404234b feat(web): zone éditeur à onglets (Monaco multi-modèles + diff)
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
components/ide/EditorArea.vue : UNE instance Monaco partagée avec cache de modèles par onglet (préserve contenu, undo et curseur au changement d'onglet ; view states restaurés), thème arboretum-dark, save + conflit STALE_FILE + reload/overwrite, dispose des modèles à la fermeture d'onglet et au démontage. Onglets diff via DiffViewer.

components/ide/EditorTabs.vue : bandeau d'onglets multi-projets, point de modification, fermeture immédiate si propre et garde à deux clics si modifié. Bascule éditeur/diff par onglet. Branché dans IdeShell (centre). 427 tests.
2026-07-17 17:12:15 +02:00
c96d929c7a feat(web): coquille IDE + arbre de projets unifié (route /ide)
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
components/ide/ : IdeShell (grille barre d'activité + panneau gauche + zone centrale + dock + barre de statut, splitters persistés, réconciliation des ressources ouvertes contre les données live), ActivityBar (Explorer/Git/Sessions/Groupes + Réglages/Aide), PrimarySidebar, ProjectTree + ProjectTreeNode (arbre unique repos -> worktrees -> sessions, fichiers embarqués via FileTree en mode sans chrome, dépliage paresseux), StatusBar.

Route /ide (layout 'ide'), entrée de nav « IDE », namespace i18n ide (EN/FR). Clics de l'arbre pilotent le store (contexte worktree, ouverture de fichier/terminal). Centre et dock en placeholders (remplis en B4/B5). FileTree gagne un mode embedded. 427 tests.
2026-07-17 17:08:35 +02:00
6acd4a16fd feat(web): store IDE (onglets, dock terminaux, panneaux, réconciliation)
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
stores/ide.ts : état de vue de l'IDE (onglets éditeur, dock de sessions, activité, contexte worktree actif, tailles/visibilité des panneaux, expansion de l'arbre), en références uniquement (ids/chemins), persisté par persistedRef via mises à jour immuables. dirty reste volatile.

pruneDeadResources réconcilie au bootstrap (retire onglets/terminaux dont worktree/session n'existent plus). Store pur, sans couplage aux stores de données (résolution côté composants). 11 tests unitaires.
2026-07-17 16:59:44 +02:00
cf60fa3a56 feat(web): infra IDE réutilisable (persisted-ref, splitter, bootstrap temps réel)
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
Extrait persistedRef dans lib/persisted-ref.ts (partagé, testable en node). Ajoute useSplitter + components/ide/PanelSplitter.vue (poignée de resize générique par pointer events, largeur/hauteur persistable), généralisant le resize souris de WorkspaceView.

Hisse le chargement initial et les abonnements temps réel dans useRealtimeBootstrap (monté par App.vue) : un seul propriétaire actif sur TOUS les layouts, y compris la vue IDE plein écran. AppShell et WorkspaceView n'en portent plus la responsabilité. Tests unitaires persisted-ref et useSplitter (10).
2026-07-17 16:55:14 +02:00
9a496abd00 feat(web): couche de tokens de design centralisée (surfaces, statuts, terminal)
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
Introduit une couche sémantique dans style.css (@theme) : surfaces (surface-0..3), bordures, texte (fg/muted/subtle) et accent, aliasant la palette Tailwind (rendu identique, vérifié dans le CSS produit). Ajoute les métriques du chrome IDE (--ide-*) pour la refonte à venir.

Centralise deux sources uniques : lib/terminal-theme.ts (thème + police xterm) et lib/status-tokens.ts (tons de statut, dédupliqués entre BaseBadge et SessionStateBadge). Définit le thème Monaco « arboretum-dark ». Remplace les #09090b épars des composants par bg-surface-0 (ne restent que index.html et les renderers exigeant un hex concret).
2026-07-17 16:50:58 +02:00
65ef616867 chore(typo): retire tous les tirets cadratins/demi-cadratins + garde CI
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
Deploy site (production) / build-and-deploy (push) Successful in 20s
Remplace les 547 tirets cadratins (U+2014) et demi-cadratins (U+2013) des fichiers versionnés par la ponctuation contextuelle adaptée (point médian, deux-points, virgule, parenthèses ; tiret simple pour les plages), sur 122 fichiers (appli, vitrine, doc, tests, workflows, scripts).

Ajoute le job CI « lint-dashes » (git grep -P) qui échoue si un tiret cadratin/demi-cadratin réapparaît, hors logo binaire et captures brutes du terminal (fidélité des fixtures de détection de dialogue).
2026-07-17 16:44:00 +02:00
08695a707d feat(p12a): services git distants (PAT/app_password) + clone HTTPS
Modèle de données :
- migration #11 git_credentials (secrets chiffrés SecretBox : secret_encrypted ; colonnes ssh/oauth posées pour P12b/P12c) ; #12 repos ALTER remote_url/git_service/credential_id (pas de FK)
- types partagés api.ts (GitCredentialSummary sans secret + hasSecret/secretLast4, CRUD, RemoteRepoSummary, Clone*) ; protocole additif : topic 'clones' + message clone_update (CloneOperation)

Backend :
- core/git-credentials.ts (GitCredentialsManager(db, box)) : CRUD chiffré, test() (GET /user), getSecret()/authContext() internes, NULLification de repos.credential_id à la suppression
- core/git-clients/ (github/gitlab/gitea) via fetch, sans dépendance : verify()+listRepos() paginés, erreurs typées AUTH_FAILED/RATE_LIMITED/UNREACHABLE, SSRF base_url http(s)
- core/git-auth.ts : withGitAuth (GIT_ASKPASS éphémère 0o700, secret par env, GIT_TERMINAL_PROMPT=0, jamais dans l'URL/.git/config, nettoyage finally)
- core/git.ts cloneRepo (spawn git clone --progress, parse progression, timeout)
- core/clone-manager.ts (EventEmitter) : clone async, dest confiné sous scanRoots + non existant, auto-enregistrement via addRepo + métadonnées de provenance, nettoyage du clone partiel, events topic 'clones'
- routes/git-connections.ts (CRUD + /test + /:id/repos + POST /repos/clone 202 + GET /repos/clone/:id) ; app.ts câble box→GitCredentialsManager + CloneManager→gateway ; gateway relaie 'clones'

Frontend :
- ws-client subscribeClones ; stores git-connections + clone (suivi WS)
- components/settings/GitConnectionsSection (liste + formulaire pat/app_password, secret jamais ré-affiché) inséré dans SettingsView ; CloneRepoModal (connexion → repos distants paginés → dest scanRoots[0] → barre de progression WS → redirection) ; bouton « Cloner » dans DashboardView ; i18n EN+FR

Tests : git-credentials (round-trip SecretBox, résumé sans secret, NULLification) ; acceptance-p12.mjs (clone bare local file:// → clone_update done + repo enregistré + secret ABSENT de l'API, de la DB et du .git/config)

Sous-phases restantes : P12b (SSH), P12c (OAuth device flow).
2026-06-27 14:27:18 +02:00
e8d10b7ec0 feat(p11): temps réel complet (watcher checkout principal + topic settings)
P11-A — branche du checkout principal modifiée hors Arboretum :
- fs-watcher: pin « permanent » non évinçable (pinRepo/unpinRepo, repoPins dans evictIfNeeded)
- worktree-manager: armMainCheckoutWatchers() + arm/désarm sur addRepo/découverte/removeRepo/hidden
- index.ts: armMainCheckoutWatchers() dans runDaemon → git checkout CLI sur le principal → worktree_update <500ms sans watch client

P11-B — réglages en temps réel :
- protocole additif: type SettingsBroadcast (source unique, réutilisé par SettingsResponse), topic 'settings', message settings_update (validés parseClientMessage + gateway)
- core/settings-bus.ts (EventEmitter) ; routes/settings émet après PATCH ; gateway relaie aux abonnés 'settings'
- web: ws-client subscribeSettings + routage ; store settings applySettings/startRealtime ; AppShell abonne globalement ; SettingsView re-sync des drafts scalaires sans écraser une saisie en cours
- tests: protocol (topic settings) + fs-watcher (pinRepo non évincé) ; acceptance-p11.mjs (checkout principal <500ms + settings_update)
2026-06-27 14:08:44 +02:00
92670a796a feat(p9): commit/push avancé dans l'IDE (staging sélectif, discard, amend, fetch/pull)
- components/workspace/CommitPanel.vue : staging sélectif par fichier (stage/unstage/stage all/unstage all), discard avec confirmation (includeUntracked), zone de commit (message + amend), commit mode 'staged', push, fetch, pull --ff-only (rebase proposé en cas de divergence), gestion 409 ALREADY_PUSHED
- components/workspace/FileRow.vue : ligne de fichier réutilisable (badge statut M/A/D/?/U + clic → diff + slot actions)
- WorkspaceView : CommitPanel remplace ChangedFilesPanel ; @changed → reload des changements
- stores/worktrees : applyWorktree (upsert depuis les mutations git → header live)
- i18n EN+FR (commit.*) ; acceptance-p9.mjs (staging sélectif, amend OK/409, pull ff-only OK + divergence refusée)
2026-06-27 13:53:05 +02:00
75efecf93f feat(p8): vue IDE /workspace (arbre + Monaco + diff + terminal)
- backend: garde-fou conflit d'édition par mtime (GET renvoie mtime, PUT refuse en 409 STALE_FILE si baseMtime périmé)
- lib: wt-key (base64url du chemin worktree), diff-parse (parseur pur du diff unifié → hunks)
- composables: useMonaco (import dynamique → chunk isolé), useWorkspaceLayout (largeurs/panneau persistés)
- components/workspace: GitStatusBadge, FileTree+FileTreeNode (lazy fs/list includeFiles), ChangedFilesPanel, DiffViewer (coloration +/-), MonacoEditor (Ctrl+S + bannière conflit reload/overwrite)
- views/WorkspaceView: 3 colonnes desktop redimensionnables + SegmentedControl mobile, header live (GitStatusBadge), watchWorktree → refresh diff/changements, terminal de la session corrélée
- router meta 'ide' + route /workspace/:repoId/:wt ; App.vue plein écran sans AppShell ; WorktreeCard bouton « Ouvrir l'IDE »
- deps: monaco-editor (lazy, vendor-monaco isolé, 0 impact bundle initial) ; vite manualChunks
- i18n EN+FR (workspace/editor/diff/git) ; tests diff-parse + wt-key ; acceptance-p8.mjs (mtime/409 + diff)
2026-06-27 13:47:10 +02:00
c8dd539571 feat(p10): archivage automatique des sessions terminées (rétention configurable)
- DB: migration #10 (sessions.archived_at + index), helpers archive/unarchive/archiveExpired (soft-archive, jamais de DELETE)
- core/retention-settings.ts: session_retention_days (défaut 30, 0=jamais, 1-3650) + session_purge_days (off)
- core/session-archive.ts: scheduler start/stop/sweep (calqué DiscoveryService), câblé dans runDaemon
- pty-manager.list({includeArchived}) + SessionSummary.archived + emitHistoricalUpdate
- routes: GET /sessions?includeArchived, POST/DELETE /sessions/:id/archive, POST /sessions/archive-now
- protocole additif: message WS session_archived (relayé par la gateway, abonnés 'sessions')
- settings: retentionDays/purgeDays exposés et validés dans PATCH /settings
- web: store sessions (showArchived + archive/unarchive), SessionsListView (toggle/badge/actions), SettingsView (slider rétention), i18n EN+FR
- tests: retention-settings + session-archive (vitest) + settings-routes étendu ; acceptance-p10.mjs (sweep, event WS, resume d'une session archivée → 201, rétention=0)
2026-06-27 13:31:28 +02:00
be43911dc0 feat(p7): moteur git détaillé, API fichiers et watcher FS temps réel
Fondations de la refonte « vrai IDE IA worktree » (chantier P7→P12).

- git.ts : listChanges (status --porcelain=v2 -z + --numstat fusionnés),
  fileDiff (diff unifié borné, refus binaire, untracked via --no-index),
  stage/unstage/restore/clean, commitStaged, amendCommit (refus si poussé),
  fetchRemote, pull (ff-only|rebase), lastCommit, resolveGitDir,
  isSafeRelativePath ; worktreeStatus enrichi (compteurs staged/unstaged/
  conflict + dernier commit), champs additifs WorktreeGitStatus.
- core/fs-watcher.ts : FsWatcherService (chokidar, refcount + pool LRU borné,
  ignore .git sauf HEAD/index, débounce) → invalide factsCache, rediffuse
  worktree_update et émet worktree_changes.
- WorktreeManager : getWorktreeChanges/getFileDiff/stage/unstage/discard/
  fetch/pull/watch/unwatch/assertPathInWorktree ; commitWorktree { mode, amend }.
- routes/git.ts (changes/diff/stage/unstage/discard/fetch/pull) et
  routes/files.ts (GET/PUT contenu fichier, bornage strict au worktree) ;
  fs/list?includeFiles=1 (flag isFile).
- protocole ADDITIF (PROTOCOL_VERSION inchangé) : messages WS watch/unwatch
  (validés dans parseClientMessage) + worktree_changes poussé ciblé par la
  gateway ; front wsClient.watchWorktree + lib/git-api.ts + api.put.
- tests git/fs-watcher/protocole + acceptance-p7.mjs (ALL GREEN).
- dépendance : chokidar (serveur).
2026-06-27 12:15:00 +02:00
0f126cf911 fix(web): repeindre les terminaux au retour-visible + flush ACK ciblé
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
Un terminal caché dans une grille de groupe (ou hors-écran) n'était pas repeint
au retour — `visibilitychange` ne couvre que l'onglet — d'où l'écran noir avec
curseur ; et son ACK ne progressait plus (callback xterm.write throttlé) → le
serveur mettait le PTY en pause → sortie « par à-coups ». Ajoute un
IntersectionObserver (repaint + flushAck ciblé au retour-visible) et expose
Attachment.flushAck (factorisé avec flushAcks).
2026-06-24 10:15:29 +02:00
b5236b41c8 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é.
2026-06-24 10:15:29 +02:00
8b9060e0c0 feat: création de projet (nouveau dossier + session) & en-tête de session enrichi
- POST /api/v1/projects : crée <root>/<name> (mkdir non récursif, 409 si existant),
  git init optionnel, puis lance une session dedans. Anti-traversal sur le nom (core/project.ts).
- Web : NewProjectModal (racine préremplie scanRoots[0], case git init) + bouton dans SessionsListView.
- SessionContextBar : badge du groupe (cliquable) + repos couverts avec leur branche dans l'en-tête
  de session ; session simple = repo+branche déduits du cwd.
- 14 tests (unitaires + route).
2026-06-23 18:52:22 +02:00
057c2d55ed docs: documenter l'extension VS Code (README EN/FR, centre d'aide, site)
- README.md / README.fr.md : bullet dans « What Arboretum does », section dédiée
  « VS Code extension » (build/packaging VSIX, install, sign in), ligne monorepo.
- Centre d'aide in-app (help-content.ts) : item « VS Code extension » dans Integrations (EN/FR).
- Site vitrine : 9e carte de features « Native VS Code extension » (feat9, EN/FR + icône).
2026-06-23 17:43:12 +02:00
663ae7ace1 fix: recherche worktrees, sessions externes masquées, rendu/historique terminal & plein écran grille
- Recherche : le watcher débouncé du singleton useWorktreeView est créé dans un effectScope
  détaché → ne gèle plus après une navigation ; recherche aussi par label de dépôt ; accès à
  la palette depuis le MoreSheet mobile (sidebar/⌘K indisponibles sur mobile).
- Sessions : sessionsForCwd exclut désormais toujours les sessions masquées ; les sessions
  externes (discovered) sont masquées par défaut côté UI, avec puce de révélation par carte et
  case globale dans le dashboard.
- Terminal : ACK découplé du callback de rendu (armé à la réception + flush au retour de
  visibilité) → plus de pause PTY quand l'onglet passe en arrière-plan ; repaint forcé sur
  perte de contexte WebGL / resize / retour en avant-plan. Historique élargi : REPLAY_TAIL_BYTES
  1 Mo, RING_CAPACITY 4 Mo, scrollback xterm 20k.
- Grille de groupe : bouton « plein écran » sur chaque cellule ouvrant /sessions/:id.
2026-06-23 17:42:49 +02:00
f4fb6c3b52 feat(worktrees): création intelligente (auto) + commit/push/promotion + grille agrandissable
- addWorktree résout la branche par dépôt (branchExists) : checkout si présente, suivi de origin/<b> si seulement remote, sinon création (-b) depuis la branche par défaut. Corrige le 'fatal: invalid reference' sur les groupes hétérogènes ; newBranch -> mode (déprécié, mappé en route).
- Nouvelles opérations git : commit (add -A), push (upstream auto), promotion « en principal » (la branche du worktree devient le checkout principal, sans merge ni conflit, ancienne branche conservée). Routes /worktrees/{commit,push,promote} auditées + GET /repos/:id/branches.
- Web : actions Commit/Push/Promouvoir sur WorktreeCard, sélecteur de branche de base (datalist) dans RepoSection et GroupSessionModal, agrandissement d'un terminal au choix dans TerminalGrid.
- Court-circuite la session de groupe quand aucun worktree n'a pu être créé (fini le masquage par « no resolvable worktree »).
- Tests : git.ts (branchExists/listBranches/commitAll/auto), worktree-manager (commit/promote/branches) ; acceptance P5 étend le scénario worktree de groupe sur branche neuve + commit + promotion.
2026-06-22 19:55:45 +02:00
dacff2b98f feat(sessions): masquer les sessions externes + terminal plein écran
Masquage persistant des sessions Claude découvertes (lancées hors Arboretum)
qui polluent la liste — calqué sur repos.hidden :
- table hidden_sessions (#9) clé claudeSessionId ; helpers DB
- DiscoveryService pose le champ additif SessionSummary.hidden (resume/fork
  restent possibles, survit à un re-scan)
- routes POST/DELETE /sessions/:id/hide, POST /sessions/hide-discovered
  (masse, un clic), GET /sessions?includeHidden=true
- UI : bouton Masquer par ligne, « Masquer l'historique externe », toggle
  « Afficher les masquées », badge masquée ; store + i18n EN/FR + audit

Terminal plein écran (corrige le sizing) :
- SessionView root s'étire (flex-1) → xterm calcule les bonnes dimensions
- refit robuste (requestAnimationFrame + après resync), h-full sur le wrapper
- bouton plein écran (API Fullscreen), tabbar mobile masquée en fullbleed

DialogPrompt : options en liste verticale pleine largeur (plus de troncature
ni de chevauchement sur les libellés longs / multi-lignes).

Doc : centre d'aide (sessions), README EN/FR, site (showcase), CLAUDE.md.
Tests : discovery (hidden) + route hide/unhide/hide-discovered.
2026-06-22 16:02:27 +02:00
d02c1dabd4 docs(help): refléter la session de groupe unique (P6) + retouche FAQ du site
Le centre d'aide décrivait encore le modèle P5 (une session par repo) ;
l'entrée « cross-repo » dit désormais qu'un groupe lance UNE seule session
Claude couvrant tous ses repos (contexte partagé) et s'ouvre sur son terminal.
Inclut aussi un ajustement de copie de la FAQ du site.
2026-06-22 14:41:29 +02:00
86cdc1f597 fix(web): agrandir les terminaux de la grille sur grand écran (hauteur responsive)
Hauteur de cellule relative au viewport (h-[68dvh] min-h-80
lg:h-[calc((100dvh-9rem)/2)]) au lieu de h-80 fixe : terminaux grands sur PC,
exploitables sur mobile. xterm se re-fit via son ResizeObserver.
2026-06-22 14:41:22 +02:00
1b57c3801c feat(groups): session de groupe au parent commun + redirection vers le terminal
Le cwd d'une session de groupe devient le PARENT COMMUN des répertoires
couverts (racine neutre, chemins relatifs cross-repo naturels) au lieu du
premier repo arbitraire ; chaque repo reste relié en --add-dir. Garde-fou :
si le parent commun est la racine du FS, on retombe sur le premier répertoire.
Nouveau module pur resolveGroupCwd (core/group-session.ts). Côté web, un
lancement réussi redirige vers le terminal plein écran de la session, avec
un toast (succès ou couverture partielle).
2026-06-22 14:41:15 +02:00
915fdf185d feat(worktrees): démarrer une session sur la branche principale ou tout worktree
Ajoute le choix « créer un worktree OU bosser sur la branche principale » au
moment de démarrer le travail, sur deux surfaces :
- bouton « Démarrer » (claude/bash) sur chaque WorktreeCard, y compris le
  checkout principal (badge « principal ») — comblait un manque : impossible
  jusqu'ici de lancer une session sur un worktree existant depuis le dashboard ;
- toggle de mode (Worktree / Branche principale) dans le formulaire d'entête
  de RepoSection.

Option git « créer & basculer » : nouvel endpoint POST /api/v1/repos/:id/session
qui lance UNE session dans le checkout principal (repo.path), avec création ou
bascule de branche optionnelle (git switch[-c]) — refusée en 409 si l'arbre est
sale. Pas de hooks ni de pré-trust (c'est le dépôt réel de l'utilisateur).

- shared : type additif StartRepoSessionRequest.
- server : git.switchBranch + WorktreeManager.startMainSession + route.
- web : store worktrees.startMainSession, UI WorktreeCard/RepoSection, i18n fr/en.
- tests : +5 unitaires (switchBranch, startMainSession) ; acceptance-p3 étendue.
2026-06-22 11:30:40 +02:00
c6ea9f7930 feat(groups): session Claude unique par groupe via --add-dir (P6)
All checks were successful
Deploy site (production) / build-and-deploy (push) Successful in 21s
Un groupe lance désormais UNE seule session Claude couvrant tous ses
repos/worktrees (--add-dir) au lieu d'une session par repo, pour
travailler en simultané dans une conversation partagée. Le transport WS,
le flow control et l'attach sont inchangés (toujours 1 session, 1 channel).

- shared: SessionSummary.addedDirs/groupId (champs additifs optionnels) ;
  CreateGroupSessionRequest / GroupSessionResponse.
- server: --add-dir dans claude-launcher ; spawn({addDirs,groupId}) +
  persistance (migration #8 : sessions.added_dirs/group_id) ;
  POST /api/v1/groups/:id/session (résolution des dirs cote serveur) ;
  resume re-relie les dirs ; deleteGroup nullifie group_id.
- web: GroupSessionModal (modes feature / checkouts principaux) ;
  createGroupFeature/createGroupSession ; badge « groupe · N depots » ;
  sessionsInGroup dedupliquee.
- site: textes EN/FR « une seule session Claude » + diagramme hub.
- tests: claude-launcher, pty-manager (groupe), group-manager ;
  acceptation P5 etendue (session de groupe multi-repo). 272 tests verts.
2026-06-22 10:06:23 +02:00
693e6736de feat(web): lien « Buy me a coffee » (nav, réglages, funding)
- constante BUYMEACOFFEE_URL ; item de nav « Offrez-moi un café »
- section support dans les Réglages + i18n EN/FR
- champ `funding` dans le package.json publié ; mentions README EN/FR
2026-06-18 17:02:30 +02:00
33295a7aaf feat: clean install (Gitea en dur, scan off) + durcissement sécurité entreprise
Gitea
- lien « Code source » hardcodé (REPO_SOURCE_URL) vers le dépôt, toujours visible
- retrait complet du réglage configurable (api.ts, route+store settings, SettingsView, i18n, help, tests)

Découverte des dépôts
- aucune racine de scan par défaut → pas de scan au premier démarrage (clean install)
- corrige la découverte des dépôts à l'INTÉRIEUR d'une racine qui est elle-même un repo
  (depth 0 = conteneur de scan, on descend ; depth > 0 = feuille)

Sécurité « enterprise-deployable »
- en-têtes HTTP durcis (CSP, X-Frame-Options, nosniff, Referrer-Policy, HSTS conditionnel, no-store API), header Server retiré
- permissions DB 0o600 / dossier de données 0o700 ; error handler sanitisé ; garde Content-Type sur les mutations
- chiffrement au repos AES-256-GCM des secrets (server_secret, clé privée VAPID) via SecretBox
- journal d'audit (migration #7) + endpoint /audit-logs + RGPD export/effacement + UI Réglages
- SECURITY.md, docs/ENTERPRISE_DEPLOYMENT.md, sections README EN/FR, SBOM CycloneDX en CI

CI
- pack-smoke packe depuis le contexte du package (cd packages/server) au lieu de « npm pack -w » :
  corrige l'embarquement de @arboretum/shared dans le tarball

Purge des données personnelles du dépôt public
- suppression de spikes/s4-discovery/result.json, reformulation du VERDICT
- chemin de test générique, anonymisation des 5 fixtures de dialogues
2026-06-18 16:44:25 +02:00
fe2a3e66c7 feat: découverte automatique des dépôts git (scan + montrer/cacher)
Scan borné du système de fichiers (racines configurables, défaut home ; profondeur/nombre/timeout bornés ; symlinks non suivis ; exclusions node_modules/dotdirs) qui auto-enregistre les nouveaux dépôts. Insertion atomique ON CONFLICT DO NOTHING (idempotence + anti-résurrection d'un dépôt masqué + anti-course). Scan au démarrage + bouton manuel + re-scan périodique (RepoDiscoveryService, démarré dans runDaemon). Colonne repos.hidden : masquer = conservé en DB mais exclu du dashboard et jamais ré-ajouté ; supprimer = re-découvrable. UI : bouton œil par dépôt + bascule afficher-les-masqués sur le dashboard, section Découverte dans les Réglages (racines + intervalle, allow-list stricte). Robustesse : listAllWorktrees tolère l'échec git par dépôt ; flag --no-discover (escape hatch + hermétisme des acceptations).
2026-06-18 14:45:12 +02:00
c9670ca309 feat: onglets Réglages & Aide + icône Gitea
Réglages : préférences (langue, notifications push + test), gestion complète des tokens d'accès (liste/création/révocation, garde anti lock-out sur le dernier token), URL Gitea configurable, infos serveur en lecture seule (port/bind/origines/VAPID + flags CLI). Aide : documentation bilingue EN/FR de toutes les fonctionnalités, avec recherche. Icône Gitea (lien externe) dans la nav (sidebar + MoreSheet mobile).

Backend : routes /api/v1/auth/tokens (GET/POST/DELETE) + tokenId dans /me ; routes/settings.ts (GET/PATCH, allow-list stricte gitea_url, aucun secret exposé, URL validée http/https anti-XSS) ; AuthService.listTokens/revokeToken (transaction). Front : NavItem gère les liens externes, nav primaire/secondaire, store settings, vues SettingsView/HelpView.

236 tests verts (+15 nouveaux : auth-tokens, settings-routes).
2026-06-18 14:16:53 +02:00
d6c110fc3b feat: refonte UX du dashboard (coquille, tri/filtre/pagination, ⌘K, toasts)
Refonte front (packages/web) : coquille applicative partagée (sidebar desktop +
barre d'onglets mobile + PageHeader, langue/push/logout centralisés) pilotée par
route.meta.layout ; design system (BaseButton/BaseBadge/SegmentedControl/EmptyState/
SkeletonRow, anneau de focus, icônes @lucide/vue) ; tri/filtre/recherche/pagination
côté client via useListControls (+ synchro URL) sur sessions/worktrees/groupes ;
section « À traiter », palette de commandes ⌘K, toasts. Aucune modif serveur/protocole/DB.
2026-06-18 11:47:36 +02:00
e38b3a5d5e feat: groupes de travail (P5)
Ajoute la notion de groupe — collection nommée de repos (membership légère
persistée, many-to-many) — pour piloter plusieurs repos en simultané :

- DB : migration #5 (tables groups + group_repos, FK ON DELETE CASCADE).
- GroupManager (synchrone, EventEmitter) + routes REST /api/v1/groups ;
  ne persiste que la membership, worktrees/sessions filtrés par repoId côté client.
- Protocole : GroupSummary, DTOs, topic WS « groups », events group_update/removed
  (additifs, PROTOCOL_VERSION inchangé).
- Web : store groups, GroupsListView, GroupView (réutilise RepoSection),
  grille multi-terminaux (TerminalGrid/TerminalCell), action « feature cross-repo »
  (orchestration client, tolérante aux échecs partiels), routes + i18n EN/FR.
- Tests : group-manager.test.ts + extension protocol.test.ts ; acceptance-p5.mjs.
2026-06-18 10:03:19 +02:00
3acf27d48f feat(web): sélecteur de répertoire (parcourir au lieu de taper le chemin)
Ajoute un navigateur de dossiers réutilisable sur les deux champs qui
exigeaient un chemin absolu tapé à la main : « Répertoire de travail »
(nouvelle session) et « Chemin du dépôt » (ajout de repo). Un bouton
« Parcourir… » déplie un panneau inline ; le champ texte reste pour
taper/coller un chemin connu. Pour l'ajout de repo, les sous-dossiers
qui sont des dépôts git sont signalés par un badge.

Serveur : nouvel endpoint GET /api/v1/fs/list (authentifié par le hook
preValidation global), ne renvoie que des noms de sous-dossiers (jamais
de contenu de fichier). Valide le chemin via resolve() (clampe à la
racine, neutralise « .. »), masque les dotfiles par défaut (showHidden),
annote les dépôts git (markRepos), tolère les entrées illisibles.

Tests : packages/server/test/fs-routes.test.ts (10 cas — listing, tri,
hidden, markRepos, rejets 400/404, auth 401). Suite : 185/185 verts.
2026-06-17 13:04:10 +02:00
be3a4a0723 init logo 2026-06-17 11:54:56 +02:00
32e6ffba70 P4-C: PWA installable, service worker push & campagne de fiabilité dialogues
- PWA: public/manifest.webmanifest + icon.svg + meta index.html (theme-color, apple-touch-icon)
- public/sw.js: service worker push-only (push → showNotification tag=sessionId ;
  notificationclick → focus/ouvre /sessions/<id>) — pas de précache (hors périmètre)
- lib/push.ts + stores/push.ts: enable/disable, abonnement VAPID, enregistrement SW au boot
- DashboardView: toggle « Notifications » (gardé par pushSupported) + i18n en/fr
- campagne de fiabilité (verdict S3): classification de tous les types de dialogue sur captures
  réelles (trust/permission×2/question) + refus Esc (deny-esc2) + plan (synthétique)
- acceptance-p4.mjs: VAPID/auth/Origin, subscribe idempotent/unsubscribe, answer (rejets)

Choix: SW écrit à la main plutôt que vite-plugin-pwa (qui tirait ~295 paquets Workbox +
2 vulns esbuild high pour un SW push-only). Zéro dépendance front nouvelle. 175 tests verts,
acceptance p1..p4 ALL GREEN.
2026-06-15 12:23:00 +02:00
25d6d09d24 P4-A: commande WS answer — répondre aux dialogues sans clavier
Nouvelle commande WS de haut niveau `answer {channel, action, optionN?}`
traduite côté serveur en keystrokes (chiffre+Entrée / Entrée / Esc) et
validée contre le dialogue courant du tracker (anti-frappe fantôme mobile).

- protocol.ts : message `answer` + validation parseClientMessage + ErrorCode INVALID_ANSWER
- pty-manager.answer() : mappe l'intention en write PTY, réutilise le modèle mono-utilisateur
- gateway : case `answer` (NOT_CONTROLLING / SESSION_EXITED / INVALID_ANSWER)
- web : Attachment.answer(), DialogPrompt.vue (attache interactive légère sink no-op),
  intégré dans SessionView, SessionsListView, WorktreeCard + i18n en/fr
- tests : parseClientMessage (answer valide/malformé + fuzz), pty-manager.answer (163 verts)
2026-06-15 12:05:27 +02:00
d7eb9c1b3a P3-C: badges de session live sur le dashboard (résolus depuis le store sessions)
Les badges busy/waiting/idle des sessions affichées dans les cartes worktree
sont désormais résolus depuis le store des sessions (mis à jour en temps réel),
au lieu de l'instantané embarqué dans le worktree — pour que « quelle session
attend une réponse » reste live sans attendre un worktree_update.
2026-06-12 18:48:07 +02:00
fae288f511 P3-C: dashboard worktree-first + acceptance P3 (MVP complet)
Vue racine consolidée : repos → worktrees avec état git ET état de session
corrélé. Complète le MVP (P2 découverte/reprise + P3-A worktrees + P3-B états fins).

- web/lib/ws-client: abonnement multi-topics (sessions + worktrees), subscribeWorktrees.
- web/stores/worktrees: repos + worktrees, CRUD, temps réel (repo_update/worktree_*).
- web/views/DashboardView (route racine /), components RepoSection + WorktreeCard ;
  SessionStateBadge réutilisé pour l'état des sessions corrélées.
- router: / = dashboard, /sessions = liste à plat (sessions hors worktree), nav croisée.
- i18n EN/FR (dashboard/repos/worktrees).
- fix: @xterm/headless est CommonJS → chargé via createRequire (l'import nommé ESM
  échouait sous Node natif, masqué par esbuild en test) ; détecté par l'acceptation.
- scripts/acceptance-p3.mjs: repo git tmp → enregistrement, worktree + hook, broadcast
  WS worktree_update, corrélation session bash, suppression (409 sans force, 200 avec).

Vérifs : typecheck, 159 tests, build (vue-tsc), acceptations P1/P2/P3 ALL GREEN.
2026-06-12 18:46:45 +02:00
8d963beaac P3-B: claude-adapter — états fins busy/waiting/idle
Détection de l'état fin d'une session claude et du dialogue en cours, pour
savoir « quelle session attend une réponse ». Source primaire = registre
~/.claude/sessions (stable) ; l'écran reconstruit via @xterm/headless TYPE le
dialogue (trust/permission/question/plan) et couvre le cas Trust (avant registre).

- dép: @xterm/headless ^6.0.0 (aligné sur @xterm/xterm du front).
- core/screen-reader.ts: terminal headless persistant par session, alimenté
  par le flux PTY ; snapshotLines() préserve les espaces (bat le strip ANSI naïf).
- core/dialog-classifier.ts: typage pur (texte aplati + regex tolérante inter-
  versions) + extraction des options numérotées (❯ = sélection).
- core/claude-adapter.ts: SessionActivityTracker par session (registre + écran
  → activity + dialog), évaluation débouncée sur output + poll registre léger,
  émission sur changement effectif uniquement.
- shared: SessionSummary += activity?/waitingFor?/dialog? (optionnels, additif).
- pty-manager: tracker instancié pour claude, feed dans handleOutput, summarize
  enrichi, resize propagé, dispose à l'exit.
- web: composant SessionStateBadge (busy/waiting/idle colorés) réutilisable.
- tests: screen-reader + classifier sur fixtures réelles S3 (perm-write/ask/
  trust), claude-adapter (machine à états). 159 verts.
2026-06-12 18:39:38 +02:00
92c48f0978 P2: découverte & reprise des sessions Claude
Arboretum découvre désormais toutes les sessions Claude de la machine
(scan ~/.claude/projects + registre ~/.claude/sessions), distingue
vivantes/mortes par pid+procStart, et permet de reprendre une morte
(--resume dans son cwd d'origine) ou forker une vivante sans la corrompre.

- shared: SessionSummary enrichi (source, claudeSessionId, pid, resumable,
  attachable, registryStatus) — additif, PROTOCOL_VERSION inchangé ;
  types REST resume/fork.
- db: migration id:2 (claude_session_id, resumed_from).
- core: jsonl-discovery (parseur tolérant, scan asynchrone non bloquant),
  session-registry (vivacité pid+procStart), discovery-service (cache +
  refresh périodique + diff/broadcast), pty-manager (resume/fork + capture
  du claudeSessionId via le registre).
- routes: /sessions/:id/resume (garde-fou 409 anti-corruption sur session
  vivante) et /fork ; GET fusionné managées + découvertes ; relais WS.
- web: badges managed/discovered + busy/idle/waiting, actions conditionnelles
  (Open/Observe/Kill vs Fork/View vs Resume/Fork), vue read-only des sessions
  externes, i18n EN/FR.
- tests: jsonl-discovery, session-registry, discovery-service + resume/fork
  (130 verts) ; acceptation E2E acceptance-p2.mjs (sans quota) ALL GREEN.

Conforme aux verdicts S1 (resume dans cwd d'origine, vivacité pid+procStart)
et S4 (munge cwd, parseur tête+queue, priorité de titre).
2026-06-12 18:20:21 +02:00
e9404cb567 P1 complete: web front, test suite, CI — acceptance ALL GREEN
Fan-out integration + fixes found by the test/acceptance pass:
- FIX ring-buffer: chunks >= capacity skipped bytes now count into the
  monotonic offset (invariant: stream byte k lives at k % capacity) —
  window order was corrupted on unaligned big chunks
- FIX auth: non-numeric cookie expiry no longer bypasses expiration
- FIX protocol: safe-integer validation on ack.bytes / hello.protocol
- FIX @fastify/websocket v11: websocket route must be registered in an
  encapsulated context after plugin load (handler got REST signature)
- FIX flow-control deadlock found by e2e acceptance: client only ACKs
  on data receipt, so pausing with an unACKed residue in (LOW,
  ACK_EVERY] stalled both sides at 0.9 MB. ACK_EVERY now 64 KiB (<=
  LOW invariant, tested) + trailing debounced ACK in the web client
- Web: Vue 3 + Vite + Pinia + Tailwind 4 + vue-i18n (EN/FR) + xterm 6
  (fit + webgl fallback), multiplexed ws-client with reconnect/backoff
  and resync epochs
- Tests: 100 vitest (protocol fuzz, ring edges, auth, pty-manager flow
  control with mocked pty, REST e2e) ; CI Node 22/24 + pack-smoke
- scripts/acceptance-p1.mjs: real daemon + real WS client — boot,
  login, attach, stdin, 10 MB flood w/ ACK (13.7 MB/1.9s, RSS bounded),
  brutal disconnect + replay resync, kill broadcast, SIGTERM drain
2026-06-11 22:29:58 +02:00