Files
arboretum/spikes/s5-vscode/STUDY.md
Johan LEROY cf7eb05aca feat(vscode): extension VS Code native (intégration native, pas un webview)
Nouveau workspace packages/vscode (git-arboretum, privé, non publié sur npm), client REST/WS
réutilisant @arboretum/shared. Auth Authorization: Bearer sur REST et l'upgrade WS (via `ws`) ;
un client Node sans en-tête Origin passe le check Origin strict du serveur.

- Arbres temps réel : Repositories (repos → worktrees → sessions) et Groups, via le WebSocket.
- Terminaux natifs (vscode.Pseudoterminal) pour attacher/observer une session — rendu et
  scrollback de VS Code ; décodage UTF-8 streaming + comptabilité ACK dans des modules purs.
- Status bar (compteur waiting) + notifications natives sur passage en waiting, réponses Yes/No
  via la commande WS answer.
- Mutations git (create worktree, commit, push, promote), start/kill/hide/resume/fork, session
  de groupe ; conscience du workspace (reveal + start/create here).
- Bundle esbuild (format cjs, external vscode) inlinant @arboretum/shared → VSIX autonome.
  Logique réutilisable sans import vscode → testée par vitest (19 tests).
- CI : .gitea/workflows/vscode-release.yml package le VSIX sur tag vscode-vX.Y.Z (artefact +
  asset de release best-effort). build:vscode hors du build principal (comme le site).
- spikes/s5-vscode/STUDY.md : décision de conception (GO phasé A→D), marquée implémentée.
2026-06-23 17:43:03 +02:00

8.3 KiB

Spike S5 — Extension VSCode (intégration native) — ÉTUDE : GO (phasé)

IMPLÉMENTÉ dans packages/vscode (git-arboretum, privé) — phases A→D livrées : connexion + auth SecretStorage, arbres temps réel (Repos/Groups), terminaux natifs Pseudoterminal, status bar + notifications waiting (commande answer), mutations git, conscience du workspace. Bundle esbuild (shared inliné), packagé en VSIX (npm run build:vscode puis vsce package --no-dependencies). CI : .gitea/workflows/vscode-release.yml sur tag vscode-vX.Y.Z. L'étude ci-dessous reste la justification de conception.

Étude de conception (pas de mesure Go/No-Go : c'est un design + plan d'implémentation phasé). Objectif validé avec l'utilisateur : une vraie intégration native dans VSCode, pas un simple webview — une connexion réelle avec ce qui se passe dans l'éditeur (terminaux natifs, arbre, statut, notifications, conscience du workspace).

Contexte & contrainte

Arboretum est déjà un daemon qui sert une SPA et expose :

  • une API REST (/api/v1/...) : repos, worktrees (+ mutations git commit/push/promote), sessions, groupes, auth ;
  • un protocole WebSocket multiplexé (@arboretum/sharedprotocol.ts) : attach/stdin/resize, frames binaires [type u8][channel u32le][payload], flow-control par watermarks (FLOW), events temps réel sessions/worktrees/groups, commande answer (dialogues), hello/hello_ok.

Un plugin n'a donc rien à réimplémenter côté logique : il est un client de cette API. La question n'est pas « comment afficher le dashboard » mais « comment exposer Arboretum avec les primitives natives de VSCode ». Le webview (iframe du dashboard) reste un fallback optionnel ; ce n'est pas le cœur.

Pourquoi pas « juste un webview »

  • Un webview = iframe vers http://127.0.0.1:7317 : lourd, isolé (CSP stricte, acquireVsCodeApi indisponible dans l'iframe), pas d'intégration avec les terminaux/arbres/statut de VSCode, auth à re-bricoler (cookie/token dans une iframe cross-origin). On retrouve les bugs xterm-in-webview (rendu, scrollback) qu'on cherche justement à éviter.
  • Une intégration native réutilise le rendu terminal, le scrollback, le copier/coller, les liens de VSCode (gratuitement robustes), et place Arboretum là où l'utilisateur travaille déjà.

Architecture proposée — packages/vscode

Nouveau workspace privé @arboretum/vscode, extension TypeScript, packagée en VSIX via vsce (build esbuild). Le @arboretum/shared est résolu en dev par le symlink workspace et inliné au packaging (même principe que scripts/inline-shared.mjs côté serveur) → VSIX autonome.

