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
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).
This commit is contained in:
@@ -3,18 +3,18 @@
|
||||
## 0.2.0
|
||||
|
||||
Follows the daemon's "worktree IDE" milestone (P7→P12), while keeping the extension a lightweight
|
||||
**visual worktree manager** — no editor/diff duplicated here, that lives in the web IDE:
|
||||
**visual worktree manager**: no editor/diff duplicated here, that lives in the web IDE:
|
||||
|
||||
- **Detailed git status** in the worktree tree: staged / unstaged / conflict counts and the last commit
|
||||
subject (from the daemon's enriched `WorktreeGitStatus`).
|
||||
- **Open Worktree IDE** command — deep-links to the web `/workspace/:repoId/:wt` view.
|
||||
- **Open Worktree IDE** command: deep-links to the web `/workspace/:repoId/:wt` view.
|
||||
- **Fetch** and **Pull** (fast-forward only or rebase) commands on worktrees, alongside commit/push.
|
||||
- **Archived sessions**: the `session_archived` event is handled live; finished, auto-archived sessions
|
||||
are badged and hidden by default, with a new `arboretum.showArchivedSessions` setting to reveal them.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
Initial release — native VS Code integration for the Arboretum daemon:
|
||||
Initial release: native VS Code integration for the Arboretum daemon:
|
||||
|
||||
- Real-time **Repositories** and **Groups** trees (repos → worktrees → sessions) over the daemon WebSocket.
|
||||
- **Native terminals** (`Pseudoterminal`) to attach/observe any session, reusing VS Code's rendering and
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
# Arboretum for VS Code
|
||||
|
||||
Pilot your git **worktrees** and **Claude Code sessions** from inside VS Code — a real native
|
||||
Pilot your git **worktrees** and **Claude Code sessions** from inside VS Code: a real native
|
||||
integration on top of the [Arboretum](https://git.lidge.fr/johanleroy/arboretum) daemon, not a webview.
|
||||
|
||||
- **Live tree** of Repositories → Worktrees → Sessions (and a Groups view), updated in real time over
|
||||
the daemon's WebSocket.
|
||||
- **Native terminals**: attach to any session in a real VS Code terminal (a `Pseudoterminal` bridges the
|
||||
daemon's PTY) — you get VS Code's own rendering, scrollback, copy/paste and links for free.
|
||||
daemon's PTY): you get VS Code's own rendering, scrollback, copy/paste and links for free.
|
||||
- **Waiting alerts**: a status-bar counter and native notifications when a Claude session is waiting for
|
||||
input — answer **Yes/No** without even opening the terminal (uses the daemon's `answer` command).
|
||||
- **Git mutations** from the tree: create worktree, commit, push, fetch, pull, promote to main — with a
|
||||
input: answer **Yes/No** without even opening the terminal (uses the daemon's `answer` command).
|
||||
- **Git mutations** from the tree: create worktree, commit, push, fetch, pull, promote to main, with a
|
||||
**detailed git status** on each worktree (staged / unstaged / conflicts and the last commit subject).
|
||||
- **Open Worktree IDE**: jump from any worktree to its full `/workspace` view (file tree, Monaco editor,
|
||||
inline diffs, integrated terminal) in the browser. The extension stays a lightweight visual worktree
|
||||
manager — the heavy editing lives in the web IDE.
|
||||
manager: the heavy editing lives in the web IDE.
|
||||
- **Workspace-aware**: the worktree matching your open folder is highlighted; start a session or create a
|
||||
worktree for the current folder in one command.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A running Arboretum daemon (`npx @johanleroy/git-arboretum`, or installed as a user service via
|
||||
`arboretum install`). The extension is a **client** — it does not start the daemon.
|
||||
`arboretum install`). The extension is a **client**: it does not start the daemon.
|
||||
- An access token. The bootstrap token is printed once on first daemon start; you can also create one in
|
||||
the Arboretum dashboard (**Settings → Tokens**).
|
||||
|
||||
@@ -41,7 +41,7 @@ integration on top of the [Arboretum](https://git.lidge.fr/johanleroy/arboretum)
|
||||
| `arboretum.notifyOnWaiting` | `true` | Native notification when a session starts waiting for input. |
|
||||
|
||||
The extension authenticates with `Authorization: Bearer <token>` on both REST and the WebSocket upgrade.
|
||||
For remote access, point `arboretum.url` at your Tailscale Serve URL (`https://…`) — the WebSocket is
|
||||
For remote access, point `arboretum.url` at your Tailscale Serve URL (`https://…`): the WebSocket is
|
||||
derived automatically (`wss://`).
|
||||
|
||||
## Building & packaging (private VSIX)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "git-arboretum",
|
||||
"displayName": "Arboretum",
|
||||
"description": "Pilot your git worktrees and Claude Code sessions from VS Code — native terminals, live tree, waiting alerts.",
|
||||
"description": "Pilot your git worktrees and Claude Code sessions from VS Code: native terminals, live tree, waiting alerts.",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"publisher": "johanleroy",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Client REST typé du daemon Arboretum — module pur (pas d'import vscode), testable.
|
||||
// Client REST typé du daemon Arboretum : module pur (pas d'import vscode), testable.
|
||||
// Auth par en-tête `Authorization: Bearer <token>` (le hook preValidation du serveur l'accepte
|
||||
// au même titre que le cookie). Un client Node n'envoie pas d'en-tête Origin → il passe le check
|
||||
// Origin strict côté serveur. Réutilise les types de `@arboretum/shared`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Client WebSocket multiplexé Node — portage du client web (packages/web/src/lib/ws-client.ts).
|
||||
// Client WebSocket multiplexé Node : portage du client web (packages/web/src/lib/ws-client.ts).
|
||||
// UNE connexion, plusieurs canaux ; handshake hello/hello_ok, sub des topics, corrélation FIFO des
|
||||
// 'attached', flow control par ACK (cumul d'octets PAR CANAL, remis à 0 à chaque RESYNC), reconnexion
|
||||
// avec backoff. Différences vs web :
|
||||
@@ -39,7 +39,7 @@ export interface AttachOptions {
|
||||
sink: AttachmentSink;
|
||||
}
|
||||
|
||||
/** Transport minimal — abstrait pour les tests ; l'impl par défaut enveloppe `ws`. */
|
||||
/** Transport minimal : abstrait pour les tests ; l'impl par défaut enveloppe `ws`. */
|
||||
export interface RawSocket {
|
||||
send(data: string): void;
|
||||
close(): void;
|
||||
@@ -404,7 +404,7 @@ export class ArbWsClient {
|
||||
return;
|
||||
case 'error': {
|
||||
if (msg.channel !== undefined) {
|
||||
this.opts.log?.(`channel ${msg.channel}: ${msg.code} — ${msg.message}`);
|
||||
this.opts.log?.(`channel ${msg.channel}: ${msg.code}: ${msg.message}`);
|
||||
return;
|
||||
}
|
||||
if ((msg.code === 'NOT_FOUND' || msg.code === 'SESSION_EXITED') && this.awaitingAttached.length > 0) {
|
||||
@@ -420,7 +420,7 @@ export class ArbWsClient {
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.opts.log?.(`${msg.code} — ${msg.message}`);
|
||||
this.opts.log?.(`${msg.code}: ${msg.message}`);
|
||||
return;
|
||||
}
|
||||
case 'pong':
|
||||
|
||||
@@ -42,7 +42,7 @@ async function run(label: string, fn: () => Promise<void>): Promise<void> {
|
||||
await fn();
|
||||
} catch (err) {
|
||||
const msg = err instanceof RestError ? `${err.code}: ${err.message}` : (err as Error).message;
|
||||
void vscode.window.showErrorMessage(`Arboretum — ${label}: ${msg}`);
|
||||
void vscode.window.showErrorMessage(`Arboretum · ${label}: ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ async function promoteWorktreeFlow(deps: CommandDeps, repoId: string, worktree:
|
||||
return;
|
||||
}
|
||||
const msg = err instanceof RestError ? `${err.code}: ${err.message}` : (err as Error).message;
|
||||
void vscode.window.showErrorMessage(`Arboretum — promote: ${msg}`);
|
||||
void vscode.window.showErrorMessage(`Arboretum · promote: ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ async function startGroupSessionFlow(deps: CommandDeps, group: GroupSummary): Pr
|
||||
deps.terminals.open(res.session, 'interactive');
|
||||
if (res.skipped.length > 0) {
|
||||
void vscode.window.showWarningMessage(
|
||||
`Arboretum: ${res.skipped.length} repo(s) skipped — ${res.skipped.map((s) => s.reason).join('; ')}`,
|
||||
`Arboretum: ${res.skipped.length} repo(s) skipped: ${res.skipped.map((s) => s.reason).join('; ')}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Lecture des réglages `arboretum.*` — module pur (aucun import vscode) pour rester testable.
|
||||
// Lecture des réglages `arboretum.*` : module pur (aucun import vscode) pour rester testable.
|
||||
// L'extension fournit un `ConfigSnapshot` lu depuis workspace.getConfiguration ; ce module
|
||||
// normalise/dérive les valeurs (notamment l'URL WebSocket à partir de l'URL HTTP).
|
||||
|
||||
@@ -22,7 +22,7 @@ export function wsUrlFromBase(baseUrl: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit l'URL de la vue IDE web d'un worktree — deep-link vers la route `/workspace/:repoId/:wt`
|
||||
* Construit l'URL de la vue IDE web d'un worktree : deep-link vers la route `/workspace/:repoId/:wt`
|
||||
* (le routeur web est en history mode). `repoId` et le chemin du worktree sont encodés par segment.
|
||||
*/
|
||||
export function workspaceUrl(baseUrl: string, repoId: string, worktreePath: string): string {
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
||||
try {
|
||||
await probe.me();
|
||||
} catch (err) {
|
||||
void vscode.window.showErrorMessage(`Arboretum: sign in failed — ${(err as Error).message}`);
|
||||
void vscode.window.showErrorMessage(`Arboretum: sign in failed: ${(err as Error).message}`);
|
||||
return;
|
||||
}
|
||||
await auth.storeToken(token);
|
||||
|
||||
@@ -41,7 +41,7 @@ export class WaitingNotifier implements vscode.Disposable {
|
||||
|
||||
private notify(session: SessionSummary): void {
|
||||
const title = session.title?.trim() || session.command;
|
||||
const detail = session.waitingFor ? ` — ${session.waitingFor}` : '';
|
||||
const detail = session.waitingFor ? ` · ${session.waitingFor}` : '';
|
||||
void vscode.window
|
||||
.showInformationMessage(`Arboretum: "${title}" is waiting for input${detail}`, 'Open Terminal', 'Yes', 'No')
|
||||
.then((choice) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// État mémoire de l'extension — module pur (aucun import vscode), testable.
|
||||
// État mémoire de l'extension : module pur (aucun import vscode), testable.
|
||||
// Seedé via REST au (re)connect, puis patché par les events WS. Émet onDidChange à chaque mutation ;
|
||||
// les arbres / status bar / notifications s'y abonnent. Principe « groupe léger » : on ne corrèle pas
|
||||
// les worktrees aux sessions via la liste embarquée (potentiellement périmée) mais par `cwd`, exactement
|
||||
@@ -194,7 +194,7 @@ export class Store {
|
||||
.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
||||
}
|
||||
|
||||
/** sessions en attente d'input (activity === 'waiting') — pour la status bar et les notifications. */
|
||||
/** sessions en attente d'input (activity === 'waiting') : pour la status bar et les notifications. */
|
||||
waitingSessions(): SessionSummary[] {
|
||||
return [...this.sessions.values()].filter((s) => s.live && s.activity === 'waiting');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Pont d'une session vers un terminal — module pur (aucun import vscode), testable.
|
||||
// Pont d'une session vers un terminal : module pur (aucun import vscode), testable.
|
||||
// Le ws-client remet des octets BRUTS (un chunk PTY peut couper un caractère UTF-8 en frontière de
|
||||
// frame, exactement comme côté web où xterm.write(Uint8Array) gérait le décodage). Ici la cible est
|
||||
// un Pseudoterminal qui veut des `string` → on décode en streaming avec un TextDecoder, et on recrée
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Adaptateur vscode.Pseudoterminal : expose une session Arboretum comme un terminal NATIF de VS Code
|
||||
// (rendu, scrollback, copier/coller, liens gratuits). C'est la pièce maîtresse de l'intégration —
|
||||
// (rendu, scrollback, copier/coller, liens gratuits). C'est la pièce maîtresse de l'intégration :
|
||||
// pas de xterm-in-webview. Le pont WS/décodage vit dans SessionBridge (pur) ; cet adaptateur ne fait
|
||||
// que relier les events Pseudoterminal ↔ bridge.
|
||||
import * as vscode from 'vscode';
|
||||
@@ -45,13 +45,13 @@ export class SessionTerminalManager {
|
||||
},
|
||||
onControlChanged: (controlling) => {
|
||||
if (!controlling && mode === 'interactive') {
|
||||
writeEmitter.fire('\r\n\x1b[33m[arboretum: control lost — now observing]\x1b[0m\r\n');
|
||||
writeEmitter.fire('\r\n\x1b[33m[arboretum: control lost: now observing]\x1b[0m\r\n');
|
||||
}
|
||||
},
|
||||
});
|
||||
this.bridges.set(session.id, bridge);
|
||||
bridge.open(dims?.columns ?? 80, dims?.rows ?? 24).catch((err: Error) => {
|
||||
writeEmitter.fire(`\r\n\x1b[31m[arboretum: cannot attach — ${err.message}]\x1b[0m\r\n`);
|
||||
writeEmitter.fire(`\r\n\x1b[31m[arboretum: cannot attach: ${err.message}]\x1b[0m\r\n`);
|
||||
closeEmitter.fire();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -94,12 +94,12 @@ function sessionDescription(s: SessionSummary): string {
|
||||
if (s.archived) parts.push('archived');
|
||||
if (s.source === 'discovered') parts.push('external');
|
||||
if (s.groupId) parts.push('group');
|
||||
if (s.waitingFor) parts.push(`— ${s.waitingFor}`);
|
||||
if (s.waitingFor) parts.push(`· ${s.waitingFor}`);
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
function sessionTooltip(s: SessionSummary): string {
|
||||
const lines = [`${s.command} — ${s.cwd}`, `status: ${s.status}${s.live ? ' (live)' : ''}`];
|
||||
const lines = [`${s.command} · ${s.cwd}`, `status: ${s.status}${s.live ? ' (live)' : ''}`];
|
||||
if (s.activity) lines.push(`activity: ${s.activity}`);
|
||||
if (s.addedDirs && s.addedDirs.length > 0) lines.push(`added dirs: ${s.addedDirs.length}`);
|
||||
return lines.join('\n');
|
||||
|
||||
Reference in New Issue
Block a user