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:
@@ -1,4 +1,4 @@
|
||||
# Spike S1 — resume / fork / vivacité — VERDICT : ✅ GO
|
||||
# Spike S1 · resume / fork / vivacité · VERDICT : ✅ GO
|
||||
|
||||
Exécuté le 2026-06-11 (CLI claude 2.1.173, repos jetables /tmp/spike-s1-*). Scripts : `harness.mjs` + `run.mjs`, captures brutes et `results.json` dans `captures/` (non versionnés).
|
||||
|
||||
@@ -9,7 +9,7 @@ Exécuté le 2026-06-11 (CLI claude 2.1.173, repos jetables /tmp/spike-s1-*). Sc
|
||||
| 1 | `claude --resume <id>` pendant que la session est **vivante** | **Aucun verrou, aucun avertissement.** B s'ouvre sur le même sessionId, les deux process s'inscrivent au registre avec le même sessionId, et les messages des deux TUI **s'entrelacent dans le même transcript** (ALPHA/BRAVO/CHARLIE dans un seul fichier). Corruption logique confirmée → le daemon DOIT détecter la vivacité avant tout resume. |
|
||||
| 2 | `--resume <id> --fork-session` sur session vivante | **Sûr.** Nouveau sessionId, nouveau fichier contenant la copie de l'historique + ses propres messages ; l'original n'est **pas** pollué. C'est l'opération à proposer pour une session vivante (« Dupliquer »). |
|
||||
| 3 | kill -9 puis détection | Le fichier registre **reste** (stale) immédiatement après ; `pid` mort + `procStart` discordant le détectent à coup sûr. `--resume` après mort : même sessionId, continuation propre dans le même fichier, 0 parentUuid cassé. |
|
||||
| 4 | resume depuis un **autre cwd** | **Échec explicite** : « No conversation found with session ID » — le CLI cherche la session dans le dossier projet dérivé du cwd **courant**. → Le daemon doit toujours lancer `--resume` dans le cwd d'origine de la session (champ `cwd` du JSONL), comme prévu au design. |
|
||||
| 4 | resume depuis un **autre cwd** | **Échec explicite** : « No conversation found with session ID » : le CLI cherche la session dans le dossier projet dérivé du cwd **courant**. → Le daemon doit toujours lancer `--resume` dans le cwd d'origine de la session (champ `cwd` du JSONL), comme prévu au design. |
|
||||
| 5 | sortie gracieuse (`/exit`) | Exit code 0, fichier registre **nettoyé**. |
|
||||
| 6 | SIGTERM | Fichier registre **nettoyé** aussi (handler du CLI). |
|
||||
| 7 | GC des stale | Le fichier stale du kill -9 a été nettoyé plus tard par une autre instance du CLI → **ne jamais raisonner sur la présence/absence du fichier**, toujours valider pid+procStart à la lecture. |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Spike S1 — harnais commun : spawn de claude dans un PTY + observation registre/JSONL
|
||||
// Spike S1 · harnais commun : spawn de claude dans un PTY + observation registre/JSONL
|
||||
import ptyMod from '@homebridge/node-pty-prebuilt-multiarch';
|
||||
import { readdirSync, readFileSync, existsSync, mkdirSync, appendFileSync, statSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
@@ -58,7 +58,7 @@ export class ClaudeSession {
|
||||
if (v) { log(`${this.label}: ${desc}`, { afterMs: Date.now() - t0 }); return v; }
|
||||
await sleep(everyMs);
|
||||
}
|
||||
log(`${this.label}: TIMEOUT — ${desc}`, { timeoutMs, screenTail: this.tail(600) });
|
||||
log(`${this.label}: TIMEOUT : ${desc}`, { timeoutMs, screenTail: this.tail(600) });
|
||||
throw new Error(`timeout: ${this.label} ${desc}`);
|
||||
}
|
||||
// Démarrage : gère le trust dialog (précède l'inscription au registre) puis attend le registre.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// Spike S1 — resume/fork/vivacité. Scénarios :
|
||||
// Spike S1 · resume/fork/vivacité. Scénarios :
|
||||
// 1. session A vivante → claude --resume <id> en B : comportement (interleave ?)
|
||||
// 2. --fork-session sur session vivante : nouveau fichier, original intact
|
||||
// 3. kill -9 : fichier registre stale détectable (pid/procStart), resume sûr ensuite
|
||||
@@ -108,7 +108,7 @@ try {
|
||||
};
|
||||
save();
|
||||
|
||||
// ---------- Scénario 5 : sortie gracieuse — nettoyage du registre ? ----------
|
||||
// ---------- Scénario 5 : sortie gracieuse, nettoyage du registre ? ----------
|
||||
log('=== S1.5 sortie gracieuse de D (/exit) ===');
|
||||
await D.type('/exit');
|
||||
await sleep(4000);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Spike S3 — dialogues TUI, frappes, flood — VERDICT : ✅ GO (partiel documenté)
|
||||
# Spike S3 · dialogues TUI, frappes, flood · VERDICT : ✅ GO (partiel documenté)
|
||||
|
||||
Exécuté le 2026-06-11 (CLI 2.1.173). Scripts : `run.mjs` (passe 1), `run2.mjs` (passe 2), `flood.mjs`. Résultats bruts dans `captures/`.
|
||||
|
||||
@@ -6,21 +6,21 @@ Exécuté le 2026-06-11 (CLI 2.1.173). Scripts : `run.mjs` (passe 1), `run2.mjs`
|
||||
|
||||
| Dialogue | Détection | Options extraites | Réponse validée |
|
||||
|---|---|---|---|
|
||||
| **Trust dialog** (nouveau dossier) | écran seul (précède le registre) — motif `trust` + `❯` sur texte aplati | `❯1. Yes, I trust this folder / 2. No, exit` — « Enter to confirm · Esc to cancel » | **Entrée** ✅ (10+ occurrences via S1/S3) |
|
||||
| **Permission Write** (création/édition de fichier) | registre `status: waiting`, `waitingFor: "permission prompt"` + écran | `❯1. Yes / 2. Yes, allow all edits during this session (shift+tab) / 3. No` — « Esc to cancel · Tab to amend » | chiffre seul **insuffisant** ; **chiffre puis Entrée** ✅ (fichier créé) |
|
||||
| **AskUserQuestion** | registre `waiting` (même `waitingFor: "permission prompt"` — le registre ne distingue pas les types) | `❯1. Rouge / 2. Bleu / 3. Type something. / 4. Chat about this` — « Enter to select · ↑/↓ to navigate · Esc to cancel » | **↓ + Entrée** ✅ (tour complété, bonne option choisie) |
|
||||
| **Permission Bash** | **non déclenchée** : `echo` est dans l'allowlist par défaut ET `node -e` s'exécute dans le **bash sandboxé** du CLI 2.x sans prompt | — | — |
|
||||
| **Refus par Esc** | non exercé (quota) ; « Esc to cancel » affiché sur les deux dialogues observés | — | à valider en P4 |
|
||||
| **Trust dialog** (nouveau dossier) | écran seul (précède le registre) : motif `trust` + `❯` sur texte aplati | `❯1. Yes, I trust this folder / 2. No, exit` : « Enter to confirm · Esc to cancel » | **Entrée** ✅ (10+ occurrences via S1/S3) |
|
||||
| **Permission Write** (création/édition de fichier) | registre `status: waiting`, `waitingFor: "permission prompt"` + écran | `❯1. Yes / 2. Yes, allow all edits during this session (shift+tab) / 3. No` : « Esc to cancel · Tab to amend » | chiffre seul **insuffisant** ; **chiffre puis Entrée** ✅ (fichier créé) |
|
||||
| **AskUserQuestion** | registre `waiting` (même `waitingFor: "permission prompt"`, le registre ne distingue pas les types) | `❯1. Rouge / 2. Bleu / 3. Type something. / 4. Chat about this` : « Enter to select · ↑/↓ to navigate · Esc to cancel » | **↓ + Entrée** ✅ (tour complété, bonne option choisie) |
|
||||
| **Permission Bash** | **non déclenchée** : `echo` est dans l'allowlist par défaut ET `node -e` s'exécute dans le **bash sandboxé** du CLI 2.x sans prompt | - | - |
|
||||
| **Refus par Esc** | non exercé (quota) ; « Esc to cancel » affiché sur les deux dialogues observés | - | à valider en P4 |
|
||||
|
||||
## Enseignements structurants
|
||||
|
||||
1. **Le protocole de réponse est : (chiffre pour positionner) + Entrée pour confirmer.** Entrée seule valide l'option pré-sélectionnée (`❯`). Flèches ↑/↓ + Entrée fonctionnent partout. → L'UI mobile enverra `<chiffre>` + `\r`.
|
||||
2. **Le registre `~/.claude/sessions` détecte les états d'attente des dialogues outils** (permission ET AskUserQuestion) avec le même `waitingFor: "permission prompt"` → le typage fin du dialogue (question vs permission, options) exige la lecture d'écran (`@xterm/headless` en prod ; le strip ANSI naïf mange les espaces — voir VERDICT S1).
|
||||
2. **Le registre `~/.claude/sessions` détecte les états d'attente des dialogues outils** (permission ET AskUserQuestion) avec le même `waitingFor: "permission prompt"` → le typage fin du dialogue (question vs permission, options) exige la lecture d'écran (`@xterm/headless` en prod ; le strip ANSI naïf mange les espaces, voir VERDICT S1).
|
||||
3. **Il y a moins de prompts de permission que prévu** : le bash sandboxé exécute sans prompt les commandes sans réseau/écriture hors workspace. Les vrais `waiting` viennent des éditions de fichiers, du réseau, d'AskUserQuestion et des plans. Bonne nouvelle pour l'UX (moins d'interruptions), à intégrer aux fixtures P4.
|
||||
4. **Pré-trust programmatique plausible** : accepter le dialogue écrit `projects["<dir>"].hasTrustDialogAccepted: true` dans `~/.claude.json` (structure complète capturée). À la création d'un worktree, écrire cette entrée (avec consentement explicite dans la modal) éviterait le dialogue — à valider en P2/P3.
|
||||
4. **Pré-trust programmatique plausible** : accepter le dialogue écrit `projects["<dir>"].hasTrustDialogAccepted: true` dans `~/.claude.json` (structure complète capturée). À la création d'un worktree, écrire cette entrée (avec consentement explicite dans la modal) éviterait le dialogue, à valider en P2/P3.
|
||||
5. La regex d'options `^\s*(❯\s*)?(\d+)\.\s*(.+)$` fonctionne sur le texte aplati (labels concaténés mais exploitables).
|
||||
|
||||
## Volet flood (node-pty pause/resume) — ✅ GO complet
|
||||
## Volet flood (node-pty pause/resume) · ✅ GO complet
|
||||
|
||||
21,5 Mo streamés ; pause de 10 s → **0 octet** reçu pendant la pause, bash survit, flux complet après resume (marqueur final intact, aucun octet perdu) ; écho 4,7 ms après le flood ; ~21 Mo en ~0,7 s hors pause.
|
||||
|
||||
@@ -28,5 +28,5 @@ Exécuté le 2026-06-11 (CLI 2.1.173). Scripts : `run.mjs` (passe 1), `run2.mjs`
|
||||
|
||||
- Exercer le refus par Esc et le dialogue « No, and tell Claude… » (champ texte).
|
||||
- Approbation de plan (plan mode) non testée.
|
||||
- Campagne de fiabilité 10/10 par type de dialogue (le critère complet du spike) — à automatiser comme tests d'intégration du claude-adapter.
|
||||
- Campagne de fiabilité 10/10 par type de dialogue (le critère complet du spike), à automatiser comme tests d'intégration du claude-adapter.
|
||||
- Le MVP (P3) n'a PAS besoin de tout ça : les boutons d'approbation sont en P4 ; le fallback « ouvrir le terminal » couvre tous les cas.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// Spike S3 (volet flood) — pause()/resume() node-pty sous 20 Mo de débit, sans claude (zéro quota).
|
||||
// Spike S3 (volet flood) · pause()/resume() node-pty sous 20 Mo de débit, sans claude (zéro quota).
|
||||
// Critères : aucune perte (marqueur final reçu, volume >= attendu), 0 octet pendant pause,
|
||||
// process bash survit à 10 s de pause, écho < 100 ms après le flood.
|
||||
import ptyMod from '@homebridge/node-pty-prebuilt-multiarch';
|
||||
@@ -11,7 +11,7 @@ const CAPTURES = new URL('./captures/', import.meta.url).pathname;
|
||||
mkdirSync(CAPTURES, { recursive: true });
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
// 20 Mo de base64 (lignes de 76 chars) — la conversion LF→CRLF du TTY est prise en compte
|
||||
// 20 Mo de base64 (lignes de 76 chars) : la conversion LF→CRLF du TTY est prise en compte
|
||||
execSync('head -c 15728640 /dev/urandom | base64 > /tmp/spike-flood.txt && wc -c < /tmp/spike-flood.txt');
|
||||
const fileBytes = parseInt(execSync('wc -c < /tmp/spike-flood.txt').toString().trim(), 10);
|
||||
const fileLines = parseInt(execSync('wc -l < /tmp/spike-flood.txt').toString().trim(), 10);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// Spike S3 — dialogues TUI : détection (registre + écran), extraction des options, frappes de réponse.
|
||||
// Spike S3 · dialogues TUI : détection (registre + écran), extraction des options, frappes de réponse.
|
||||
// Types couverts : trust dialog (nouveau dossier), permission Bash, permission Write (édition),
|
||||
// AskUserQuestion, refus par Esc. (Approbation de plan : suivi manuel, voir VERDICT.)
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// Spike S3 (2e passe) — D2..D5 avec commandes hors allowlist, scénarios isolés,
|
||||
// Spike S3 (2e passe) · D2..D5 avec commandes hors allowlist, scénarios isolés,
|
||||
// détection registre + fallback écran. D1 (trust) déjà validé en 1re passe.
|
||||
import { execSync } from 'node:child_process';
|
||||
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
|
||||
@@ -78,7 +78,7 @@ await scenario('d2_permBash', async () => {
|
||||
const S = new ClaudeSession([], d, 'perm-bash2', CAPTURES);
|
||||
try {
|
||||
await S.waitReady();
|
||||
await S.type(`Run this exact bash command and show me its output: node -e "console.log('spike-s3-marker-xyz')" — do not do anything else.`);
|
||||
await S.type(`Run this exact bash command and show me its output: node -e "console.log('spike-s3-marker-xyz')", do not do anything else.`);
|
||||
const det = await waitDialog(S);
|
||||
await sleep(1200);
|
||||
const screen = S.tail(2500);
|
||||
@@ -119,7 +119,7 @@ await scenario('d4_denyEsc', async () => {
|
||||
const S = new ClaudeSession([], d, 'deny-esc2', CAPTURES);
|
||||
try {
|
||||
await S.waitReady();
|
||||
await S.type(`Run this exact bash command and show me its output: node -e "console.log('should-be-denied')" — do not do anything else.`);
|
||||
await S.type(`Run this exact bash command and show me its output: node -e "console.log('should-be-denied')", do not do anything else.`);
|
||||
await waitDialog(S);
|
||||
await sleep(1200);
|
||||
const screenBefore = S.tail(1200);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Spike S4 — Découverte JSONL + registre — VERDICT : ✅ GO
|
||||
# Spike S4 · Découverte JSONL + registre · VERDICT : ✅ GO
|
||||
|
||||
Exécuté sur un poste de dev (CLI 2.1.170/2.1.173 actifs, transcripts 2.1.139 → 2.1.173). Script : `scan.mjs`.
|
||||
|
||||
@@ -17,5 +17,5 @@ Exécuté sur un poste de dev (CLI 2.1.170/2.1.173 actifs, transcripts 2.1.139
|
||||
2. **Munge cwd → dossier validé à 100 %** (1316/1316) : `cwd.replace(/[^A-Za-z0-9]/g, '-')` reproduit exactement le nom du dossier projet. Le lookup direct worktree → dossier est fiable en O(1) ; l'inversion reste interdite (ambiguë), le `cwd` se lit dans le contenu.
|
||||
3. **12 types de lignes observés** : `user`, `last-prompt`, `attachment`, `assistant`, `queue-operation`, `ai-title`, `file-history-snapshot`, `system`, `permission-mode`, `agent-name`, `mode`, `bridge-session`. `ai-title` (470 fichiers) est une source de titre supérieure au `slug` → priorité titre : `ai-title` > `summary` > `last-prompt` > 1er prompt user.
|
||||
4. **22 versions du CLI** dans les transcripts → base de fixtures multi-versions toute prête pour les tests du parseur.
|
||||
5. **Registre `~/.claude/sessions`** : statuts observés sur 2 jours : `busy`, `idle`, `waiting` (+ `waitingFor: "permission prompt"`). `kind: interactive`, `peerProtocol: 1`. Les 3 entrées actuelles sont toutes valides (pas de fichier stale en ce moment) — le comportement post-kill -9 sera couvert par S1.
|
||||
5. **Registre `~/.claude/sessions`** : statuts observés sur 2 jours : `busy`, `idle`, `waiting` (+ `waitingFor: "permission prompt"`). `kind: interactive`, `peerProtocol: 1`. Les 3 entrées actuelles sont toutes valides (pas de fichier stale en ce moment) : le comportement post-kill -9 sera couvert par S1.
|
||||
6. **Rattachement** : 573 sessions sous `~/WebstormProjects` (dont worktrees `*-wt-*` avec leur propre dossier projet, confirmé), 743 hors périmètre (dont 323 sur la racine WebstormProjects elle-même) → la vue « sessions hors worktree connu » est nécessaire dès le MVP.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// Spike S4 — découverte des sessions JSONL (~/.claude/projects) + registre (~/.claude/sessions)
|
||||
// Spike S4 · découverte des sessions JSONL (~/.claude/projects) + registre (~/.claude/sessions)
|
||||
// Critères Go : ≥95 % des JSONL exploitables (sessionId+cwd), scan < 3 s (tête+queue),
|
||||
// validation pid/procStart du registre, parseur strictement tolérant.
|
||||
import { readdirSync, statSync, openSync, readSync, closeSync, readFileSync, existsSync } from 'node:fs';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Spike S5 — Extension VSCode (intégration native) — ÉTUDE : ✅ GO (phasé)
|
||||
# Spike S5 · Extension VSCode (intégration native) · ÉTUDE : ✅ GO (phasé)
|
||||
|
||||
> **✅ IMPLÉMENTÉ** dans `packages/vscode` (`git-arboretum`, privé) — phases A→D livrées : connexion +
|
||||
> **✅ 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`).
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
|
||||
É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
|
||||
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).
|
||||
|
||||
@@ -35,7 +35,7 @@ de VSCode ». Le webview (iframe du dashboard) reste un *fallback* optionnel ; c
|
||||
- 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`
|
||||
## 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
|
||||
@@ -92,21 +92,21 @@ restent **synchronisés** avec le serveur par construction.
|
||||
|
||||
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)
|
||||
## Découpage phasé (livrable séparé, non implémenté dans ce lot)
|
||||
|
||||
- **A — Lecture & connexion** : auth (SecretStorage) + client REST/WS + TreeView lecture seule
|
||||
- **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`
|
||||
- **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
|
||||
- **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.
|
||||
- **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
|
||||
`@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
|
||||
|
||||
Reference in New Issue
Block a user