Connexion au daemon

  • Réglages : arboretum.url (défaut http://127.0.0.1:7317), arboretum.token.
  • Auth : commande Arboretum: Sign in qui demande le token et le stocke dans VS Code SecretStorage (les tokens daemon sont stockés hashés et le bootstrap ne s'affiche qu'une fois → impossible de « lire » le token existant ; on le saisit une fois). Option avancée : commande qui spawn le daemon (npx @johanleroy/git-arboretum) et capture le bootstrap token au premier démarrage.
  • Le check Origin strict du daemon ne s'applique qu'aux navigateurs (en-tête Origin) : un client Node/extension n'envoie pas d'Origin → OK ; l'auth par token (en-tête/cookie) reste requise.

Réutilisation du protocole (le « vrai » lien)

Importer @arboretum/shared : decodeBinaryFrame/encodage, BINARY_FRAME, FLOW, PROTOCOL_VERSION, types de messages et SessionSummary/WorktreeSummary/GroupSummary. Le pont terminal et la liste restent synchronisés avec le serveur par construction.

Surfaces natives

  1. Activity Bar + TreeView(s) (vscode.window.createTreeView) : arbre Repos → Worktrees → Sessions, avec badges d'état fins (activity busy/waiting/idle, dialog) déjà portés par SessionSummary. Une vue Groupes en parallèle. Rafraîchi en temps réel par les events WS (onDidChangeTreeData).

  2. Terminaux VSCode natifs via Pseudoterminal (pièce maîtresse) : vscode.window.createTerminal({ name, pty }) où le Pseudoterminal ponte la session Arboretum :

    • open()wsClient.attach(sessionId) ;
    • OUTPUT WS (frames binaires) → writeEmitter.fire(text) (onDidWrite) ;
    • handleInput(data) → WS stdin ;
    • setDimensions({columns, rows}) → WS resize ;
    • close()detach. On réutilise le renderer + scrollback natifs de VSCode : règle au passage, côté VSCode, les problèmes de rendu et d'historique de la SPA. Flow-control : réutiliser FLOW/les ACK du protocole (envoyer un ACK des octets écrits) pour ne pas saturer.
  3. Status bar (createStatusBarItem) : compteur des sessions waiting (à traiter) ; clic → focus de l'arbre / quick-pick des sessions en attente.

  4. Notifications natives : sur transition d'une session vers waiting (event WS), showInformation Message avec actions (« Ouvrir le terminal », « Oui »/« Non » → commande WS answer). Doublonne utilement le Web Push : ici, natif dans l'éditeur.

  5. Conscience du workspace : mapper workspace.workspaceFolders → repo/worktree Arboretum (par chemin). Actions contextuelles : « Démarrer une session Claude ici », « Créer un worktree pour ce repo », et les mutations git existantes (commit/push/promote via REST). Met aussi en évidence dans l'arbre le worktree correspondant au dossier ouvert.

  6. Commandes (contributes.commands + palette VSCode) : sign in, ouvrir le dashboard (webview optionnel), rafraîchir, attacher/observer une session, créer worktree, commit/push/promote, start/hide session, répondre à un dialogue.

  7. Webview : seulement en option (vue dashboard complète pour qui la veut). UX primaire = native.

Découpage phasé (livrable séparé — non implémenté dans ce lot)

  • A — Lecture & connexion : auth (SecretStorage) + client REST/WS + TreeView lecture seule (repos/worktrees/sessions/groupes) en temps réel. Valeur immédiate, risque faible.
  • B — Terminaux natifs : Pseudoterminal attach/observe via pont WS réutilisant @arboretum/shared (flow-control inclus). Le cœur de la valeur ; à faire juste après A.
  • C — Actions & supervision : mutations (créer worktree, commit/push/promote, start/hide), status bar waiting, notifications + commande answer.
  • D — Workspace-aware : mapping dossier ↔ repo/worktree + actions contextuelles.

Intégration monorepo & packaging

  • packages/vscode/ : package.json d'extension (engines.vscode, activationEvents, contributes.{commands,views,viewsContainers,configuration}), build esbuild (bundle Node), test via @vscode/test-electron (optionnel). Publication : VSIX (vsce package) — le registre privé Gitea ne sert pas d'extensions VSCode, donc distribution par VSIX (et/ou Open VSX) plutôt que le Marketplace si l'on veut rester privé.
  • Le tsconfig reste hors du tsc -b server/shared actuel (build séparé) pour ne pas alourdir le typecheck CI existant.

Risques / questions ouvertes

  • Auth bootstrap : saisie manuelle du token vs spawn+capture. MVP = saisie manuelle (SecretStorage).
  • Flow-control côté extension : reprendre FLOW (ACK par octets) pour les terminaux natifs ; un Pseudoterminal n'expose pas de back-pressure → ACK « ce qui a été émis dans onDidWrite ».
  • Remote/SSH/Codespaces : si l'on veut le webview fallback, prévoir vscode.env.asExternalUri + portMapping. Les surfaces natives (REST/WS via arboretum.url) restent valides tant que le daemon est joignable depuis l'hôte de l'extension.
  • Multi-fenêtres VSCode : une connexion WS par fenêtre (acceptable, outil mono-utilisateur).

Verdict

GO sur une extension native packages/vscode, en commençant par A → B (TreeView temps réel + terminaux natifs Pseudoterminal), qui apportent l'essentiel de la valeur et réutilisent directement l'API REST + le protocole WS de @arboretum/shared. Le webview n'est qu'un fallback optionnel. Références API : Webview, Tree View, Pseudoterminal/createTerminal, StatusBarItem, SecretStorage, asExternalUri (docs officielles VSCode).