palabre 0.5.0 → 0.6.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
@@ -13,7 +13,7 @@
13
13
 
14
14
  ## Français
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, OpenCode et Ollama.
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.
17
17
 
18
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.
19
19
 
@@ -57,6 +57,7 @@ palabre codex-claude "Preview" --context src --show-prompt
57
57
  - Claude Code via `claude --print`
58
58
  - Codex CLI via `codex exec`
59
59
  - Gemini CLI via `gemini --prompt -`
60
+ - Antigravity CLI via `agy --print` en pseudo-terminal
60
61
  - OpenCode via `opencode run`
61
62
  - Ollama via l'API locale HTTP
62
63
 
@@ -64,7 +65,7 @@ PALABRE ne liste pas les modèles : ils changent souvent et dépendent de chaque
64
65
 
65
66
  ### Confidentialité
66
67
 
67
- 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, OpenCode, Ollama ou de tout autre agent configuré.
68
+ 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é.
68
69
 
69
70
  ### Développement local
70
71
 
@@ -87,7 +88,7 @@ MIT. Voir [LICENSE](./LICENSE).
87
88
 
88
89
  ## English
89
90
 
90
- PALABRE is a CLI orchestrator that lets multiple AI agents installed on your machine talk to each other: Claude Code, Codex CLI, Gemini CLI, OpenCode, and Ollama.
91
+ 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.
91
92
 
92
93
  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.
93
94
 
@@ -131,6 +132,7 @@ palabre codex-claude "Preview" --context src --show-prompt
131
132
  - Claude Code via `claude --print`
132
133
  - Codex CLI via `codex exec`
133
134
  - Gemini CLI via `gemini --prompt -`
135
+ - Antigravity CLI via `agy --print` in a pseudo-terminal
134
136
  - OpenCode via `opencode run`
135
137
  - Ollama via the local HTTP API
136
138
 
@@ -138,7 +140,7 @@ PALABRE does not list models: they change often and depend on each CLI or user a
138
140
 
139
141
  ### Privacy
140
142
 
141
- 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, OpenCode, Ollama, or any custom agent you configure.
143
+ 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.
142
144
 
143
145
  ### Local Development
144
146
 
