palabre 0.6.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,24 +9,24 @@
9
9
 
10
10
  ![PALABRE](docs/assets/palabre-logo-text-og.png)
11
11
 
12
- [Français](#français) | [English](#english)
12
+ [English](#english) | [Français](#français)
13
13
 
14
- ## Français
14
+ ## English
15
15
 
16
- PALABRE est un orchestrateur CLI qui fait dialoguer plusieurs agents IA installés sur votre machine : Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode et Ollama.
16
+ PALABRE is a CLI/TUI orchestrator that lets multiple AI agents installed on your machine work together: Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode, Mistral Vibe, and Ollama.
17
17
 
18
- Il ne remplace pas vos outils : il les pilote. Vous gardez vos abonnements, vos modèles par défaut, vos habitudes de terminal et vos fichiers en local. PALABRE exporte ensuite le débat en Markdown.
18
+ It does not replace your tools: it drives them. You keep your subscriptions, default models, terminal habits, and local files. PALABRE can run a debate between two agents or an Ask request where several agents answer independently before a comparative summary. It then exports the session as Markdown.
19
19
 
20
20
  ### Documentation
21
21
 
22
22
  - https://palab.re
23
23
  - https://palabre.netlify.app
24
24
 
25
- Pages utiles : [Installation](https://palab.re/fr/get-started/installation), [Configuration](https://palab.re/fr/get-started/configuration), [Premier débat](https://palab.re/fr/get-started/first-debate), [Référence CLI](https://palab.re/fr/reference/cli), [Dépannage](https://palab.re/fr/troubleshooting), [Roadmap](https://palab.re/fr/roadmap).
25
+ Useful pages: [Installation](https://palab.re/en/get-started/installation), [Configuration](https://palab.re/en/get-started/configuration), [First session](https://palab.re/en/get-started/first-debate), [CLI reference](https://palab.re/en/reference/cli), [Troubleshooting](https://palab.re/en/troubleshooting), [Roadmap](https://palab.re/en/roadmap).
26
26
 
27
27
  ### Installation
28
28
 
29
- Prérequis : Node.js 20 ou plus, et au moins deux agents déjà installés/authentifiés si vous voulez les faire débattre.
29
+ Requirements: Node.js 20 or newer, and at least two already installed/authenticated agents if you want them to debate.
30
30
 
31
31
  ```bash
32
32
  npm install -g palabre
@@ -34,53 +34,71 @@ palabre --version
34
34
  palabre --help
35
35
  ```
36
36
 
37
- ### Démarrage rapide
37
+ ### Quick Start
38
38
 
39
39
  ```bash
40
40
  palabre init
41
41
  palabre doctor
42
- palabre new
42
+ palabre
43
43
  ```
44
44
 
45
- Exemples directs :
45
+ Direct examples:
46
46
 
47
47
  ```bash
48
- palabre codex-claude "Critique ce plan" -t 4
49
- palabre -s "Compare ces deux approches" -t 2
50
- palabre codex-claude "Relis cette architecture" --context src docs
51
- palabre claude-ollama "Critique ce fichier" --files README.md
48
+ palabre codex-claude "Review this plan" -t 4
49
+ palabre ask "Compare these two approaches" --agents codex claude opencode
50
+ palabre -s "Compare these two approaches" -t 2
51
+ palabre codex-claude "Review this architecture" --context src docs
52
+ palabre claude-ollama "Review this file" --files README.md
52
53
  palabre codex-claude "Preview" --context src --show-prompt
53
54
  palabre context scan src docs --json
54
55
  ```
55
56
 
56
- ### Agents supportés
57
+ In an interactive terminal, Palabre uses the TUI by default. `palabre` opens the home screen, `/ask` switches from debate to independent answers, `/agents` and `/roles` help you choose the active setup, and `--terminal` forces the older raw rendering suitable for logs.
58
+
59
+ ### Supported Agents
57
60
 
58
61
  - Claude Code via `claude --print`
59
62
  - Codex CLI via `codex exec`
60
63
  - Gemini CLI via `gemini --prompt -`
61
- - Antigravity CLI via `agy --print` en pseudo-terminal
64
+ - Antigravity CLI via `agy --print` in a pseudo-terminal
62
65
  - OpenCode via `opencode run`
63
- - Ollama via l'API locale HTTP
66
+ - Mistral Vibe via `vibe --output text --agent plan --trust --prompt`
67
+ - Ollama via the local HTTP API
64
68
 
65
- PALABRE ne liste pas les modèles : ils changent souvent et dépendent de chaque CLI ou compte utilisateur. `--model-a`, `--model-b` et `--summary-model` transmettent simplement la valeur brute à l'agent concerné.
69
+ PALABRE does not list models: they change often and depend on each CLI or user account. `--model-a`, `--model-b`, and `--summary-model` simply pass the raw value to the selected agent.
66
70
 
67
- ### Intégrations
71
+ ### Integrations
68
72
 
69
- PALABRE expose des sorties JSON versionnées pour les clients externes :
73
+ PALABRE exposes versioned JSON outputs for external clients:
70
74
 
71
- - `palabre presets --json` pour lire les paires d'agents disponibles ;
72
- - `palabre context scan --json` pour prévisualiser le contexte que `--context` retiendrait ;
73
- - `--renderer ndjson` ou `--json` pour suivre un débat événement par événement.
75
+ - `palabre presets --json` to read available agent pairs;
76
+ - `palabre context scan --json` to preview the context `--context` would retain;
77
+ - `--renderer ndjson` or `--json` to follow a debate event by event.
74
78
 
75
- Le flux NDJSON v1 est traité comme une API publique d'intégration. Les ajouts compatibles se font sans casser v1 ; les changements cassants doivent changer le champ `v`.
79
+ The NDJSON v1 stream is treated as a public integration API. Compatible additions do not break v1; breaking changes must change the `v` field.
76
80
 
77
- ### Confidentialité
81
+ ### Skill for AI agents
78
82
 
79
- PALABRE tourne localement et n'envoie aucune donnée à un serveur appartenant à PALABRE. Les données envoyées aux agents dépendent des outils que vous utilisez : vérifiez les politiques de confidentialité de Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode, Ollama ou de tout autre agent configuré.
83
+ PALABRE ships a ready-to-use skill that teaches an AI agent when and how to run Palabre sessions. It follows the open [agentskills.io](https://agentskills.io) standard, so it is portable across Hermes Agent, Claude, Codex, Gemini CLI, and any skills-compatible agent.
80
84
 
81
- Si un agent échoue pendant le débat ou la synthèse, PALABRE conserve l'export Markdown partiel avec une section d'interruption quand c'est possible.
85
+ Install it in **Hermes Agent**:
82
86
 
83
- ### Développement local
87
+ ```bash
88
+ hermes skills install JuReyms/Palabre/skills/palabre
89
+ ```
90
+
91
+ For other agents (Claude desktop, Claude Code…), see the docs: [Palabre skill](https://palab.re/en/get-started/skill).
92
+
93
+ The skill is versioned under [skills/palabre](./skills/palabre).
94
+
95
+ ### Privacy
96
+
97
+ PALABRE runs locally and does not send data to a PALABRE-owned server. Data sent to agents depends on the tools you use: check the privacy policies of Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode, Mistral Vibe, Ollama, or any custom agent you configure.
98
+
99
+ If an agent fails during the debate or final summary, PALABRE keeps the partial Markdown export with an interruption section whenever possible.
100
+
101
+ ### Local Development
84
102
 
85
103
  ```bash
86
104
  git clone https://github.com/JuReyms/Palabre.git
@@ -91,32 +109,32 @@ pnpm link --global
91
109
  palabre --version
92
110
  ```
93
111
 
94
- Commandes utiles : `pnpm check`, `pnpm test`, `pnpm build`.
112
+ Useful commands: `pnpm check`, `pnpm test`, `pnpm build`.
95
113
 
96
- Avant une publication, `pnpm smoke:real-presets -- --keep-going` lance des débats réels sur les presets prioritaires disponibles afin de vérifier le flux complet agent → NDJSON → export. Ce smoke test appelle de vraies CLIs IA et peut consommer des quotas ; il n'est donc pas lancé par `pnpm test`.
114
+ Before publishing, `pnpm smoke:real-presets -- --keep-going` runs real debates for the available priority presets to validate the full agent → NDJSON → export flow. This smoke test calls real AI CLIs and may consume quota, so it is not part of `pnpm test`.
97
115
 
98
- Roadmap publique : [docs/guide/fr/roadmap.md](./docs/guide/fr/roadmap.md). Changements : [CHANGELOG.md](./CHANGELOG.md). Guide agents/contributeurs : [AGENTS.md](./AGENTS.md).
116
+ Public roadmap: [docs/guide/fr/roadmap.md](./docs/guide/fr/roadmap.md). Changes: [CHANGELOG.md](./CHANGELOG.md). Agent/contributor guide: [AGENTS.md](./AGENTS.md).
99
117
 
100
- ### Licence
118
+ ### License
101
119
 
102
- MIT. Voir [LICENSE](./LICENSE).
120
+ MIT. See [LICENSE](./LICENSE).
103
121
 
104
- ## English
122
+ ## Français
105
123
 
106
- PALABRE is a CLI orchestrator that lets multiple AI agents installed on your machine talk to each other: Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode, and Ollama.
124
+ PALABRE est un orchestrateur CLI/TUI qui fait travailler plusieurs agents IA installés sur votre machine : Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode, Mistral Vibe et Ollama.
107
125
 
108
- It does not replace your tools: it drives them. You keep your subscriptions, default models, terminal habits, and local files. PALABRE then exports the debate as Markdown.
126
+ Il ne remplace pas vos outils : il les pilote. Vous gardez vos abonnements, vos modèles par défaut, vos habitudes de terminal et vos fichiers en local. PALABRE peut lancer un débat entre deux agents ou une demande Ask où plusieurs agents répondent indépendamment avant une synthèse comparative. Il exporte ensuite la session en Markdown.
109
127
 
110
128
  ### Documentation
111
129
 
112
130
  - https://palab.re
113
131
  - https://palabre.netlify.app
114
132
 
115
- Useful pages: [Installation](https://palab.re/en/get-started/installation), [Configuration](https://palab.re/en/get-started/configuration), [First debate](https://palab.re/en/get-started/first-debate), [CLI reference](https://palab.re/en/reference/cli), [Troubleshooting](https://palab.re/en/troubleshooting), [Roadmap](https://palab.re/en/roadmap).
133
+ Pages utiles : [Installation](https://palab.re/fr/get-started/installation), [Configuration](https://palab.re/fr/get-started/configuration), [Première session](https://palab.re/fr/get-started/first-debate), [Référence CLI](https://palab.re/fr/reference/cli), [Dépannage](https://palab.re/fr/troubleshooting), [Roadmap](https://palab.re/fr/roadmap).
116
134
 
117
135
  ### Installation
118
136
 
119
- Requirements: Node.js 20 or newer, and at least two already installed/authenticated agents if you want them to debate.
137
+ Prérequis : Node.js 20 ou plus, et au moins deux agents déjà installés/authentifiés si vous voulez les faire débattre.
120
138
 
121
139
  ```bash
122
140
  npm install -g palabre
@@ -124,53 +142,71 @@ palabre --version
124
142
  palabre --help
125
143
  ```
126
144
 
127
- ### Quick Start
145
+ ### Démarrage rapide
128
146
 
129
147
  ```bash
130
148
  palabre init
131
149
  palabre doctor
132
- palabre new
150
+ palabre
133
151
  ```
134
152
 
135
- Direct examples:
153
+ Exemples directs :
136
154
 
137
155
  ```bash
138
- palabre codex-claude "Review this plan" -t 4
139
- palabre -s "Compare these two approaches" -t 2
140
- palabre codex-claude "Review this architecture" --context src docs
141
- palabre claude-ollama "Review this file" --files README.md
156
+ palabre codex-claude "Critique ce plan" -t 4
157
+ palabre ask "Compare ces deux approches" --agents codex claude opencode
158
+ palabre -s "Compare ces deux approches" -t 2
159
+ palabre codex-claude "Relis cette architecture" --context src docs
160
+ palabre claude-ollama "Critique ce fichier" --files README.md
142
161
  palabre codex-claude "Preview" --context src --show-prompt
143
162
  palabre context scan src docs --json
144
163
  ```
145
164
 
146
- ### Supported Agents
165
+ Dans un terminal interactif, Palabre utilise l'interface TUI par défaut. `palabre` ouvre l'accueil, `/ask` passe du débat aux réponses indépendantes, `/agents` et `/roles` aident à choisir la configuration courante, et `--terminal` force l'ancien rendu brut adapté aux logs.
166
+
167
+ ### Agents supportés
147
168
 
148
169
  - Claude Code via `claude --print`
149
170
  - Codex CLI via `codex exec`
150
171
  - Gemini CLI via `gemini --prompt -`
151
- - Antigravity CLI via `agy --print` in a pseudo-terminal
172
+ - Antigravity CLI via `agy --print` en pseudo-terminal
152
173
  - OpenCode via `opencode run`
153
- - Ollama via the local HTTP API
174
+ - Mistral Vibe via `vibe --output text --agent plan --trust --prompt`
175
+ - Ollama via l'API locale HTTP
154
176
 
155
- PALABRE does not list models: they change often and depend on each CLI or user account. `--model-a`, `--model-b`, and `--summary-model` simply pass the raw value to the selected agent.
177
+ PALABRE ne liste pas les modèles : ils changent souvent et dépendent de chaque CLI ou compte utilisateur. `--model-a`, `--model-b` et `--summary-model` transmettent simplement la valeur brute à l'agent concerné.
156
178
 
157
- ### Integrations
179
+ ### Intégrations
158
180
 
159
- PALABRE exposes versioned JSON outputs for external clients:
181
+ PALABRE expose des sorties JSON versionnées pour les clients externes :
160
182
 
161
- - `palabre presets --json` to read available agent pairs;
162
- - `palabre context scan --json` to preview the context `--context` would retain;
163
- - `--renderer ndjson` or `--json` to follow a debate event by event.
183
+ - `palabre presets --json` pour lire les paires d'agents disponibles ;
184
+ - `palabre context scan --json` pour prévisualiser le contexte que `--context` retiendrait ;
185
+ - `--renderer ndjson` ou `--json` pour suivre un débat événement par événement.
164
186
 
165
- The NDJSON v1 stream is treated as a public integration API. Compatible additions do not break v1; breaking changes must change the `v` field.
187
+ Le flux NDJSON v1 est traité comme une API publique d'intégration. Les ajouts compatibles se font sans casser v1 ; les changements cassants doivent changer le champ `v`.
166
188
 
167
- ### Privacy
189
+ ### Skill pour agents IA
168
190
 
169
- PALABRE runs locally and does not send data to a PALABRE-owned server. Data sent to agents depends on the tools you use: check the privacy policies of Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode, Ollama, or any custom agent you configure.
191
+ PALABRE fournit un skill prêt à l'emploi qui apprend à un agent IA quand et comment lancer des sessions Palabre. Il suit le standard ouvert [agentskills.io](https://agentskills.io) : il est donc portable entre Hermes Agent, Claude, Codex, Gemini CLI et tout agent compatible skills.
170
192
 
171
- If an agent fails during the debate or final summary, PALABRE keeps the partial Markdown export with an interruption section whenever possible.
193
+ Installation dans **Hermes Agent** :
172
194
 
173
- ### Local Development
195
+ ```bash
196
+ hermes skills install JuReyms/Palabre/skills/palabre
197
+ ```
198
+
199
+ Pour les autres agents (Claude desktop, Claude Code…), voir la doc : [Skill Palabre](https://palab.re/fr/get-started/skill).
200
+
201
+ Le skill est versionné dans [skills/palabre](./skills/palabre).
202
+
203
+ ### Confidentialité
204
+
205
+ PALABRE tourne localement et n'envoie aucune donnée à un serveur appartenant à PALABRE. Les données envoyées aux agents dépendent des outils que vous utilisez : vérifiez les politiques de confidentialité de Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode, Mistral Vibe, Ollama ou de tout autre agent configuré.
206
+
207
+ Si un agent échoue pendant le débat ou la synthèse, PALABRE conserve l'export Markdown partiel avec une section d'interruption quand c'est possible.
208
+
209
+ ### Développement local
174
210
 
175
211
  ```bash
176
212
  git clone https://github.com/JuReyms/Palabre.git
@@ -181,12 +217,12 @@ pnpm link --global
181
217
  palabre --version
182
218
  ```
183
219
 
184
- Useful commands: `pnpm check`, `pnpm test`, `pnpm build`.
220
+ Commandes utiles : `pnpm check`, `pnpm test`, `pnpm build`.
185
221
 
186
- Before publishing, `pnpm smoke:real-presets -- --keep-going` runs real debates for the available priority presets to validate the full agent → NDJSON → export flow. This smoke test calls real AI CLIs and may consume quota, so it is not part of `pnpm test`.
222
+ Avant une publication, `pnpm smoke:real-presets -- --keep-going` lance des débats réels sur les presets prioritaires disponibles afin de vérifier le flux complet agent → NDJSON → export. Ce smoke test appelle de vraies CLIs IA et peut consommer des quotas ; il n'est donc pas lancé par `pnpm test`.
187
223
 
188
- Public roadmap: [docs/guide/fr/roadmap.md](./docs/guide/fr/roadmap.md). Changes: [CHANGELOG.md](./CHANGELOG.md). Agent/contributor guide: [AGENTS.md](./AGENTS.md).
224
+ Roadmap publique : [docs/guide/fr/roadmap.md](./docs/guide/fr/roadmap.md). Changements : [CHANGELOG.md](./CHANGELOG.md). Guide agents/contributeurs : [AGENTS.md](./AGENTS.md).
189
225
 
190
- ### License
226
+ ### Licence
191
227
 
192
- MIT. See [LICENSE](./LICENSE).
228
+ MIT. Voir [LICENSE](./LICENSE).
@@ -38,6 +38,9 @@ export class CliPtyAdapter {
38
38
  };
39
39
  }
40
40
  async generate(prompt) {
41
+ if (prompt.signal?.aborted) {
42
+ throw cancelledError(this.name);
43
+ }
41
44
  const renderedPrompt = formatAgentPrompt(prompt);
42
45
  const promptMode = this.config.promptMode ?? "stdin";
43
46
  const baseArgs = withModelArgs(this.config.args ?? [], this.config.model, this.config.modelArg ?? "--model");
@@ -53,6 +56,7 @@ export class CliPtyAdapter {
53
56
  let term;
54
57
  let dataSubscription;
55
58
  let exitSubscription;
59
+ let abortListener;
56
60
  const maxOutputBytes = this.config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
57
61
  const finish = (error, exitCode, kill = true) => {
58
62
  if (settled)
@@ -61,6 +65,9 @@ export class CliPtyAdapter {
61
65
  clearTimeout(hardTimer);
62
66
  dataSubscription?.dispose();
63
67
  exitSubscription?.dispose();
68
+ if (abortListener) {
69
+ prompt.signal?.removeEventListener("abort", abortListener);
70
+ }
64
71
  if (kill) {
65
72
  try {
66
73
  term.kill();
@@ -107,6 +114,10 @@ export class CliPtyAdapter {
107
114
  }));
108
115
  return;
109
116
  }
117
+ abortListener = () => {
118
+ finish(cancelledError(this.name));
119
+ };
120
+ prompt.signal?.addEventListener("abort", abortListener, { once: true });
110
121
  hardTimer = setTimeout(() => {
111
122
  finish(new AdapterError("timeout", this.name, `${this.name} timed out after ${this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS}ms`, {
112
123
  timeoutMs: this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS
@@ -165,6 +176,9 @@ function createPtyExitError(adapterName, exitCode, raw) {
165
176
  raw
166
177
  });
167
178
  }
179
+ function cancelledError(adapterName) {
180
+ return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
181
+ }
168
182
  function summarizePtyOutput(output) {
169
183
  const cleaned = cleanTerminalOutput(output);
170
184
  return cleaned ? cleaned.slice(-1_200) : "aucune sortie PTY capturee.";
@@ -37,6 +37,9 @@ export class CliAdapter {
37
37
  };
38
38
  }
39
39
  async generate(prompt) {
40
+ if (prompt.signal?.aborted) {
41
+ throw cancelledError(this.name);
42
+ }
40
43
  const renderedPrompt = formatAgentPrompt(prompt);
41
44
  const promptMode = this.config.promptMode ?? "stdin";
42
45
  const baseArgs = withModelArgs(this.config.args ?? [], this.config.model, this.config.modelArg ?? "--model");
@@ -54,6 +57,7 @@ export class CliAdapter {
54
57
  let outputBytes = 0;
55
58
  let hardTimer;
56
59
  let idleTimer;
60
+ let abortListener;
57
61
  const maxOutputBytes = this.config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
58
62
  const finish = (error) => {
59
63
  if (settled)
@@ -62,6 +66,9 @@ export class CliAdapter {
62
66
  clearTimeout(hardTimer);
63
67
  if (idleTimer)
64
68
  clearTimeout(idleTimer);
69
+ if (abortListener) {
70
+ prompt.signal?.removeEventListener("abort", abortListener);
71
+ }
65
72
  if (error) {
66
73
  reject(error);
67
74
  return;
@@ -84,8 +91,13 @@ export class CliAdapter {
84
91
  raw: stdout
85
92
  });
86
93
  };
94
+ abortListener = () => {
95
+ killChildProcess(child);
96
+ finish(cancelledError(this.name));
97
+ };
98
+ prompt.signal?.addEventListener("abort", abortListener, { once: true });
87
99
  hardTimer = setTimeout(() => {
88
- child.kill();
100
+ killChildProcess(child);
89
101
  finish(new AdapterError("timeout", this.name, `${this.name} timed out after ${this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS}ms`, {
90
102
  timeoutMs: this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS
91
103
  }));
@@ -96,7 +108,7 @@ export class CliAdapter {
96
108
  if (idleTimer)
97
109
  clearTimeout(idleTimer);
98
110
  idleTimer = setTimeout(() => {
99
- child.kill();
111
+ killChildProcess(child);
100
112
  finish(new AdapterError("idle-timeout", this.name, `${this.name} stopped producing output for ${this.config.idleTimeoutMs}ms`, { idleTimeoutMs: this.config.idleTimeoutMs }));
101
113
  }, this.config.idleTimeoutMs);
102
114
  };
@@ -104,7 +116,7 @@ export class CliAdapter {
104
116
  child.stdout.on("data", (chunk) => {
105
117
  outputBytes += chunk.length;
106
118
  if (outputBytes > maxOutputBytes) {
107
- child.kill();
119
+ killChildProcess(child);
108
120
  finish(new AdapterError("output-too-large", this.name, `${this.name} produced more than ${maxOutputBytes} bytes of output`, {
109
121
  maxOutputBytes,
110
122
  outputBytes
@@ -117,7 +129,7 @@ export class CliAdapter {
117
129
  child.stderr.on("data", (chunk) => {
118
130
  outputBytes += chunk.length;
119
131
  if (outputBytes > maxOutputBytes) {
120
- child.kill();
132
+ killChildProcess(child);
121
133
  finish(new AdapterError("output-too-large", this.name, `${this.name} produced more than ${maxOutputBytes} bytes of output`, {
122
134
  maxOutputBytes,
123
135
  outputBytes
@@ -295,3 +307,19 @@ function clipLine(value, maxLength) {
295
307
  ? value
296
308
  : `${value.slice(0, maxLength - 1)}…`;
297
309
  }
310
+ function cancelledError(adapterName) {
311
+ return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
312
+ }
313
+ function killChildProcess(child) {
314
+ if (process.platform === "win32" && child.pid) {
315
+ const killer = spawn("taskkill.exe", ["/PID", String(child.pid), "/T", "/F"], {
316
+ windowsHide: true,
317
+ stdio: "ignore"
318
+ });
319
+ killer.on("error", () => {
320
+ child.kill();
321
+ });
322
+ return;
323
+ }
324
+ child.kill();
325
+ }
@@ -35,6 +35,9 @@ export class OllamaAdapter {
35
35
  };
36
36
  }
37
37
  async generate(prompt) {
38
+ if (prompt.signal?.aborted) {
39
+ throw cancelledError(this.name);
40
+ }
38
41
  const baseUrl = normalizeBaseUrl(this.config.baseUrl ?? "http://localhost:11434");
39
42
  if (this.config.validateModel !== false) {
40
43
  await this.ensureModelAvailable(baseUrl);
@@ -43,6 +46,8 @@ export class OllamaAdapter {
43
46
  await this.unloadOtherRunningModels(baseUrl);
44
47
  }
45
48
  const controller = new AbortController();
49
+ const abortListener = () => controller.abort();
50
+ prompt.signal?.addEventListener("abort", abortListener, { once: true });
46
51
  const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs ?? 120_000);
47
52
  try {
48
53
  const response = await fetch(`${baseUrl}/api/chat`, {
@@ -85,8 +90,15 @@ export class OllamaAdapter {
85
90
  content
86
91
  };
87
92
  }
93
+ catch (error) {
94
+ if (prompt.signal?.aborted) {
95
+ throw cancelledError(this.name);
96
+ }
97
+ throw error;
98
+ }
88
99
  finally {
89
100
  clearTimeout(timeout);
101
+ prompt.signal?.removeEventListener("abort", abortListener);
90
102
  }
91
103
  }
92
104
  /**
@@ -217,3 +229,6 @@ async function unloadModel(baseUrl, model, signal) {
217
229
  function normalizeBaseUrl(baseUrl) {
218
230
  return baseUrl.replace(/\/$/, "");
219
231
  }
232
+ function cancelledError(adapterName) {
233
+ return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
234
+ }
@@ -8,7 +8,8 @@ const KNOWN_CLI_AGENTS = [
8
8
  { configKey: "claude", commandAliases: ["claude"], discoveryKey: "claude" },
9
9
  { configKey: "gemini", commandAliases: ["gemini"], discoveryKey: "gemini" },
10
10
  { configKey: "antigravity", commandAliases: ["agy", "antigravity"], discoveryKey: "antigravity" },
11
- { configKey: "opencode", commandAliases: ["opencode"], discoveryKey: "opencode" }
11
+ { configKey: "opencode", commandAliases: ["opencode"], discoveryKey: "opencode" },
12
+ { configKey: "vibe", commandAliases: ["vibe"], discoveryKey: "vibe" }
12
13
  ];
13
14
  /** Clé de config de l'agent Ollama local par défaut. */
14
15
  export const OLLAMA_AGENT_KEY = "ollama-local";
@@ -36,7 +37,7 @@ export function detectionForCommand(command, discovery) {
36
37
  }
37
38
  /**
38
39
  * Liste les clés d'agents connus effectivement détectés localement, dans
39
- * l'ordre canonique (`codex`, `claude`, `gemini`, `antigravity`, `opencode`,
40
+ * l'ordre canonique (`codex`, `claude`, `gemini`, `antigravity`, `opencode`, `vibe`,
40
41
  * puis `ollama-local`).
41
42
  */
42
43
  export function detectedAgentNames(discovery) {
package/dist/args.js CHANGED
@@ -13,6 +13,8 @@ const FLAG_SPECS = {
13
13
  // Booléens : présence = vrai, aucune valeur consommée.
14
14
  help: { arity: "boolean" },
15
15
  version: { arity: "boolean" },
16
+ tui: { arity: "boolean" },
17
+ terminal: { arity: "boolean" },
16
18
  plain: { arity: "boolean" },
17
19
  json: { arity: "boolean" },
18
20
  "no-summary": { arity: "boolean" },
@@ -23,20 +25,28 @@ const FLAG_SPECS = {
23
25
  apply: { arity: "boolean" },
24
26
  "clear-defaults": { arity: "boolean" },
25
27
  "sync-agents": { arity: "boolean" },
28
+ "sync-ollama-model": { arity: "boolean" },
29
+ "ollama-models": { arity: "boolean" },
26
30
  // Valeur unique.
27
31
  "agent-a": { arity: "single" },
28
32
  "agent-b": { arity: "single" },
29
33
  config: { arity: "single" },
34
+ interface: { arity: "single" },
30
35
  language: { arity: "single" },
31
36
  "model-a": { arity: "single" },
32
37
  "model-b": { arity: "single" },
38
+ mode: { arity: "single" },
39
+ "set-ollama-model": { arity: "single" },
33
40
  preset: { arity: "single" },
34
41
  "summary-agent": { arity: "single" },
42
+ "ask-summary-agent": { arity: "single" },
35
43
  "summary-model": { arity: "single" },
36
44
  topic: { arity: "single" },
37
45
  turns: { arity: "single" },
38
46
  renderer: { arity: "single" },
39
47
  // Valeurs multiples.
48
+ agents: { arity: "multi", max: 4 },
49
+ "ask-agents": { arity: "multi", max: 4 },
40
50
  "set-defaults": { arity: "multi", max: 2 },
41
51
  files: { arity: "multi" },
42
52
  context: { arity: "multi" }
@@ -44,6 +54,7 @@ const FLAG_SPECS = {
44
54
  /** Commandes acceptées comme premier argument positionnel. */
45
55
  const COMMANDS = new Set([
46
56
  "run",
57
+ "ask",
47
58
  "new",
48
59
  "init",
49
60
  "setup",
@@ -170,8 +181,17 @@ export function parseArgs(args, messages) {
170
181
  if (command === "run") {
171
182
  applyRunPositionals(positionals, flags, presets, commandExplicit, COMMANDS, messages);
172
183
  }
184
+ else if (command === "ask") {
185
+ flags.mode = "ask";
186
+ applyTopicPositionals(positionals, flags);
187
+ }
173
188
  return { command, commandExplicit, positionals, flags };
174
189
  }
190
+ function applyTopicPositionals(positionals, flags) {
191
+ if (positionals.length > 0) {
192
+ flags.topic ??= positionals.join(" ");
193
+ }
194
+ }
175
195
  /**
176
196
  * Détecte si une valeur ressemble à une faute de frappe d'une commande connue
177
197
  * (même première lettre et distance de Levenshtein ≤ 2).
@@ -245,7 +265,8 @@ export function normalizeFlagName(value) {
245
265
  lang: "language",
246
266
  s: "topic",
247
267
  subject: "topic",
248
- t: "turns"
268
+ t: "turns",
269
+ "no-tui": "terminal"
249
270
  };
250
271
  return aliases[value] ?? value;
251
272
  }