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 +99 -63
- package/dist/adapters/cli-pty.js +14 -0
- package/dist/adapters/cli.js +32 -4
- package/dist/adapters/ollama.js +15 -0
- package/dist/agentRegistry.js +3 -2
- package/dist/args.js +22 -1
- package/dist/config.js +78 -4
- package/dist/configWizard.js +29 -6
- package/dist/discovery.js +3 -1
- package/dist/doctor.js +18 -0
- package/dist/index.js +593 -55
- package/dist/messages/agents.js +4 -2
- package/dist/messages/common.js +4 -0
- package/dist/messages/config.js +28 -8
- package/dist/messages/doctor.js +8 -0
- package/dist/messages/help.js +58 -2
- package/dist/messages/index.js +2 -0
- package/dist/messages/init.js +2 -2
- package/dist/messages/new.js +14 -0
- package/dist/messages/orchestrator.js +2 -0
- package/dist/messages/output.js +10 -0
- package/dist/messages/preview.js +4 -2
- package/dist/messages/prompt.js +46 -2
- package/dist/messages/renderers.js +2 -2
- package/dist/messages/tui.js +192 -0
- package/dist/messages/update.js +16 -2
- package/dist/new.js +158 -4
- package/dist/orchestrator.js +199 -6
- package/dist/output.js +31 -8
- package/dist/presets.js +48 -0
- package/dist/prompt.js +61 -10
- package/dist/renderers/console.js +39 -3
- package/dist/renderers/ndjson.js +30 -1
- package/dist/renderers/tui.js +932 -0
- package/dist/update.js +2 -0
- package/dist/version.js +54 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -9,24 +9,24 @@
|
|
|
9
9
|
|
|
10
10
|

|
|
11
11
|
|
|
12
|
-
[
|
|
12
|
+
[English](#english) | [Français](#français)
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## English
|
|
15
15
|
|
|
16
|
-
PALABRE
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
37
|
+
### Quick Start
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
palabre init
|
|
41
41
|
palabre doctor
|
|
42
|
-
palabre
|
|
42
|
+
palabre
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Direct examples:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
palabre codex-claude "
|
|
49
|
-
palabre
|
|
50
|
-
palabre
|
|
51
|
-
palabre claude
|
|
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
|
-
|
|
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`
|
|
64
|
+
- Antigravity CLI via `agy --print` in a pseudo-terminal
|
|
62
65
|
- OpenCode via `opencode run`
|
|
63
|
-
-
|
|
66
|
+
- Mistral Vibe via `vibe --output text --agent plan --trust --prompt`
|
|
67
|
+
- Ollama via the local HTTP API
|
|
64
68
|
|
|
65
|
-
PALABRE
|
|
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
|
-
###
|
|
71
|
+
### Integrations
|
|
68
72
|
|
|
69
|
-
PALABRE
|
|
73
|
+
PALABRE exposes versioned JSON outputs for external clients:
|
|
70
74
|
|
|
71
|
-
- `palabre presets --json`
|
|
72
|
-
- `palabre context scan --json`
|
|
73
|
-
- `--renderer ndjson`
|
|
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
|
-
|
|
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
|
-
###
|
|
81
|
+
### Skill for AI agents
|
|
78
82
|
|
|
79
|
-
PALABRE
|
|
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
|
-
|
|
85
|
+
Install it in **Hermes Agent**:
|
|
82
86
|
|
|
83
|
-
|
|
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
|
-
|
|
112
|
+
Useful commands: `pnpm check`, `pnpm test`, `pnpm build`.
|
|
95
113
|
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
118
|
+
### License
|
|
101
119
|
|
|
102
|
-
MIT.
|
|
120
|
+
MIT. See [LICENSE](./LICENSE).
|
|
103
121
|
|
|
104
|
-
##
|
|
122
|
+
## Français
|
|
105
123
|
|
|
106
|
-
PALABRE
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
145
|
+
### Démarrage rapide
|
|
128
146
|
|
|
129
147
|
```bash
|
|
130
148
|
palabre init
|
|
131
149
|
palabre doctor
|
|
132
|
-
palabre
|
|
150
|
+
palabre
|
|
133
151
|
```
|
|
134
152
|
|
|
135
|
-
|
|
153
|
+
Exemples directs :
|
|
136
154
|
|
|
137
155
|
```bash
|
|
138
|
-
palabre codex-claude "
|
|
139
|
-
palabre
|
|
140
|
-
palabre
|
|
141
|
-
palabre claude
|
|
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
|
-
|
|
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`
|
|
172
|
+
- Antigravity CLI via `agy --print` en pseudo-terminal
|
|
152
173
|
- OpenCode via `opencode run`
|
|
153
|
-
-
|
|
174
|
+
- Mistral Vibe via `vibe --output text --agent plan --trust --prompt`
|
|
175
|
+
- Ollama via l'API locale HTTP
|
|
154
176
|
|
|
155
|
-
PALABRE
|
|
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
|
-
###
|
|
179
|
+
### Intégrations
|
|
158
180
|
|
|
159
|
-
PALABRE
|
|
181
|
+
PALABRE expose des sorties JSON versionnées pour les clients externes :
|
|
160
182
|
|
|
161
|
-
- `palabre presets --json`
|
|
162
|
-
- `palabre context scan --json`
|
|
163
|
-
- `--renderer ndjson`
|
|
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
|
-
|
|
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
|
-
###
|
|
189
|
+
### Skill pour agents IA
|
|
168
190
|
|
|
169
|
-
PALABRE
|
|
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
|
-
|
|
193
|
+
Installation dans **Hermes Agent** :
|
|
172
194
|
|
|
173
|
-
|
|
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
|
-
|
|
220
|
+
Commandes utiles : `pnpm check`, `pnpm test`, `pnpm build`.
|
|
185
221
|
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
226
|
+
### Licence
|
|
191
227
|
|
|
192
|
-
MIT.
|
|
228
|
+
MIT. Voir [LICENSE](./LICENSE).
|
package/dist/adapters/cli-pty.js
CHANGED
|
@@ -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.";
|
package/dist/adapters/cli.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|
package/dist/adapters/ollama.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/agentRegistry.js
CHANGED
|
@@ -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
|
}
|