@@ -0,0 +1,183 @@
1
+ import { spawn as spawnPty } from "node-pty";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { AdapterError } from "../errors.js";
5
+ import { formatAgentPrompt } from "../prompt.js";
6
+ import { cleanTerminalOutput } from "./terminal.js";
7
+ /**
8
+ * Adapter pour les CLIs qui exigent un vrai terminal.
9
+ * Contrairement à `CliAdapter`, stdout/stderr sont fusionnés dans le flux PTY.
10
+ */
11
+ export class CliPtyAdapter {
12
+ name;
13
+ config;
14
+ role;
15
+ contract;
16
+ constructor(name, config) {
17
+ this.name = name;
18
+ this.config = config;
19
+ this.role = config.role;
20
+ this.contract = {
21
+ name,
22
+ kind: "cli-pty",
23
+ capabilities: {
24
+ mode: "pty",
25
+ supportsModelOverride: true,
26
+ supportsFilesystemAccess: true,
27
+ supportsStreaming: false,
28
+ supportsProcessExitCode: true,
29
+ supportsStderr: false
30
+ },
31
+ guarantees: {
32
+ rejectsEmptyOutput: !config.allowEmptyOutput,
33
+ rejectsNonZeroExit: true,
34
+ rejectsTimeout: true,
35
+ returnsRawOutput: true
36
+ }
37
+ };
38
+ }
39
+ async generate(prompt) {
40
+ const renderedPrompt = formatAgentPrompt(prompt);
41
+ const promptMode = this.config.promptMode ?? "stdin";
42
+ const baseArgs = withModelArgs(this.config.args ?? [], this.config.model, this.config.modelArg ?? "--model");
43
+ const args = promptMode === "argument"
44
+ ? [...baseArgs, renderedPrompt]
45
+ : baseArgs;
46
+ return new Promise((resolve, reject) => {
47
+ let output = "";
48
+ let settled = false;
49
+ let hardTimer;
50
+ let term;
51
+ let dataSubscription;
52
+ let exitSubscription;
53
+ const finish = (error, exitCode, kill = true) => {
54
+ if (settled)
55
+ return;
56
+ settled = true;
57
+ clearTimeout(hardTimer);
58
+ dataSubscription?.dispose();
59
+ exitSubscription?.dispose();
60
+ if (kill) {
61
+ try {
62
+ term.kill();
63
+ }
64
+ catch {
65
+ // The PTY may already be closed.
66
+ }
67
+ }
68
+ cleanupPty(term);
69
+ if (error) {
70
+ reject(error);
71
+ return;
72
+ }
73
+ const content = cleanTerminalOutput(output);
74
+ if (exitCode && exitCode !== 0 && !content) {
75
+ reject(createPtyExitError(this.name, exitCode, output));
76
+ return;
77
+ }
78
+ if (!content && !this.config.allowEmptyOutput) {
79
+ reject(new AdapterError("empty-output", this.name, `${this.name} produced empty PTY output.`, {
80
+ raw: output
81
+ }));
82
+ return;
83
+ }
84
+ resolve({
85
+ content,
86
+ raw: output
87
+ });
88
+ };
89
+ try {
90
+ term = spawnPty(resolveExecutable(this.config.command), args, {
91
+ name: "xterm-256color",
92
+ cols: this.config.cols ?? 120,
93
+ rows: this.config.rows ?? 40,
94
+ cwd: process.cwd(),
95
+ env: process.env,
96
+ ...(process.platform !== "win32" ? { encoding: "utf8" } : {}),
97
+ ...(process.platform === "win32" ? { useConpty: true } : {})
98
+ });
99
+ }
100
+ catch (error) {
101
+ reject(new AdapterError("spawn-failed", this.name, `${this.name} failed to start PTY command "${this.config.command}": ${error instanceof Error ? error.message : String(error)}`, {
102
+ command: this.config.command
103
+ }));
104
+ return;
105
+ }
106
+ hardTimer = setTimeout(() => {
107
+ finish(new AdapterError("timeout", this.name, `${this.name} timed out after ${this.config.timeoutMs ?? 180_000}ms`, {
108
+ timeoutMs: this.config.timeoutMs ?? 180_000
109
+ }));
110
+ }, this.config.timeoutMs ?? 180_000);
111
+ dataSubscription = term.onData((chunk) => {
112
+ output += chunk;
113
+ });
114
+ exitSubscription = term.onExit(({ exitCode }) => {
115
+ finish(undefined, exitCode, false);
116
+ });
117
+ if (promptMode === "stdin") {
118
+ term.write(`${renderedPrompt}\r`);
119
+ }
120
+ });
121
+ }
122
+ }
123
+ function resolveExecutable(command) {
124
+ if (path.isAbsolute(command) || command.includes("\\") || command.includes("/")) {
125
+ return command;
126
+ }
127
+ for (const directory of (process.env.PATH ?? "").split(path.delimiter)) {
128
+ const trimmed = directory.trim();
129
+ if (!trimmed)
130
+ continue;
131
+ for (const extension of executableExtensions(command)) {
132
+ const candidate = path.join(trimmed, `${command}${extension}`);
133
+ if (existsSync(candidate)) {
134
+ return candidate;
135
+ }
136
+ }
137
+ }
138
+ return command;
139
+ }
140
+ function cleanupPty(term) {
141
+ const maybeTerm = term;
142
+ try {
143
+ maybeTerm._agent?._cleanUpProcess?.();
144
+ maybeTerm._agent?._conoutSocketWorker?._worker?.terminate?.();
145
+ }
146
+ catch {
147
+ // Best-effort cleanup for Windows ConPTY internals.
148
+ }
149
+ }
150
+ function executableExtensions(command) {
151
+ if (path.extname(command) || process.platform !== "win32") {
152
+ return [""];
153
+ }
154
+ return (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD")
155
+ .split(";")
156
+ .map((extension) => extension.toLowerCase())
157
+ .concat(".ps1", "");
158
+ }
159
+ function withModelArgs(args, model, modelArg) {
160
+ if (!model) {
161
+ return [...args];
162
+ }
163
+ const promptStdinIndex = args.lastIndexOf("-");
164
+ if (promptStdinIndex === args.length - 1) {
165
+ return [
166
+ ...args.slice(0, promptStdinIndex),
167
+ modelArg,
168
+ model,
169
+ ...args.slice(promptStdinIndex)
170
+ ];
171
+ }
172
+ return [...args, modelArg, model];
173
+ }
174
+ function createPtyExitError(adapterName, exitCode, raw) {
175
+ return new AdapterError("non-zero-exit", adapterName, `${adapterName} exited with code ${exitCode}: ${summarizePtyOutput(raw)}`, {
176
+ exitCode,
177
+ raw
178
+ });
179
+ }
180
+ function summarizePtyOutput(output) {
181
+ const cleaned = cleanTerminalOutput(output);
182
+ return cleaned ? cleaned.slice(-1_200) : "aucune sortie PTY capturee.";
183
+ }
@@ -1,6 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { AdapterError } from "../errors.js";
3
3
  import { formatAgentPrompt } from "../prompt.js";
4
+ import { cleanTerminalOutput } from "./terminal.js";
4
5
  /**
5
6
  * Adapter pour les CLIs batch (Codex, Claude, Gemini…).
6
7
  * Lance un sous-processus, injecte le prompt via stdin ou argument, capture stdout.
@@ -107,17 +108,18 @@ export class CliAdapter {
107
108
  command: this.config.command
108
109
  }));
109
110
  });
110
- child.on("close", (code) => {
111
+ const finishFromExitCode = (code) => {
111
112
  if (code && code !== 0 && !stdout.trim()) {
112
113
  finish(createCliExitError(this.name, code, stderr));
113
114
  return;
114
115
  }
115
116
  finish();
116
- });
117
+ };
118
+ child.on("close", finishFromExitCode);
117
119
  if (promptMode === "stdin") {
118
120
  child.stdin.write(renderedPrompt);
119
- child.stdin.end();
120
121
  }
122
+ child.stdin.end();
121
123
  });
122
124
  }
123
125
  }
@@ -142,9 +144,7 @@ function withModelArgs(args, model, modelArg) {
142
144
  }
143
145
  /** Retire les séquences ANSI et les espaces en tête/fin. */
144
146
  function cleanCliOutput(output) {
145
- return output
146
- .replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "")
147
- .trim();
147
+ return cleanTerminalOutput(output);
148
148
  }
