palabre 0.8.0 → 0.8.1
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 +13 -15
- package/dist/adapters/cli-pty.js +1 -1
- package/dist/adapters/cli.js +33 -3
- package/dist/adapters/ollama.js +1 -1
- package/dist/agentRegistry.js +1 -2
- package/dist/args.js +4 -2
- package/dist/config.js +25 -25
- package/dist/context.js +5 -1
- package/dist/discovery.js +1 -3
- package/dist/doctor.js +0 -1
- package/dist/history.js +85 -0
- package/dist/index.js +346 -179
- package/dist/messages/config.js +2 -0
- package/dist/messages/help.js +58 -8
- package/dist/messages/init.js +2 -2
- package/dist/messages/tui.js +64 -28
- package/dist/orchestrator.js +12 -4
- package/dist/presets.js +0 -48
- package/dist/renderers/tui.js +196 -73
- package/dist/tuiState.js +31 -0
- package/package.json +1 -1
- package/palabre.config.example.json +0 -17
package/README.md
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
<a href="https://palab.re"><img src="https://img.shields.io/badge/docs-palab.re-18181B?logo=netlify&logoColor=7C3AED" alt="Documentation"></a>
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
|
-

|
|
11
11
|
|
|
12
12
|
[English](#english) | [Français](#français)
|
|
13
13
|
|
|
14
14
|
## English
|
|
15
15
|
|
|
16
|
-
PALABRE is a CLI/TUI orchestrator that lets multiple AI agents installed on your machine work together: Claude Code, Codex CLI,
|
|
16
|
+
PALABRE is a CLI/TUI orchestrator that lets multiple AI agents installed on your machine work together: Claude Code, Codex 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
|
|
|
@@ -37,7 +37,6 @@ palabre --help
|
|
|
37
37
|
### Quick Start
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
palabre init
|
|
41
40
|
palabre doctor
|
|
42
41
|
palabre
|
|
43
42
|
```
|
|
@@ -54,16 +53,15 @@ palabre codex-claude "Preview" --context src --show-prompt
|
|
|
54
53
|
palabre context scan src docs --json
|
|
55
54
|
```
|
|
56
55
|
|
|
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.
|
|
56
|
+
In an interactive terminal, Palabre uses the TUI by default. `palabre` opens the home screen, creates the global config on first launch when needed, and refreshes detected known agents before showing the UI. `/ask` switches from debate to independent answers, `/agents` and `/roles` help you choose the active setup, `/history` shows recent exports, and `/home` returns to the home screen. `--terminal` forces the older raw rendering suitable for logs. `palabre init` remains available for explicit setup, especially with `--local`.
|
|
58
57
|
|
|
59
58
|
### Supported Agents
|
|
60
59
|
|
|
61
60
|
- Claude Code via `claude --print`
|
|
62
61
|
- Codex CLI via `codex exec`
|
|
63
|
-
- Gemini CLI via `gemini --prompt -`
|
|
64
62
|
- Antigravity CLI via `agy --print` in a pseudo-terminal
|
|
65
63
|
- OpenCode via `opencode run`
|
|
66
|
-
- Mistral Vibe via `vibe --output text --
|
|
64
|
+
- Mistral Vibe via `vibe --output text --trust --prompt`
|
|
67
65
|
- Ollama via the local HTTP API
|
|
68
66
|
|
|
69
67
|
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.
|
|
@@ -80,7 +78,7 @@ The NDJSON v1 stream is treated as a public integration API. Compatible addition
|
|
|
80
78
|
|
|
81
79
|
### Skill for AI agents
|
|
82
80
|
|
|
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,
|
|
81
|
+
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, and any skills-compatible agent.
|
|
84
82
|
|
|
85
83
|
Install it in **Hermes Agent**:
|
|
86
84
|
|
|
@@ -94,7 +92,7 @@ The skill is versioned under [skills/palabre](./skills/palabre).
|
|
|
94
92
|
|
|
95
93
|
### Privacy
|
|
96
94
|
|
|
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,
|
|
95
|
+
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, Antigravity CLI, OpenCode, Mistral Vibe, Ollama, or any custom agent you configure.
|
|
98
96
|
|
|
99
97
|
If an agent fails during the debate or final summary, PALABRE keeps the partial Markdown export with an interruption section whenever possible.
|
|
100
98
|
|
|
@@ -119,9 +117,11 @@ Public roadmap: [docs/guide/fr/roadmap.md](./docs/guide/fr/roadmap.md). Changes:
|
|
|
119
117
|
|
|
120
118
|
MIT. See [LICENSE](./LICENSE).
|
|
121
119
|
|
|
120
|
+

|
|
121
|
+
|
|
122
122
|
## Français
|
|
123
123
|
|
|
124
|
-
PALABRE est un orchestrateur CLI/TUI qui fait travailler plusieurs agents IA installés sur votre machine : Claude Code, Codex CLI,
|
|
124
|
+
PALABRE est un orchestrateur CLI/TUI qui fait travailler plusieurs agents IA installés sur votre machine : Claude Code, Codex CLI, Antigravity CLI, OpenCode, Mistral Vibe et Ollama.
|
|
125
125
|
|
|
126
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.
|
|
127
127
|
|
|
@@ -145,7 +145,6 @@ palabre --help
|
|
|
145
145
|
### Démarrage rapide
|
|
146
146
|
|
|
147
147
|
```bash
|
|
148
|
-
palabre init
|
|
149
148
|
palabre doctor
|
|
150
149
|
palabre
|
|
151
150
|
```
|
|
@@ -162,16 +161,15 @@ palabre codex-claude "Preview" --context src --show-prompt
|
|
|
162
161
|
palabre context scan src docs --json
|
|
163
162
|
```
|
|
164
163
|
|
|
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.
|
|
164
|
+
Dans un terminal interactif, Palabre utilise l'interface TUI par défaut. `palabre` ouvre l'accueil, crée la config globale au premier lancement si nécessaire, et rafraîchit les agents connus détectés avant d'afficher l'interface. `/ask` passe du débat aux réponses indépendantes, `/agents` et `/roles` aident à choisir la configuration courante, `/history` affiche les derniers exports, et `/home` revient à l'accueil. `--terminal` force l'ancien rendu brut adapté aux logs. `palabre init` reste disponible pour un setup explicite, notamment avec `--local`.
|
|
166
165
|
|
|
167
166
|
### Agents supportés
|
|
168
167
|
|
|
169
168
|
- Claude Code via `claude --print`
|
|
170
169
|
- Codex CLI via `codex exec`
|
|
171
|
-
- Gemini CLI via `gemini --prompt -`
|
|
172
170
|
- Antigravity CLI via `agy --print` en pseudo-terminal
|
|
173
171
|
- OpenCode via `opencode run`
|
|
174
|
-
- Mistral Vibe via `vibe --output text --
|
|
172
|
+
- Mistral Vibe via `vibe --output text --trust --prompt`
|
|
175
173
|
- Ollama via l'API locale HTTP
|
|
176
174
|
|
|
177
175
|
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é.
|
|
@@ -188,7 +186,7 @@ Le flux NDJSON v1 est traité comme une API publique d'intégration. Les ajouts
|
|
|
188
186
|
|
|
189
187
|
### Skill pour agents IA
|
|
190
188
|
|
|
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
|
|
189
|
+
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 et tout agent compatible skills.
|
|
192
190
|
|
|
193
191
|
Installation dans **Hermes Agent** :
|
|
194
192
|
|
|
@@ -202,7 +200,7 @@ Le skill est versionné dans [skills/palabre](./skills/palabre).
|
|
|
202
200
|
|
|
203
201
|
### Confidentialité
|
|
204
202
|
|
|
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,
|
|
203
|
+
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, Antigravity CLI, OpenCode, Mistral Vibe, Ollama ou de tout autre agent configuré.
|
|
206
204
|
|
|
207
205
|
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
206
|
|
package/dist/adapters/cli-pty.js
CHANGED
package/dist/adapters/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { formatAgentPrompt } from "../prompt.js";
|
|
|
4
4
|
import { DEFAULT_MAX_OUTPUT_BYTES, DEFAULT_TIMEOUT_MS, withModelArgs } from "./cli-shared.js";
|
|
5
5
|
import { cleanTerminalOutput } from "./terminal.js";
|
|
6
6
|
/**
|
|
7
|
-
* Adapter pour les CLIs batch (Codex, Claude,
|
|
7
|
+
* Adapter pour les CLIs batch (Codex, Claude, OpenCode, Vibe...).
|
|
8
8
|
* Lance un sous-processus, injecte le prompt via stdin ou argument, capture stdout.
|
|
9
9
|
* Garantit : rejection des sorties vides (sauf `allowEmptyOutput`), des timeouts et des exit codes non nuls sans stdout.
|
|
10
10
|
*/
|
|
@@ -47,7 +47,8 @@ export class CliAdapter {
|
|
|
47
47
|
? [...baseArgs, renderedPrompt]
|
|
48
48
|
: baseArgs;
|
|
49
49
|
return new Promise((resolve, reject) => {
|
|
50
|
-
const
|
|
50
|
+
const spawnCommand = shellCommandForSpawn(this.config.command, args, this.config.shell ?? false);
|
|
51
|
+
const child = spawn(spawnCommand.command, spawnCommand.args, {
|
|
51
52
|
stdio: ["pipe", "pipe", "pipe"],
|
|
52
53
|
shell: this.config.shell ?? false
|
|
53
54
|
});
|
|
@@ -147,7 +148,7 @@ export class CliAdapter {
|
|
|
147
148
|
}));
|
|
148
149
|
});
|
|
149
150
|
const finishFromExitCode = (code) => {
|
|
150
|
-
if (code && code !== 0
|
|
151
|
+
if (code && code !== 0) {
|
|
151
152
|
finish(createCliExitError(this.name, code, stderr));
|
|
152
153
|
return;
|
|
153
154
|
}
|
|
@@ -307,6 +308,35 @@ function clipLine(value, maxLength) {
|
|
|
307
308
|
? value
|
|
308
309
|
: `${value.slice(0, maxLength - 1)}…`;
|
|
309
310
|
}
|
|
311
|
+
function shellCommandForSpawn(command, args, shell) {
|
|
312
|
+
if (!shell) {
|
|
313
|
+
return { command, args };
|
|
314
|
+
}
|
|
315
|
+
if (process.platform !== "win32") {
|
|
316
|
+
return {
|
|
317
|
+
command: [command, ...args].map(quotePosixShellArg).join(" "),
|
|
318
|
+
args: []
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
command: [command, ...args].map(quoteWindowsShellArg).join(" "),
|
|
323
|
+
args: []
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function quoteWindowsShellArg(value) {
|
|
327
|
+
if (value.length === 0) {
|
|
328
|
+
return "\"\"";
|
|
329
|
+
}
|
|
330
|
+
return `"${value
|
|
331
|
+
.replace(/(\\*)"/g, "$1$1\\\"")
|
|
332
|
+
.replace(/(\\+)$/g, "$1$1")}"`;
|
|
333
|
+
}
|
|
334
|
+
function quotePosixShellArg(value) {
|
|
335
|
+
if (value.length === 0) {
|
|
336
|
+
return "''";
|
|
337
|
+
}
|
|
338
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
339
|
+
}
|
|
310
340
|
function cancelledError(adapterName) {
|
|
311
341
|
return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
|
|
312
342
|
}
|
package/dist/adapters/ollama.js
CHANGED
|
@@ -116,7 +116,7 @@ export class OllamaAdapter {
|
|
|
116
116
|
throw new AdapterError("model-unavailable", this.name, `Modele Ollama indisponible: ${this.config.model}. Modeles detectes: ${models.join(", ") || "aucun"}. ` +
|
|
117
117
|
"Utilise --pull-models ou autoPullModel: true pour autoriser le telechargement.", { model: this.config.model, availableModels: models });
|
|
118
118
|
}
|
|
119
|
-
process.
|
|
119
|
+
process.stderr.write(`\n[ollama] Modele absent, telechargement: ${this.config.model}\n`);
|
|
120
120
|
await this.pullModel(baseUrl);
|
|
121
121
|
if (!(await this.isModelAvailable(baseUrl))) {
|
|
122
122
|
throw new AdapterError("model-pull-failed", this.name, `Le modele Ollama ${this.config.model} reste indisponible apres telechargement.`);
|
package/dist/agentRegistry.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
const KNOWN_CLI_AGENTS = [
|
|
7
7
|
{ configKey: "codex", commandAliases: ["codex"], discoveryKey: "codex" },
|
|
8
8
|
{ configKey: "claude", commandAliases: ["claude"], discoveryKey: "claude" },
|
|
9
|
-
{ configKey: "gemini", commandAliases: ["gemini"], discoveryKey: "gemini" },
|
|
10
9
|
{ configKey: "antigravity", commandAliases: ["agy", "antigravity"], discoveryKey: "antigravity" },
|
|
11
10
|
{ configKey: "opencode", commandAliases: ["opencode"], discoveryKey: "opencode" },
|
|
12
11
|
{ configKey: "vibe", commandAliases: ["vibe"], discoveryKey: "vibe" }
|
|
@@ -37,7 +36,7 @@ export function detectionForCommand(command, discovery) {
|
|
|
37
36
|
}
|
|
38
37
|
/**
|
|
39
38
|
* Liste les clés d'agents connus effectivement détectés localement, dans
|
|
40
|
-
* l'ordre canonique (`codex`, `claude`, `
|
|
39
|
+
* l'ordre canonique (`codex`, `claude`, `antigravity`, `opencode`, `vibe`,
|
|
41
40
|
* puis `ollama-local`).
|
|
42
41
|
*/
|
|
43
42
|
export function detectedAgentNames(discovery) {
|
package/dist/args.js
CHANGED
|
@@ -45,8 +45,8 @@ const FLAG_SPECS = {
|
|
|
45
45
|
turns: { arity: "single" },
|
|
46
46
|
renderer: { arity: "single" },
|
|
47
47
|
// Valeurs multiples.
|
|
48
|
-
agents: { arity: "multi"
|
|
49
|
-
"ask-agents": { arity: "multi"
|
|
48
|
+
agents: { arity: "multi" },
|
|
49
|
+
"ask-agents": { arity: "multi" },
|
|
50
50
|
"set-defaults": { arity: "multi", max: 2 },
|
|
51
51
|
files: { arity: "multi" },
|
|
52
52
|
context: { arity: "multi" }
|
|
@@ -67,6 +67,8 @@ const COMMANDS = new Set([
|
|
|
67
67
|
"agents",
|
|
68
68
|
"preset",
|
|
69
69
|
"presets",
|
|
70
|
+
"history",
|
|
71
|
+
"historique",
|
|
70
72
|
"context"
|
|
71
73
|
]);
|
|
72
74
|
/**
|
package/dist/config.js
CHANGED
|
@@ -54,23 +54,6 @@ export const exampleConfig = {
|
|
|
54
54
|
role: "reviewer",
|
|
55
55
|
tier: "primary"
|
|
56
56
|
},
|
|
57
|
-
gemini: {
|
|
58
|
-
type: "cli",
|
|
59
|
-
command: "gemini",
|
|
60
|
-
args: [
|
|
61
|
-
"--output-format",
|
|
62
|
-
"text",
|
|
63
|
-
"--approval-mode",
|
|
64
|
-
"plan",
|
|
65
|
-
"--skip-trust",
|
|
66
|
-
"--prompt",
|
|
67
|
-
"-"
|
|
68
|
-
],
|
|
69
|
-
promptMode: "stdin",
|
|
70
|
-
shell: process.platform === "win32",
|
|
71
|
-
role: "reviewer",
|
|
72
|
-
tier: "primary"
|
|
73
|
-
},
|
|
74
57
|
antigravity: {
|
|
75
58
|
type: "cli-pty",
|
|
76
59
|
command: "agy",
|
|
@@ -102,8 +85,6 @@ export const exampleConfig = {
|
|
|
102
85
|
args: [
|
|
103
86
|
"--output",
|
|
104
87
|
"text",
|
|
105
|
-
"--agent",
|
|
106
|
-
"plan",
|
|
107
88
|
"--trust",
|
|
108
89
|
"--prompt"
|
|
109
90
|
],
|
|
@@ -234,11 +215,34 @@ export function syncDetectedAgents(config, discovery) {
|
|
|
234
215
|
const discoveredConfig = createConfigFromDiscovery(discovery);
|
|
235
216
|
const missingAgents = detectedAgentNames(discovery).filter((agentName) => !config.agents[agentName]);
|
|
236
217
|
applyDetectedCommands(config, discovery);
|
|
218
|
+
migrateKnownAgentDefaults(config);
|
|
237
219
|
for (const agentName of missingAgents) {
|
|
238
220
|
config.agents[agentName] = discoveredConfig.agents[agentName];
|
|
239
221
|
}
|
|
240
222
|
return missingAgents;
|
|
241
223
|
}
|
|
224
|
+
function migrateKnownAgentDefaults(config) {
|
|
225
|
+
migrateVibePlanAgent(config);
|
|
226
|
+
}
|
|
227
|
+
function migrateVibePlanAgent(config) {
|
|
228
|
+
const agent = config.agents.vibe;
|
|
229
|
+
if (agent?.type !== "cli" || !isLegacyVibePlanArgs(agent.args)) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
agent.args = ["--output", "text", "--trust", "--prompt"];
|
|
233
|
+
}
|
|
234
|
+
function isLegacyVibePlanArgs(args) {
|
|
235
|
+
return JSON.stringify(args) === JSON.stringify(["--output", "text", "--agent", "plan", "--trust", "--prompt"]);
|
|
236
|
+
}
|
|
237
|
+
export function syncDetectedAgentsDetailed(config, discovery) {
|
|
238
|
+
const before = JSON.stringify(config.agents);
|
|
239
|
+
const addedAgents = syncDetectedAgents(config, discovery);
|
|
240
|
+
const changed = JSON.stringify(config.agents) !== before;
|
|
241
|
+
return {
|
|
242
|
+
addedAgents,
|
|
243
|
+
changed
|
|
244
|
+
};
|
|
245
|
+
}
|
|
242
246
|
export function syncOllamaModel(config, discovery) {
|
|
243
247
|
const agent = config.agents["ollama-local"];
|
|
244
248
|
if (agent?.type !== "ollama" || discovery.ollama.models.length === 0) {
|
|
@@ -279,7 +283,7 @@ function chooseDefaultOllamaModel(discovery) {
|
|
|
279
283
|
return discovery.ollama.models[0] ?? DEFAULT_OLLAMA_MODEL;
|
|
280
284
|
}
|
|
281
285
|
function chooseDefaultSummaryAgent(pair) {
|
|
282
|
-
for (const preferred of ["claude", "codex", "antigravity", "vibe"
|
|
286
|
+
for (const preferred of ["claude", "codex", "antigravity", "vibe"]) {
|
|
283
287
|
if (pair.includes(preferred)) {
|
|
284
288
|
return preferred;
|
|
285
289
|
}
|
|
@@ -305,16 +309,12 @@ function chooseDefaultPair(discovery) {
|
|
|
305
309
|
if (discovery.antigravity.available && discovery.ollama.available) {
|
|
306
310
|
return ["antigravity", "ollama-local"];
|
|
307
311
|
}
|
|
308
|
-
if (discovery.gemini.available && discovery.ollama.available) {
|
|
309
|
-
return ["gemini", "ollama-local"];
|
|
310
|
-
}
|
|
311
312
|
const cliAgents = [
|
|
312
313
|
discovery.codex.available ? "codex" : undefined,
|
|
313
314
|
discovery.claude.available ? "claude" : undefined,
|
|
314
315
|
discovery.antigravity.available ? "antigravity" : undefined,
|
|
315
316
|
discovery.opencode.available ? "opencode" : undefined,
|
|
316
|
-
discovery.vibe.available ? "vibe" : undefined
|
|
317
|
-
discovery.gemini.available ? "gemini" : undefined
|
|
317
|
+
discovery.vibe.available ? "vibe" : undefined
|
|
318
318
|
].filter((agent) => Boolean(agent));
|
|
319
319
|
if (cliAgents.length >= 2) {
|
|
320
320
|
return [cliAgents[0], cliAgents[1]];
|
package/dist/context.js
CHANGED
|
@@ -80,7 +80,11 @@ async function addContextPaths(paths, cwd, state) {
|
|
|
80
80
|
const uniquePaths = [...new Set(paths.map((item) => item.trim()).filter(Boolean))];
|
|
81
81
|
for (const inputPath of uniquePaths) {
|
|
82
82
|
const absolutePath = path.resolve(cwd, inputPath);
|
|
83
|
-
const fileStat = await stat(absolutePath);
|
|
83
|
+
const fileStat = await stat(absolutePath).catch(() => undefined);
|
|
84
|
+
if (!fileStat) {
|
|
85
|
+
state.warnings.push(state.messages.context.ignoredNotFileOrDirectory(inputPath));
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
84
88
|
if (fileStat.isFile()) {
|
|
85
89
|
await addContextFile(absolutePath, cwd, state);
|
|
86
90
|
continue;
|
package/dist/discovery.js
CHANGED
|
@@ -7,10 +7,9 @@ import { executableExtensions } from "./exec.js";
|
|
|
7
7
|
* Antigravity est exposé selon les installations sous `agy` ou `antigravity`.
|
|
8
8
|
*/
|
|
9
9
|
export async function discoverLocalTools() {
|
|
10
|
-
const [codex, claude,
|
|
10
|
+
const [codex, claude, antigravity, opencode, vibe, ollamaCommand] = await Promise.all([
|
|
11
11
|
detectCommand("codex"),
|
|
12
12
|
detectFirstCommand(process.platform === "win32" ? ["claude.exe", "claude"] : ["claude"]),
|
|
13
|
-
detectCommand("gemini"),
|
|
14
13
|
detectFirstCommand(["agy", "antigravity"]),
|
|
15
14
|
detectCommand("opencode"),
|
|
16
15
|
detectCommand("vibe"),
|
|
@@ -20,7 +19,6 @@ export async function discoverLocalTools() {
|
|
|
20
19
|
return {
|
|
21
20
|
codex,
|
|
22
21
|
claude,
|
|
23
|
-
gemini,
|
|
24
22
|
antigravity,
|
|
25
23
|
opencode,
|
|
26
24
|
vibe,
|
package/dist/doctor.js
CHANGED
|
@@ -43,7 +43,6 @@ export async function runDoctor(explicitConfigPath, plain = false, explicitLangu
|
|
|
43
43
|
lines.push(info(t.doctor.localTools, "tools"));
|
|
44
44
|
lines.push(formatCommand("Codex CLI", discovery.codex.available, discovery.codex.command, discovery.codex.path, t));
|
|
45
45
|
lines.push(formatCommand("Claude CLI", discovery.claude.available, discovery.claude.command, discovery.claude.path, t));
|
|
46
|
-
lines.push(formatCommand("Gemini CLI", discovery.gemini.available, discovery.gemini.command, discovery.gemini.path, t));
|
|
47
46
|
lines.push(formatCommand("Antigravity CLI", discovery.antigravity.available, discovery.antigravity.command, discovery.antigravity.path, t));
|
|
48
47
|
lines.push(formatCommand("OpenCode CLI", discovery.opencode.available, discovery.opencode.command, discovery.opencode.path, t));
|
|
49
48
|
lines.push(formatCommand("Mistral Vibe CLI", discovery.vibe.available, discovery.vibe.command, discovery.vibe.path, t));
|
package/dist/history.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const maxHeaderBytes = 12_000;
|
|
4
|
+
export async function listHistoryEntries(outputDir, limit = 10) {
|
|
5
|
+
const resolved = path.resolve(outputDir);
|
|
6
|
+
let entries;
|
|
7
|
+
try {
|
|
8
|
+
entries = await readdir(resolved, { withFileTypes: true });
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
const markdownFiles = entries
|
|
14
|
+
.filter((entry) => entry.isFile() && /\.(debate|ask)\.md$/i.test(entry.name))
|
|
15
|
+
.map((entry) => path.join(resolved, entry.name));
|
|
16
|
+
const history = await Promise.all(markdownFiles.map(readHistoryFile));
|
|
17
|
+
return history
|
|
18
|
+
.filter((entry) => Boolean(entry))
|
|
19
|
+
.sort((left, right) => right.mtimeMs - left.mtimeMs)
|
|
20
|
+
.slice(0, limit);
|
|
21
|
+
}
|
|
22
|
+
async function readHistoryFile(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
const [metadata, raw] = await Promise.all([
|
|
25
|
+
stat(filePath),
|
|
26
|
+
readFile(filePath, "utf8")
|
|
27
|
+
]);
|
|
28
|
+
const header = raw.slice(0, maxHeaderBytes);
|
|
29
|
+
const table = parseMetadataTable(header);
|
|
30
|
+
const fileName = path.basename(filePath);
|
|
31
|
+
const mode = fileName.endsWith(".ask.md") ? "ask" : "debate";
|
|
32
|
+
return {
|
|
33
|
+
fileName,
|
|
34
|
+
path: filePath,
|
|
35
|
+
mode,
|
|
36
|
+
topic: table.Sujet ?? table.Subject ?? topicFromFileName(fileName),
|
|
37
|
+
agents: table.Agents ?? "",
|
|
38
|
+
date: table["Date locale"] ?? table["Local date"] ?? table["Session demarree a"] ?? table["Session started at"] ?? "",
|
|
39
|
+
count: countFromTable(mode, table),
|
|
40
|
+
mtimeMs: metadata.mtimeMs
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function countFromTable(mode, table) {
|
|
48
|
+
if (mode === "ask") {
|
|
49
|
+
const received = table["Reponses recues"] ?? table["Received responses"];
|
|
50
|
+
const requested = table["Reponses attendues"] ?? table["Expected responses"];
|
|
51
|
+
return received && requested ? `${received}/${requested}` : received ?? requested ?? "";
|
|
52
|
+
}
|
|
53
|
+
const played = table["Tours joues"] ?? table["Played turns"];
|
|
54
|
+
const requested = table["Tours demandes"] ?? table["Requested turns"];
|
|
55
|
+
return played && requested ? `${played}/${requested}` : played ?? requested ?? "";
|
|
56
|
+
}
|
|
57
|
+
function parseMetadataTable(markdown) {
|
|
58
|
+
const fields = {};
|
|
59
|
+
const lines = markdown.split(/\r?\n/);
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
const match = line.match(/^\|\s*([^|]+?)\s*\|\s*([^|]*?)\s*\|$/);
|
|
62
|
+
if (!match)
|
|
63
|
+
continue;
|
|
64
|
+
const key = stripMarkdown(match[1] ?? "");
|
|
65
|
+
const value = stripMarkdown(match[2] ?? "");
|
|
66
|
+
if (!key || key === "Champ" || key === "Field" || /^-+$/.test(key))
|
|
67
|
+
continue;
|
|
68
|
+
fields[key] = value;
|
|
69
|
+
}
|
|
70
|
+
return fields;
|
|
71
|
+
}
|
|
72
|
+
function stripMarkdown(value) {
|
|
73
|
+
return value
|
|
74
|
+
.replace(/\*\*/g, "")
|
|
75
|
+
.replace(/`/g, "")
|
|
76
|
+
.trim();
|
|
77
|
+
}
|
|
78
|
+
function topicFromFileName(fileName) {
|
|
79
|
+
return fileName
|
|
80
|
+
.replace(/^palabre-/, "")
|
|
81
|
+
.replace(/\.(debate|ask)\.md$/i, "")
|
|
82
|
+
.replace(/-\d{4}-\d{2}-\d{2}t.*$/i, "")
|
|
83
|
+
.replace(/-/g, " ")
|
|
84
|
+
.trim() || fileName;
|
|
85
|
+
}
|