65ef616867
chore(typo): retire tous les tirets cadratins/demi-cadratins + garde CI
...
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
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é
...
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
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
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
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
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
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