149
149
  /**
150
150
  * Construit une `AdapterError` typée depuis un exit code non nul.
@@ -1,10 +1,13 @@
1
1
  import { CliAdapter } from "./cli.js";
2
+ import { CliPtyAdapter } from "./cli-pty.js";
2
3
  import { OllamaAdapter } from "./ollama.js";
3
4
  /** Factory qui instancie l'adapter approprié selon `config.type`. Exhaustive : tout `AgentConfig` valide produit un adapter. */
4
5
  export function createAgent(name, config) {
5
6
  switch (config.type) {
6
7
  case "cli":
7
8
  return new CliAdapter(name, config);
9
+ case "cli-pty":
10
+ return new CliPtyAdapter(name, config);
8
11
  case "ollama":
9
12
  return new OllamaAdapter(name, config);
10
13
  }
@@ -0,0 +1,13 @@
1
+ /** Retire les séquences de contrôle ANSI/OSC et normalise les retours ligne d'une sortie terminal. */
2
+ export function cleanTerminalOutput(output) {
3
+ return output
4
+ .replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "")
5
+ .replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "")
6
+ .replace(/\u001b[()][A-Za-z0-9]/g, "")
7
+ .replace(/\u001b[=>]/g, "")
8
+ .replace(/\r\n/g, "\n")
9
+ .replace(/\r/g, "\n")
10
+ .replace(/\u0007/g, "")
11
+ .replace(/\u0000/g, "")
12
+ .trim();
13
+ }
package/dist/config.js CHANGED
@@ -66,6 +66,19 @@ export const exampleConfig = {
66
66
  role: "reviewer",
67
67
  tier: "primary"
68
68
  },
69
+ antigravity: {
70
+ type: "cli-pty",
71
+ command: "agy",
72
+ args: [
73
+ "--print-timeout",
74
+ "5m0s",
75
+ "--print"
76
+ ],
77
+ promptMode: "argument",
78
+ role: "reviewer",
79
+ tier: "primary",
80
+ timeoutMs: 300_000
81
+ },
69
82
  opencode: {
70
83
  type: "cli",
71
84
  command: "opencode",
@@ -155,6 +168,10 @@ export function createConfigFromDiscovery(discovery) {
155
168
  ...config.agents.gemini,
156
169
  ...(discovery.gemini.available ? { command: discovery.gemini.command } : {})
157
170
  };
171
+ config.agents.antigravity = {
172
+ ...config.agents.antigravity,
173
+ ...(discovery.antigravity.available ? { command: discovery.antigravity.command } : {})
174
+ };
158
175
  config.agents.opencode = {
159
176
  ...config.agents.opencode,
160
177
  ...(discovery.opencode.available ? { command: discovery.opencode.command } : {})
@@ -186,7 +203,7 @@ function chooseDefaultOllamaModel(discovery) {
186
203
  return discovery.ollama.models[0] ?? DEFAULT_OLLAMA_MODEL;
187
204
  }
188
205
  function chooseDefaultSummaryAgent(pair) {
189
- for (const preferred of ["claude", "codex", "gemini"]) {
206
+ for (const preferred of ["claude", "codex", "antigravity", "gemini"]) {
190
207
  if (pair.includes(preferred)) {
191
208
  return preferred;
192
209
  }
@@ -206,12 +223,16 @@ function chooseDefaultPair(discovery) {
206
223
  if (discovery.opencode.available && discovery.ollama.available) {
207
224
  return ["opencode", "ollama-local"];
208
225
  }
226
+ if (discovery.antigravity.available && discovery.ollama.available) {
227
+ return ["antigravity", "ollama-local"];
228
+ }
209
229
  if (discovery.gemini.available && discovery.ollama.available) {
210
230
  return ["gemini", "ollama-local"];
211
231
  }
212
232
  const cliAgents = [
213
233
  discovery.codex.available ? "codex" : undefined,
214
234
  discovery.claude.available ? "claude" : undefined,
235
+ discovery.antigravity.available ? "antigravity" : undefined,
215
236
  discovery.opencode.available ? "opencode" : undefined,
216
237
  discovery.gemini.available ? "gemini" : undefined
217
238
  ].filter((agent) => Boolean(agent));
package/dist/discovery.js CHANGED
@@ -5,10 +5,11 @@ import path from "node:path";
5
5
  * Sur Windows, tente `claude.exe` avant `claude`.
6
6
  */
7
7
  export async function discoverLocalTools() {
8
- const [codex, claude, gemini, opencode, ollamaCommand] = await Promise.all([
8
+ const [codex, claude, gemini, antigravity, opencode, ollamaCommand] = await Promise.all([
9
9
  detectCommand("codex"),
10
10
  detectFirstCommand(process.platform === "win32" ? ["claude.exe", "claude"] : ["claude"]),
11
11
  detectCommand("gemini"),
12
+ detectCommand("agy"),
12
13
  detectCommand("opencode"),
13
14
  detectCommand("ollama")
14
15
  ]);
@@ -17,6 +18,7 @@ export async function discoverLocalTools() {
17
18
  codex,
18
19
  claude,
19
20
  gemini,
21
+ antigravity,
20
22
  opencode,
21
23
  ollama: {
22
24
  ...ollamaServer,
package/dist/doctor.js CHANGED
@@ -41,6 +41,7 @@ export async function runDoctor(explicitConfigPath, plain = false, explicitLangu
41
41
  lines.push(formatCommand("Codex CLI", discovery.codex.available, discovery.codex.command, discovery.codex.path, t));
42
42
  lines.push(formatCommand("Claude CLI", discovery.claude.available, discovery.claude.command, discovery.claude.path, t));
43
43
  lines.push(formatCommand("Gemini CLI", discovery.gemini.available, discovery.gemini.command, discovery.gemini.path, t));
44
+ lines.push(formatCommand("Antigravity CLI", discovery.antigravity.available, discovery.antigravity.command, discovery.antigravity.path, t));
44
45
  lines.push(formatCommand("OpenCode CLI", discovery.opencode.available, discovery.opencode.command, discovery.opencode.path, t));
45
46
  lines.push(discovery.ollama.available
46
47
  ? ok(t.doctor.ollamaReachable(discovery.ollama.baseUrl, discovery.ollama.models.length))
@@ -156,7 +157,7 @@ function inspectAgents(config, discovery, lines, t) {
156
157
  lines.push(info(t.doctor.configuredAgents, "agents"));
157
158
  for (const [name, agent] of Object.entries(config.agents)) {
158
159
  inspectAgentShape(name, agent, lines, t);
159
- if (agent.type === "cli") {
160
+ if (agent.type === "cli" || agent.type === "cli-pty") {
160
161
  inspectCliAgent(name, agent, discovery, lines, t);
161
162
  continue;
162
163
  }
@@ -167,7 +168,7 @@ function inspectAgentShape(name, agent, lines, t) {
167
168
  if (!agent.role) {
168
169
  lines.push(error(t.doctor.roleMissing(name)));
169
170
  }
170
- if (agent.type === "cli") {
171
+ if (agent.type === "cli" || agent.type === "cli-pty") {
171
172
  if (!agent.command || !agent.command.trim()) {
172
173
  lines.push(error(t.doctor.cliCommandMissing(name)));
173
174
  }
@@ -223,6 +224,7 @@ function detectedAgentNames(discovery) {
223
224
  discovery.codex.available ? "codex" : undefined,
224
225
  discovery.claude.available ? "claude" : undefined,
225
226
  discovery.gemini.available ? "gemini" : undefined,
227
+ discovery.antigravity.available ? "antigravity" : undefined,
226
228
  discovery.opencode.available ? "opencode" : undefined,
227
229
  discovery.ollama.available ? "ollama-local" : undefined
228
230
  ].filter((name) => Boolean(name));
@@ -240,6 +242,10 @@ function knownCliDetection(command, discovery) {
240
242
  return discovery.claude;
241
243
  if (normalized === "gemini")
242
244
  return discovery.gemini;
245
+ if (normalized === "agy")
246
+ return discovery.antigravity;
247
+ if (normalized === "antigravity")
248
+ return discovery.antigravity;
243
249
  if (normalized === "opencode")
244
250
  return discovery.opencode;
245
251
  return undefined;
package/dist/index.js CHANGED
@@ -699,6 +699,7 @@ function findDetectedMissingAgents(config, discovery) {
699
699
  discovery.codex.available ? "codex" : undefined,
700
700
  discovery.claude.available ? "claude" : undefined,
701
701
  discovery.gemini.available ? "gemini" : undefined,
702
+ discovery.antigravity.available ? "antigravity" : undefined,
702
703
  discovery.opencode.available ? "opencode" : undefined,
703
704
  discovery.ollama.available ? "ollama-local" : undefined
704
705
  ].filter((agent) => Boolean(agent));
@@ -780,16 +781,20 @@ function formatAgentDetection(name, agentConfig, discovery, messages) {
780
781
  * @param discovery - Résultat de la découverte locale des outils.
781
782
  */
782
783
  function cliDetectionForAgent(name, agentConfig, discovery) {
783
- const command = normalizeCommandName(agentConfig.type === "cli" ? agentConfig.command : name);
784
+ const command = normalizeCommandName(agentConfig.type === "cli" || agentConfig.type === "cli-pty" ? agentConfig.command : name);
784
785
  if (command === "codex")
785
786
  return discovery.codex;
786
787
  if (command === "claude")
787
788
  return discovery.claude;
788
789
  if (command === "gemini")
789
790
  return discovery.gemini;
791
+ if (command === "agy")
792
+ return discovery.antigravity;
793
+ if (command === "antigravity")
794
+ return discovery.antigravity;
790
795
  if (command === "opencode")
791
796
  return discovery.opencode;
792
- return { available: true, command: agentConfig.type === "cli" ? agentConfig.command : name };
797
+ return { available: true, command: agentConfig.type === "cli" || agentConfig.type === "cli-pty" ? agentConfig.command : name };
793
798
  }
794
799
  /**
795
800
  * Extrait le nom de base d'une commande en supprimant le chemin et l'extension Windows éventuelle.
@@ -809,6 +814,7 @@ function printInitDiscovery(discovery, config, messages) {
809
814
  console.log(`- Codex CLI: ${formatCommandDetection(discovery.codex, messages)}`);
810
815
  console.log(`- Claude CLI: ${formatCommandDetection(discovery.claude, messages)}`);
811
816
  console.log(`- Gemini CLI: ${formatCommandDetection(discovery.gemini, messages)}`);
817
+ console.log(`- Antigravity CLI: ${formatCommandDetection(discovery.antigravity, messages)}`);
812
818
  console.log(`- OpenCode CLI: ${formatCommandDetection(discovery.opencode, messages)}`);
813
819
  console.log(`- Ollama API: ${formatOllamaDetection(discovery.ollama, messages)}`);
814
820
  console.log("");
@@ -822,6 +828,7 @@ function formatDetectedAgentSummary(discovery, language) {
822
828
  discovery.codex.available ? "codex" : undefined,
823
829
  discovery.claude.available ? "claude" : undefined,
824
830
  discovery.gemini.available ? "gemini" : undefined,
831
+ discovery.antigravity.available ? "antigravity" : undefined,
825
832
  discovery.opencode.available ? "opencode" : undefined,
826
833
  discovery.ollama.available ? "ollama-local" : undefined
827
834
  ].filter((name) => Boolean(name));
@@ -10,7 +10,7 @@ export const initMessages = {
10
10
  ollamaMissing: "non détecté",
11
11
  ollamaDetected: (modelCount) => `détectée (${modelCount} modèle${modelCount > 1 ? "s" : ""})`,
12
12
  defaults: (agentA, agentB) => `Défauts: ${agentA} <-> ${agentB}`,
13
- noDefaultPair: (detectedAgents) => `Défauts: ${detectedAgents}. Palabre a besoin d'au moins deux agents.\nAgents compatibles: Codex CLI, Claude CLI, Gemini CLI, OpenCode CLI, Ollama local.\nGuide: https://palab.re/fr/agents/overview`,
13
+ noDefaultPair: (detectedAgents) => `Défauts: ${detectedAgents}. Palabre a besoin d'au moins deux agents.\nAgents compatibles: Codex CLI, Claude CLI, Gemini CLI, Antigravity CLI, OpenCode CLI, Ollama local.\nGuide: https://palab.re/fr/agents/overview`,
14
14
  languageHint: (language) => `Langue: ${language}\nEnglish > palabre config --language en`
15
15
  },
16
16
  en: {
@@ -24,7 +24,7 @@ export const initMessages = {
24
24
  ollamaMissing: "not detected",
25
25
  ollamaDetected: (modelCount) => `detected (${modelCount} model${modelCount > 1 ? "s" : ""})`,
26
26
  defaults: (agentA, agentB) => `Defaults: ${agentA} <-> ${agentB}`,
27
- noDefaultPair: (detectedAgents) => `Defaults: ${detectedAgents}. Palabre needs at least two agents.\nCompatible agents: Codex CLI, Claude CLI, Gemini CLI, OpenCode CLI, local Ollama.\nGuide: https://palab.re/en/agents/overview`,
27
+ noDefaultPair: (detectedAgents) => `Defaults: ${detectedAgents}. Palabre needs at least two agents.\nCompatible agents: Codex CLI, Claude CLI, Gemini CLI, Antigravity CLI, OpenCode CLI, local Ollama.\nGuide: https://palab.re/en/agents/overview`,
28
28
  languageHint: (language) => `Language: ${language}\nFrançais > palabre config --language fr`
29
29
  }
30
30
  };
package/dist/new.js CHANGED
@@ -147,6 +147,10 @@ function isAgentDetected(name, config, discovery) {
147
147
  return discovery.claude.available;
148
148
  if (normalized === "gemini")
149
149
  return discovery.gemini.available;
150
+ if (normalized === "agy")
151
+ return discovery.antigravity.available;
152
+ if (normalized === "antigravity")
153
+ return discovery.antigravity.available;
150
154
  if (normalized === "opencode")
151
155
  return discovery.opencode.available;
152
156
  return true;
package/dist/presets.js CHANGED
@@ -16,6 +16,14 @@ const presets = {
16
16
  agentA: "opencode",
17
17
  agentB: "codex"
18
18
  },
19
+ "codex-antigravity": {
20
+ agentA: "codex",
21
+ agentB: "antigravity"
22
+ },
23
+ "antigravity-codex": {
24
+ agentA: "antigravity",
25
+ agentB: "codex"
26
+ },
19
27
  "claude-opencode": {
20
28
  agentA: "claude",
21
29
  agentB: "opencode"
@@ -24,6 +32,14 @@ const presets = {
24
32
  agentA: "opencode",
25
33
  agentB: "claude"
26
34
  },
35
+ "claude-antigravity": {
36
+ agentA: "claude",
37
+ agentB: "antigravity"
38
+ },
39
+ "antigravity-claude": {
40
+ agentA: "antigravity",
41
+ agentB: "claude"
42
+ },
27
43
  "gemini-opencode": {
28
44
  agentA: "gemini",
29
45
  agentB: "opencode"
@@ -32,6 +48,22 @@ const presets = {
32
48
  agentA: "opencode",
33
49
  agentB: "gemini"
34
50
  },
51
+ "gemini-antigravity": {
52
+ agentA: "gemini",
53
+ agentB: "antigravity"
54
+ },
55
+ "antigravity-gemini": {
56
+ agentA: "antigravity",
57
+ agentB: "gemini"
58
+ },
59
+ "opencode-antigravity": {
60
+ agentA: "opencode",
61
+ agentB: "antigravity"
62
+ },
63
+ "antigravity-opencode": {
64
+ agentA: "antigravity",
65
+ agentB: "opencode"
66
+ },
35
67
  "opencode-ollama": {
36
68
  agentA: "opencode",
37
69
  agentB: "ollama-local"
@@ -64,6 +96,14 @@ const presets = {
64
96
  agentA: "ollama-local",
65
97
  agentB: "gemini"
66
98
  },
99
+ "antigravity-ollama": {
100
+ agentA: "antigravity",
101
+ agentB: "ollama-local"
102
+ },
103
+ "ollama-antigravity": {
104
+ agentA: "ollama-local",
105
+ agentB: "antigravity"
106
+ },
67
107
  "codex-gemini": {
68
108
  agentA: "codex",
69
109
  agentB: "gemini"
@@ -159,6 +199,10 @@ function knownCliDetection(agent, discovery) {
159
199
  return discovery.claude;
160
200
  if (command === "gemini")
161
201
  return discovery.gemini;
202
+ if (command === "agy")
203
+ return discovery.antigravity;
204
+ if (command === "antigravity")
205
+ return discovery.antigravity;
162
206
  if (command === "opencode")
163
207
  return discovery.opencode;
164
208
  return undefined;
@@ -27,7 +27,7 @@ class PrettyConsoleRenderer {
27
27
  }
28
28
  /** Affiche l'en-tête du débat (sujet, agents, options). */
29
29
  start(options, agents = []) {
30
- const title = "PALABRE";
30
+ const title = "PALABRE CLI";
31
31
  process.stdout.write([
32
32
  "",
33
33
  this.c("cyan", `┌─ ${title} ${"─".repeat(Math.max(1, 54 - title.length))}`),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palabre",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Orchestrateur de debat entre agents IA locaux, CLIs et Ollama.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,6 +43,9 @@
43
43
  "engines": {
44
44
  "node": ">=20"
45
45
  },
46
+ "dependencies": {
47
+ "node-pty": "^1.1.0"
48
+ },
46
49
  "devDependencies": {
47
50
  "@types/node": "^20.12.0",
48
51
  "typescript": "^5.4.0"