palabre 0.3.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.
Files changed (41) hide show
  1. package/README.md +6 -4
  2. package/dist/adapters/cli-pty.js +183 -0
  3. package/dist/adapters/cli.js +6 -6
  4. package/dist/adapters/index.js +3 -0
  5. package/dist/adapters/terminal.js +13 -0
  6. package/dist/config.js +55 -8
  7. package/dist/configWizard.js +45 -40
  8. package/dist/context.js +16 -14
  9. package/dist/discovery.js +3 -1
  10. package/dist/doctor.js +147 -137
  11. package/dist/errors.js +4 -31
  12. package/dist/i18n.js +30 -0
  13. package/dist/index.js +275 -258
  14. package/dist/limits.js +11 -10
  15. package/dist/messages/adapter-errors.js +36 -0
  16. package/dist/messages/agents.js +38 -0
  17. package/dist/messages/common.js +28 -0
  18. package/dist/messages/config.js +88 -0
  19. package/dist/messages/context.js +24 -0
  20. package/dist/messages/doctor.js +126 -0
  21. package/dist/messages/help.js +280 -0
  22. package/dist/messages/index.js +38 -0
  23. package/dist/messages/init.js +30 -0
  24. package/dist/messages/limits.js +12 -0
  25. package/dist/messages/new.js +66 -0
  26. package/dist/messages/orchestrator.js +14 -0
  27. package/dist/messages/output.js +64 -0
  28. package/dist/messages/presets.js +26 -0
  29. package/dist/messages/preview.js +22 -0
  30. package/dist/messages/prompt.js +102 -0
  31. package/dist/messages/renderers.js +38 -0
  32. package/dist/messages/update.js +40 -0
  33. package/dist/new.js +46 -42
  34. package/dist/orchestrator.js +23 -18
  35. package/dist/output.js +34 -33
  36. package/dist/presets.js +122 -2
  37. package/dist/prompt.js +43 -58
  38. package/dist/renderers/console.js +33 -27
  39. package/dist/update.js +10 -21
  40. package/package.json +4 -1
  41. package/palabre.config.example.json +1 -0
package/dist/index.js CHANGED
@@ -2,16 +2,17 @@
2
2
  import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, writeExampleConfig } from "./config.js";
5
+ import { configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, resolveOutputDir, writeExampleConfig } from "./config.js";
6
6
  import { loadProjectInputs } from "./context.js";
7
7
  import { discoverLocalTools } from "./discovery.js";
8
8
  import { runDoctor } from "./doctor.js";
9
9
  import { AdapterError, formatAdapterError } from "./errors.js";
10
10
  import { runConfigWizard } from "./configWizard.js";
11
+ import { createTranslator, DEFAULT_LANGUAGE, parseLanguage, resolveLanguage } from "./i18n.js";
11
12
  import { DEFAULT_TURNS, parseTurnsFlag, turnsOrDefault } from "./limits.js";
12
13
  import { formatAgentPrompt } from "./prompt.js";
13
14
  import { runNewWizard } from "./new.js";
14
- import { listPresetNames, resolvePreset } from "./presets.js";
15
+ import { listPresetNames, listPresetsWithAvailability, resolvePreset } from "./presets.js";
15
16
  import { createConsoleRenderer } from "./renderers/console.js";
16
17
  import { createNdjsonRenderer } from "./renderers/ndjson.js";
17
18
  import { runDebate } from "./orchestrator.js";
@@ -20,17 +21,20 @@ import { applySourceUpdate, formatUpdateInstructions, getUpdateInfo } from "./up
20
21
  import { createSessionContext } from "./session.js";
21
22
  /** Point d'entrée principal du CLI Palabre. Dispatche vers la commande appropriée selon les arguments. */
22
23
  async function main() {
23
- const parsed = parseArgs(process.argv.slice(2));
24
+ const rawArgs = process.argv.slice(2);
25
+ const startupLanguage = resolveLanguage({ explicitLanguage: findRawLanguageFlag(rawArgs) });
26
+ const startupMessages = createTranslator(startupLanguage);
27
+ const parsed = parseArgs(rawArgs, startupMessages);
24
28
  if (parsed.command === "version" || parsed.flags.version) {
25
29
  console.log(await getPackageVersion());
26
30
  return;
27
31
  }
28
32
  if (parsed.command === "help" || parsed.flags.help) {
29
- printHelp();
33
+ printHelp(await resolveCommandMessages(parsed.flags), commandHelpTarget(parsed));
30
34
  return;
31
35
  }
32
36
  if (parsed.command === "doctor") {
33
- const result = await runDoctor(optionalString(parsed.flags.config), Boolean(parsed.flags.plain));
37
+ const result = await runDoctor(optionalString(parsed.flags.config), Boolean(parsed.flags.plain), optionalString(parsed.flags.language));
34
38
  console.log(result.output);
35
39
  process.exitCode = result.ok ? 0 : 1;
36
40
  return;
@@ -44,43 +48,68 @@ async function main() {
44
48
  return;
45
49
  }
46
50
  if (parsed.command === "presets" || parsed.command === "preset") {
47
- runPresetsCommand(parsed.flags);
51
+ await runPresetsCommand(parsed.flags);
48
52
  return;
49
53
  }
50
54
  if (parsed.command === "update") {
51
55
  const info = await getUpdateInfo(await getPackageVersion());
56
+ const updateConfigPath = optionalString(parsed.flags.config) ?? await resolveDefaultConfigPath();
57
+ const updateConfig = await configExists(updateConfigPath)
58
+ ? await loadConfig(updateConfigPath)
59
+ : undefined;
60
+ const updateLanguage = resolveLanguage({
61
+ explicitLanguage: optionalString(parsed.flags.language),
62
+ configLanguage: updateConfig?.language
63
+ });
64
+ const updateMessages = createTranslator(updateLanguage);
52
65
  if (parsed.flags.apply) {
53
- await applySourceUpdate(info);
54
- console.log("PALABRE est a jour.");
66
+ await applySourceUpdate(info, updateMessages);
67
+ console.log(updateMessages.update.upToDate);
55
68
  return;
56
69
  }
57
- console.log(formatUpdateInstructions(info));
70
+ console.log(formatUpdateInstructions(info, updateMessages));
58
71
  return;
59
72
  }
60
73
  if (parsed.command === "init" || parsed.command === "setup") {
61
74
  const initConfigPath = optionalString(parsed.flags.config) ?? (parsed.flags.local ? DEFAULT_CONFIG_PATH : GLOBAL_CONFIG_PATH);
62
75
  if (await configExists(initConfigPath)) {
63
- console.log(`${initConfigPath} existe déjà.`);
76
+ console.log(startupMessages.init.configExists(initConfigPath));
64
77
  return;
65
78
  }
66
79
  const discovery = await discoverLocalTools();
67
80
  const config = createConfigFromDiscovery(discovery);
81
+ config.language = resolveLanguage({
82
+ explicitLanguage: optionalString(parsed.flags.language),
83
+ configLanguage: config.language
84
+ });
85
+ const initMessages = createTranslator(config.language);
68
86
  await writeExampleConfig(initConfigPath, config);
69
- console.log(`${initConfigPath} créé.`);
70
- printInitDiscovery(discovery, config);
87
+ console.log(initMessages.init.configCreated(initConfigPath));
88
+ printInitDiscovery(discovery, config, initMessages);
71
89
  return;
72
90
  }
73
91
  const configPath = optionalString(parsed.flags.config) ?? await resolveDefaultConfigPath();
74
92
  if (!(await configExists(configPath))) {
75
- await writeExampleConfig(configPath);
76
- console.log(`${configPath} créé. Édite la config puis relance palabre run.`);
93
+ const config = createConfigFromDiscovery(await discoverLocalTools());
94
+ config.language = resolveLanguage({
95
+ explicitLanguage: optionalString(parsed.flags.language),
96
+ configLanguage: config.language
97
+ });
98
+ const messages = createTranslator(config.language);
99
+ await writeExampleConfig(configPath, config);
100
+ console.log(messages.init.editConfigThenRerun(configPath));
77
101
  return;
78
102
  }
79
103
  const config = await loadConfig(configPath);
104
+ const language = resolveLanguage({
105
+ explicitLanguage: optionalString(parsed.flags.language),
106
+ configLanguage: config.language
107
+ });
108
+ const messages = createTranslator(language);
80
109
  if (parsed.command === "new") {
81
- const selection = await runNewWizard(config);
110
+ const selection = await runNewWizard(config, messages);
82
111
  if (!selection) {
83
- console.log("Création de débat annulée.");
112
+ console.log(messages.new.cancelled);
84
113
  return;
85
114
  }
86
115
  parsed.flags["agent-a"] = selection.agentA;
@@ -108,17 +137,18 @@ async function main() {
108
137
  parsed.flags.context = selection.context;
109
138
  }
110
139
  const topic = optionalString(parsed.flags.topic) ?? "";
111
- const context = await loadProjectInputs(getStringListFlag(parsed.flags.files), getStringListFlag(parsed.flags.context));
140
+ const context = await loadProjectInputs(getStringListFlag(parsed.flags.files), getStringListFlag(parsed.flags.context), process.cwd(), messages);
112
141
  const presetName = optionalString(parsed.flags.preset);
113
- const preset = presetName ? resolvePreset(presetName) : undefined;
142
+ const preset = presetName ? resolvePreset(presetName, messages) : undefined;
114
143
  if (!topic) {
115
- throw new Error("Le parametre --topic/--subject est requis.");
144
+ throw new Error(messages.common.topicRequired);
116
145
  }
117
146
  const options = {
147
+ language,
118
148
  topic,
119
- agentA: resolveAgentName("agent A", parsed.flags["agent-a"], preset?.agentA, config.defaults?.agentA),
120
- agentB: resolveAgentName("agent B", parsed.flags["agent-b"], preset?.agentB, config.defaults?.agentB),
121
- turns: parseTurnsFlag(parsed.flags.turns, config.defaults?.turns ?? DEFAULT_TURNS, "--turns"),
149
+ agentA: resolveAgentName("agent A", parsed.flags["agent-a"], preset?.agentA, config.defaults?.agentA, messages),
150
+ agentB: resolveAgentName("agent B", parsed.flags["agent-b"], preset?.agentB, config.defaults?.agentB, messages),
151
+ turns: parseTurnsFlag(parsed.flags.turns, config.defaults?.turns ?? DEFAULT_TURNS, "--turns", messages),
122
152
  session: createSessionContext(),
123
153
  files: context.files,
124
154
  modelA: optionalString(parsed.flags["model-a"]),
@@ -131,14 +161,14 @@ async function main() {
131
161
  plainOutput: Boolean(parsed.flags.plain)
132
162
  };
133
163
  if (parsed.flags["show-prompt"]) {
134
- printContextWarnings(context.warnings);
135
- printPromptPreview(config, options);
164
+ printContextWarnings(context.warnings, messages);
165
+ printPromptPreview(config, options, language, messages);
136
166
  return;
137
167
  }
138
- const renderer = createRendererFromFlags(parsed.flags, options.plainOutput);
168
+ const renderer = createRendererFromFlags(parsed.flags, options.plainOutput, messages);
139
169
  context.warnings.forEach((warning) => renderer.warning(warning));
140
- const result = await runDebate(config, options, renderer);
141
- const outputPath = await writeDebateMarkdown(config.outputDir ?? ".", result.options, result.messages, result.summary, result.stopReason);
170
+ const result = await runDebate(config, options, renderer, messages);
171
+ const outputPath = await writeDebateMarkdown(resolveOutputDir(config.outputDir), result.options, result.messages, result.summary, result.stopReason, messages);
142
172
  renderer.done(outputPath);
143
173
  }
144
174
  /**
@@ -148,11 +178,17 @@ async function main() {
148
178
  async function runAgentsCommand(flags) {
149
179
  const configPath = optionalString(flags.config) ?? await resolveDefaultConfigPath();
150
180
  if (!(await configExists(configPath))) {
151
- throw new Error("Aucune config trouvée. Lance `palabre init`, puis `palabre agents`.");
181
+ const messages = createTranslator(resolveLanguage({ explicitLanguage: optionalString(flags.language) }));
182
+ throw new Error(messages.agents.noConfig);
152
183
  }
153
184
  const config = await loadConfig(configPath);
185
+ const language = resolveLanguage({
186
+ explicitLanguage: optionalString(flags.language),
187
+ configLanguage: config.language
188
+ });
189
+ const messages = createTranslator(language);
154
190
  const discovery = await discoverLocalTools();
155
- printAgents(configPath, config, discovery);
191
+ printAgents(configPath, config, discovery, messages);
156
192
  }
157
193
  /**
158
194
  * Exécute la commande `config` : wizard interactif ou mise à jour directe des paramètres par défaut.
@@ -160,62 +196,76 @@ async function runAgentsCommand(flags) {
160
196
  */
161
197
  async function runConfigCommand(flags) {
162
198
  const configPath = optionalString(flags.config) ?? await resolveDefaultConfigPath();
199
+ const explicitLanguage = optionalString(flags.language);
163
200
  if (!(await configExists(configPath))) {
201
+ const messages = createTranslator(resolveLanguage({ explicitLanguage }));
164
202
  await writeExampleConfig(configPath);
165
- console.log(`${configPath} créé. Édite la config puis relance palabre config.`);
203
+ console.log(messages.config.createdForConfig(configPath));
166
204
  return;
167
205
  }
168
206
  const config = await loadConfig(configPath);
207
+ const language = resolveLanguage({
208
+ explicitLanguage,
209
+ configLanguage: config.language
210
+ });
211
+ const messages = createTranslator(language);
169
212
  if (flags["sync-agents"]) {
170
213
  const discovery = await discoverLocalTools();
171
214
  const addedAgents = syncDetectedAgents(config, discovery);
172
215
  if (addedAgents.length === 0) {
173
- console.log(`Aucun agent détecté manquant dans ${configPath}.`);
216
+ console.log(messages.config.syncNoMissing(configPath));
174
217
  return;
175
218
  }
176
219
  await writeExampleConfig(configPath, config);
177
- console.log(`Agents ajoutés dans ${configPath}: ${addedAgents.join(", ")}.`);
220
+ console.log(messages.config.syncAdded(configPath, addedAgents.join(", ")));
178
221
  return;
179
222
  }
180
223
  const defaultAgents = getStringListFlag(flags["set-defaults"]);
181
224
  const hasTurnsFlag = flags.turns !== undefined;
182
225
  const summaryAgentValue = optionalString(flags["summary-agent"]);
183
- if (defaultAgents.length > 0 || hasTurnsFlag || summaryAgentValue !== undefined) {
226
+ const languageValue = explicitLanguage;
227
+ const changesDefaults = defaultAgents.length > 0 || hasTurnsFlag || summaryAgentValue !== undefined;
228
+ if (changesDefaults || languageValue !== undefined) {
184
229
  const nextDefaults = { ...(config.defaults ?? {}) };
185
230
  if (defaultAgents.length > 0) {
186
231
  const [agentA, agentB] = defaultAgents;
187
232
  if (!agentA || !agentB) {
188
- throw new Error("L'option --set-defaults attend deux agents: --set-defaults <agentA> <agentB>.");
233
+ throw new Error(messages.common.setDefaultsRequiresTwo);
189
234
  }
190
- assertKnownAgent(config, agentA, "defaults.agentA");
191
- assertKnownAgent(config, agentB, "defaults.agentB");
235
+ assertKnownAgent(config, agentA, "defaults.agentA", messages);
236
+ assertKnownAgent(config, agentB, "defaults.agentB", messages);
192
237
  nextDefaults.agentA = agentA;
193
238
  nextDefaults.agentB = agentB;
194
239
  }
195
240
  if (hasTurnsFlag) {
196
- nextDefaults.turns = parseTurnsFlag(flags.turns, nextDefaults.turns ?? DEFAULT_TURNS, "--turns");
241
+ nextDefaults.turns = parseTurnsFlag(flags.turns, nextDefaults.turns ?? DEFAULT_TURNS, "--turns", messages);
197
242
  }
198
243
  if (summaryAgentValue !== undefined) {
199
244
  if (isNoneValue(summaryAgentValue)) {
200
245
  delete nextDefaults.summaryAgent;
201
246
  }
202
247
  else {
203
- assertKnownAgent(config, summaryAgentValue, "defaults.summaryAgent");
248
+ assertKnownAgent(config, summaryAgentValue, "defaults.summaryAgent", messages);
204
249
  nextDefaults.summaryAgent = summaryAgentValue;
205
250
  }
206
251
  }
207
- config.defaults = nextDefaults;
252
+ if (languageValue !== undefined) {
253
+ config.language = parseLanguage(languageValue, "--language");
254
+ }
255
+ if (changesDefaults) {
256
+ config.defaults = nextDefaults;
257
+ }
208
258
  await writeExampleConfig(configPath, config);
209
- console.log(`Paramètres par défaut mis à jour dans ${configPath}: ${formatDefaultsForMessage(config.defaults)}.`);
259
+ console.log(messages.config.updated(configPath, formatDefaultsForMessage(config.defaults ?? {}, messages), config.language ?? DEFAULT_LANGUAGE));
210
260
  return;
211
261
  }
212
262
  if (flags["clear-defaults"]) {
213
263
  delete config.defaults;
214
264
  await writeExampleConfig(configPath, config);
215
- console.log(`Paramètres par défaut supprimés dans ${configPath}. Utilise maintenant un preset ou --agent-a/--agent-b pour lancer un débat.`);
265
+ console.log(messages.config.cleared(configPath));
216
266
  return;
217
267
  }
218
- await runConfigWizard(configPath, config);
268
+ await runConfigWizard(configPath, config, messages);
219
269
  }
220
270
  /**
221
271
  * Renvoie `true` si la valeur représente une désactivation explicite (ex. "none", "0", "disabled").
@@ -229,12 +279,8 @@ function isNoneValue(value) {
229
279
  * @param defaults - Objet `defaults` de la config Palabre.
230
280
  * @returns Chaîne résumant la paire d'agents, le nombre de réponses et l'agent de synthèse.
231
281
  */
232
- function formatDefaultsForMessage(defaults) {
233
- const pair = defaults.agentA && defaults.agentB
234
- ? `agents: ${defaults.agentA} <-> ${defaults.agentB}`
235
- : "agents: non définis";
236
- const summary = defaults.summaryAgent ? `synthèse: ${defaults.summaryAgent}` : "synthèse: agent B";
237
- return `${pair}, réponses: ${turnsOrDefault(defaults.turns)}, ${summary}`;
282
+ function formatDefaultsForMessage(defaults, messages) {
283
+ return messages.config.defaultsSummary(defaults.agentA, defaults.agentB, turnsOrDefault(defaults.turns), defaults.summaryAgent);
238
284
  }
239
285
  /**
240
286
  * Lève une erreur si `agentName` n'est pas déclaré dans la config.
@@ -242,9 +288,9 @@ function formatDefaultsForMessage(defaults) {
242
288
  * @param agentName - Nom de l'agent à vérifier.
243
289
  * @param fieldName - Nom du champ (utilisé dans le message d'erreur).
244
290
  */
245
- function assertKnownAgent(config, agentName, fieldName) {
291
+ function assertKnownAgent(config, agentName, fieldName, messages) {
246
292
  if (!config.agents[agentName]) {
247
- throw new Error(`Agent inconnu pour ${fieldName}: ${agentName}. Agents disponibles: ${Object.keys(config.agents).join(", ")}.`);
293
+ throw new Error(messages.common.unknownAgentForField(fieldName, agentName, Object.keys(config.agents).join(", ")));
248
294
  }
249
295
  }
250
296
  /**
@@ -256,10 +302,10 @@ function assertKnownAgent(config, agentName, fieldName) {
256
302
  * @param defaultValue - Valeur issue des défauts de la config.
257
303
  * @returns Nom de l'agent résolu.
258
304
  */
259
- function resolveAgentName(label, explicitValue, presetValue, defaultValue) {
305
+ function resolveAgentName(label, explicitValue, presetValue, defaultValue, messages) {
260
306
  const resolved = optionalString(explicitValue) ?? presetValue ?? defaultValue;
261
307
  if (!resolved) {
262
- throw new Error(`Aucun ${label} défini. Utilise --agent-a/--agent-b, un preset, ou lance palabre init pour définir defaults.agentA/defaults.agentB.`);
308
+ throw new Error(messages.common.noAgentDefined(label));
263
309
  }
264
310
  return resolved;
265
311
  }
@@ -268,30 +314,33 @@ function resolveAgentName(label, explicitValue, presetValue, defaultValue) {
268
314
  * @param config - Config chargée.
269
315
  * @param options - Options du débat résolues.
270
316
  */
271
- function printPromptPreview(config, options) {
317
+ function printPromptPreview(config, options, language, messages) {
272
318
  const agentConfig = config.agents[options.agentA];
273
319
  if (!agentConfig) {
274
- throw new Error(`Agent inconnu: ${options.agentA}`);
320
+ throw new Error(messages.common.unknownAgent(options.agentA));
275
321
  }
276
322
  const prompt = formatAgentPrompt({
277
323
  topic: options.topic,
278
324
  turn: 1,
325
+ totalTurns: options.turns,
279
326
  selfName: options.agentA,
280
327
  peerName: options.agentB,
281
328
  selfRole: agentConfig.role,
329
+ language: options.language,
282
330
  session: options.session,
283
331
  files: options.files,
284
332
  transcript: []
285
333
  });
286
- console.log(`# Prompt preview`);
287
- console.log(`Agent: ${options.agentA} (${agentConfig.role})`);
288
- console.log(`Peer: ${options.agentB}`);
289
- console.log(`Pull missing Ollama models: ${options.pullModels ? "yes" : "no"}`);
290
- console.log(`Summary: ${options.summaryEnabled ? options.summaryAgent ?? options.agentB : "disabled"}`);
334
+ console.log(messages.preview.title);
335
+ console.log(messages.preview.agent(options.agentA, agentConfig.role));
336
+ console.log(messages.preview.peer(options.agentB));
337
+ console.log(messages.preview.pullModels(options.pullModels));
338
+ console.log(messages.preview.summary(options.summaryEnabled ? options.summaryAgent ?? options.agentB : messages.preview.disabled));
339
+ console.log(messages.preview.interfaceLanguage(language));
291
340
  console.log("");
292
341
  console.log(prompt);
293
342
  console.log("");
294
- console.log("Note: seuls les prompts du premier tour sont exacts sans exécuter les agents. Les tours suivants incluent le transcript réel.");
343
+ console.log(messages.preview.note);
295
344
  }
296
345
  /**
297
346
  * Extrait une chaîne non vide depuis une valeur de flag, ou renvoie `undefined`.
@@ -300,6 +349,20 @@ function printPromptPreview(config, options) {
300
349
  function optionalString(value) {
301
350
  return typeof value === "string" && value.trim() ? value : undefined;
302
351
  }
352
+ /**
353
+ * Pré-lit seulement `--language`/`--lang` dans les arguments bruts pour localiser
354
+ * les erreurs qui peuvent survenir avant le parsing complet ou le chargement de config.
355
+ */
356
+ function findRawLanguageFlag(args) {
357
+ for (let index = 0; index < args.length; index += 1) {
358
+ const value = args[index];
359
+ if (value === "--language" || value === "--lang") {
360
+ const next = args[index + 1];
361
+ return next && !next.startsWith("-") ? next : undefined;
362
+ }
363
+ }
364
+ return undefined;
365
+ }
303
366
  /** Liste des kinds de renderer acceptés par `--renderer`. */
304
367
  const SUPPORTED_RENDERERS = ["auto", "pretty", "plain", "ndjson"];
305
368
  /**
@@ -313,28 +376,28 @@ const SUPPORTED_RENDERERS = ["auto", "pretty", "plain", "ndjson"];
313
376
  *
314
377
  * Lève si la valeur de `--renderer` n'est pas dans `SUPPORTED_RENDERERS`.
315
378
  */
316
- function createRendererFromFlags(flags, plainOutputFallback) {
379
+ function createRendererFromFlags(flags, plainOutputFallback, messages) {
317
380
  const explicit = optionalString(flags.renderer);
318
381
  if (explicit) {
319
382
  if (!SUPPORTED_RENDERERS.includes(explicit)) {
320
- throw new Error(`Renderer inconnu: ${explicit}. Valeurs supportées: ${SUPPORTED_RENDERERS.join(", ")}.`);
383
+ throw new Error(messages.common.unknownRenderer(explicit, SUPPORTED_RENDERERS.join(", ")));
321
384
  }
322
385
  const kind = explicit;
323
386
  switch (kind) {
324
387
  case "ndjson":
325
388
  return createNdjsonRenderer();
326
389
  case "plain":
327
- return createConsoleRenderer(true);
390
+ return createConsoleRenderer(true, messages);
328
391
  case "pretty":
329
- return createConsoleRenderer(false);
392
+ return createConsoleRenderer(false, messages);
330
393
  case "auto":
331
- return createConsoleRenderer(plainOutputFallback);
394
+ return createConsoleRenderer(plainOutputFallback, messages);
332
395
  }
333
396
  }
334
397
  if (flags.json) {
335
398
  return createNdjsonRenderer();
336
399
  }
337
- return createConsoleRenderer(plainOutputFallback);
400
+ return createConsoleRenderer(plainOutputFallback, messages);
338
401
  }
339
402
  /**
340
403
  * Exécute la commande `palabre presets`.
@@ -346,22 +409,32 @@ function createRendererFromFlags(flags, plainOutputFallback) {
346
409
  *
347
410
  * @param flags - Flags parsés depuis la ligne de commande.
348
411
  */
349
- function runPresetsCommand(flags) {
350
- const presets = listPresetNames().map((name) => {
351
- const pair = resolvePreset(name);
352
- return { name, agentA: pair.agentA, agentB: pair.agentB };
412
+ async function runPresetsCommand(flags) {
413
+ const discovery = await discoverLocalTools();
414
+ const configPath = optionalString(flags.config) ?? await resolveDefaultConfigPath();
415
+ const config = await configExists(configPath)
416
+ ? await loadConfig(configPath)
417
+ : createConfigFromDiscovery(discovery);
418
+ const language = resolveLanguage({
419
+ explicitLanguage: optionalString(flags.language),
420
+ configLanguage: config.language
353
421
  });
422
+ const messages = createTranslator(language);
423
+ const presets = listPresetsWithAvailability(config, discovery, messages);
354
424
  if (flags.json) {
355
425
  process.stdout.write(JSON.stringify({ v: 1, presets }) + "\n");
356
426
  return;
357
427
  }
358
- console.log("Presets disponibles:");
428
+ console.log(messages.presets.title);
359
429
  console.log("");
360
430
  for (const preset of presets) {
361
- console.log(` ${preset.name.padEnd(20)} ${preset.agentA} <-> ${preset.agentB}`);
431
+ const status = preset.available
432
+ ? messages.presets.available
433
+ : messages.presets.unavailable(preset.unavailableReasons.join("; "));
434
+ console.log(` ${preset.name.padEnd(20)} ${preset.agentA} <-> ${preset.agentB} ${status}`);
362
435
  }
363
436
  console.log("");
364
- console.log(`Total : ${presets.length} preset(s). Utilise --json pour une sortie machine-readable.`);
437
+ console.log(messages.presets.total(presets.length));
365
438
  }
366
439
  /**
367
440
  * Parse `process.argv` en une structure typée `ParsedArgs`.
@@ -370,7 +443,7 @@ function runPresetsCommand(flags) {
370
443
  * @param args - Tableau d'arguments (généralement `process.argv.slice(2)`).
371
444
  * @returns Commande détectée, indicateur d'explicitation et map de flags.
372
445
  */
373
- function parseArgs(args) {
446
+ function parseArgs(args, messages) {
374
447
  const flags = {};
375
448
  let command = "run";
376
449
  let commandExplicit = false;
@@ -379,13 +452,18 @@ function parseArgs(args) {
379
452
  const presets = new Set(listPresetNames());
380
453
  for (let index = 0; index < args.length; index += 1) {
381
454
  const value = args[index];
455
+ if (!value.startsWith("-") && !commandExplicit && positionals.length === 0 && commands.has(value)) {
456
+ command = value;
457
+ commandExplicit = true;
458
+ continue;
459
+ }
382
460
  if (!value.startsWith("-") && index === 0) {
383
461
  if (commands.has(value)) {
384
462
  command = value;
385
463
  commandExplicit = true;
386
464
  }
387
465
  else if (isLikelyCommandTypo(value, commands)) {
388
- throw new Error(`Commande inconnue: ${value}. Commandes disponibles: ${Array.from(commands).join(", ")}.`);
466
+ throw new Error(messages.common.unknownCommand(value, Array.from(commands).join(", ")));
389
467
  }
390
468
  else {
391
469
  positionals.push(value);
@@ -412,7 +490,7 @@ function parseArgs(args) {
412
490
  if (value === "-s") {
413
491
  const next = args[index + 1];
414
492
  if (!next || next.startsWith("-")) {
415
- throw new Error("L'option -s attend une valeur.");
493
+ throw new Error(messages.common.optionRequiresValue("-s"));
416
494
  }
417
495
  flags.topic = next;
418
496
  index += 1;
@@ -421,7 +499,7 @@ function parseArgs(args) {
421
499
  if (value === "-t") {
422
500
  const next = args[index + 1];
423
501
  if (!next || next.startsWith("-")) {
424
- throw new Error("L'option -t attend une valeur.");
502
+ throw new Error(messages.common.optionRequiresValue("-t"));
425
503
  }
426
504
  flags.turns = next;
427
505
  index += 1;
@@ -437,7 +515,7 @@ function parseArgs(args) {
437
515
  index += 1;
438
516
  }
439
517
  if (values.length !== 2) {
440
- throw new Error("L'option --set-defaults attend deux agents: --set-defaults <agentA> <agentB>.");
518
+ throw new Error(messages.common.setDefaultsRequiresTwo);
441
519
  }
442
520
  flags[key] = values;
443
521
  continue;
@@ -454,7 +532,7 @@ function parseArgs(args) {
454
532
  const next = args[index + 1];
455
533
  if (!next || next.startsWith("-")) {
456
534
  if (requiresFlagValue(key)) {
457
- throw new Error(`L'option --${rawKey} attend une valeur.`);
535
+ throw new Error(messages.common.optionRequiresValue(`--${rawKey}`));
458
536
  }
459
537
  flags[key] = true;
460
538
  }
@@ -465,7 +543,7 @@ function parseArgs(args) {
465
543
  }
466
544
  }
467
545
  if (command === "run") {
468
- applyRunPositionals(positionals, flags, presets, commandExplicit);
546
+ applyRunPositionals(positionals, flags, presets, commandExplicit, commands, messages);
469
547
  }
470
548
  return { command, commandExplicit, flags };
471
549
  }
@@ -513,7 +591,7 @@ function levenshteinDistance(left, right) {
513
591
  * @param presets - Ensemble des noms de presets valides.
514
592
  * @param commandExplicit - `true` si l'utilisateur a tapé `palabre run` explicitement.
515
593
  */
516
- function applyRunPositionals(positionals, flags, presets, commandExplicit) {
594
+ function applyRunPositionals(positionals, flags, presets, commandExplicit, commands, messages) {
517
595
  if (positionals.length === 0) {
518
596
  return;
519
597
  }
@@ -526,7 +604,10 @@ function applyRunPositionals(positionals, flags, presets, commandExplicit) {
526
604
  return;
527
605
  }
528
606
  if (!commandExplicit && positionals.length === 1 && !positionals[0]?.includes(" ")) {
529
- throw new Error(`Commande inconnue ou sujet ambigu: ${positionals[0]}. Utilise -s "${positionals[0]}" pour un sujet en un mot, ou palabre help pour voir les commandes.`);
607
+ if (isLikelyCommandTypo(positionals[0], commands)) {
608
+ throw new Error(messages.common.unknownCommand(positionals[0], Array.from(commands).join(", ")));
609
+ }
610
+ throw new Error(messages.common.ambiguousSubject(positionals[0]));
530
611
  }
531
612
  flags.topic ??= positionals.join(" ");
532
613
  }
@@ -536,6 +617,7 @@ function applyRunPositionals(positionals, flags, presets, commandExplicit) {
536
617
  */
537
618
  function normalizeFlagName(value) {
538
619
  const aliases = {
620
+ lang: "language",
539
621
  s: "topic",
540
622
  subject: "topic",
541
623
  t: "turns"
@@ -551,6 +633,7 @@ function requiresFlagValue(value) {
551
633
  "agent-a",
552
634
  "agent-b",
553
635
  "config",
636
+ "language",
554
637
  "model-a",
555
638
  "model-b",
556
639
  "preset",
@@ -586,9 +669,9 @@ function getStringListFlag(value) {
586
669
  * Écrit les avertissements de contexte sur `stderr`.
587
670
  * @param warnings - Messages d'avertissement issus du chargement des fichiers de contexte.
588
671
  */
589
- function printContextWarnings(warnings) {
672
+ function printContextWarnings(warnings, messages) {
590
673
  for (const warning of warnings) {
591
- process.stderr.write(`Warning: ${warning}\n`);
674
+ process.stderr.write(`${messages.renderers.warningPrefix} ${warning}\n`);
592
675
  }
593
676
  }
594
677
  /**
@@ -616,6 +699,7 @@ function findDetectedMissingAgents(config, discovery) {
616
699
  discovery.codex.available ? "codex" : undefined,
617
700
  discovery.claude.available ? "claude" : undefined,
618
701
  discovery.gemini.available ? "gemini" : undefined,
702
+ discovery.antigravity.available ? "antigravity" : undefined,
619
703
  discovery.opencode.available ? "opencode" : undefined,
620
704
  discovery.ollama.available ? "ollama-local" : undefined
621
705
  ].filter((agent) => Boolean(agent));
@@ -627,15 +711,15 @@ function findDetectedMissingAgents(config, discovery) {
627
711
  * @param config - Config Palabre chargée.
628
712
  * @param discovery - Résultat de la découverte locale des outils.
629
713
  */
630
- function printAgents(configPath, config, discovery) {
714
+ function printAgents(configPath, config, discovery, messages) {
631
715
  const entries = Object.entries(config.agents).sort(([left], [right]) => left.localeCompare(right));
632
- console.log(`Config: ${configPath}`);
716
+ console.log(messages.agents.config(configPath));
633
717
  console.log("");
634
- console.log("Agents déclarés:");
718
+ console.log(messages.agents.title);
635
719
  for (const [name, agentConfig] of entries) {
636
- const status = formatAgentDetection(name, agentConfig, discovery);
637
- const defaults = formatAgentDefaults(name, config);
638
- const details = formatAgentDetails(agentConfig);
720
+ const status = formatAgentDetection(name, agentConfig, discovery, messages);
721
+ const defaults = formatAgentDefaults(name, config, messages);
722
+ const details = formatAgentDetails(agentConfig, messages);
639
723
  const suffix = defaults ? ` | ${defaults}` : "";
640
724
  console.log(`- ${name.padEnd(13)} ${`${agentConfig.type}/${agentConfig.role}`.padEnd(18)} ${status}${suffix}`);
641
725
  if (details) {
@@ -643,32 +727,32 @@ function printAgents(configPath, config, discovery) {
643
727
  }
644
728
  }
645
729
  console.log("");
646
- console.log(`Défauts: ${config.defaults?.agentA ?? "aucun"} <-> ${config.defaults?.agentB ?? "aucun"}, réponses: ${turnsOrDefault(config.defaults?.turns)}, synthèse: ${config.defaults?.summaryAgent ?? "agent B"}`);
730
+ console.log(messages.agents.defaults(config.defaults?.agentA ?? messages.agents.none, config.defaults?.agentB ?? messages.agents.none, turnsOrDefault(config.defaults?.turns), config.defaults?.summaryAgent ?? messages.agents.summaryAgentB));
647
731
  }
648
732
  /**
649
733
  * Renvoie un libellé indiquant si l'agent est agent A, agent B ou agent de synthèse par défaut.
650
734
  * @param name - Nom de l'agent.
651
735
  * @param config - Config Palabre contenant les défauts.
652
736
  */
653
- function formatAgentDefaults(name, config) {
737
+ function formatAgentDefaults(name, config, messages) {
654
738
  const labels = [];
655
739
  if (config.defaults?.agentA === name)
656
- labels.push("agent A par défaut");
740
+ labels.push(messages.agents.defaultAgentA);
657
741
  if (config.defaults?.agentB === name)
658
- labels.push("agent B par défaut");
742
+ labels.push(messages.agents.defaultAgentB);
659
743
  if (config.defaults?.summaryAgent === name)
660
- labels.push("synthèse par défaut");
744
+ labels.push(messages.agents.defaultSummary);
661
745
  return labels.join(", ");
662
746
  }
663
747
  /**
664
748
  * Renvoie une ligne de détails pour un agent : commande CLI ou modèle Ollama.
665
749
  * @param agentConfig - Configuration de l'agent.
666
750
  */
667
- function formatAgentDetails(agentConfig) {
751
+ function formatAgentDetails(agentConfig, messages) {
668
752
  if (agentConfig.type === "ollama") {
669
- return `modèle: ${agentConfig.model}`;
753
+ return messages.agents.model(agentConfig.model);
670
754
  }
671
- return `commande: ${agentConfig.command}${agentConfig.model ? ` | modèle: ${agentConfig.model}` : ""}`;
755
+ return messages.agents.command(agentConfig.command, agentConfig.model);
672
756
  }
673
757
  /**
674
758
  * Renvoie le statut de détection d'un agent sous forme de chaîne lisible.
@@ -677,17 +761,17 @@ function formatAgentDetails(agentConfig) {
677
761
  * @param agentConfig - Configuration de l'agent.
678
762
  * @param discovery - Résultat de la découverte locale des outils.
679
763
  */
680
- function formatAgentDetection(name, agentConfig, discovery) {
764
+ function formatAgentDetection(name, agentConfig, discovery, messages) {
681
765
  if (agentConfig.type === "ollama") {
682
766
  if (!discovery.ollama.available) {
683
- return discovery.ollama.commandAvailable ? "Ollama non joignable" : "Ollama non détecté";
767
+ return discovery.ollama.commandAvailable ? messages.agents.ollamaUnreachable : messages.agents.ollamaNotDetected;
684
768
  }
685
769
  return discovery.ollama.models.includes(agentConfig.model)
686
- ? "détecté"
687
- : `modèle absent (${agentConfig.model})`;
770
+ ? messages.agents.detected()
771
+ : messages.agents.missingModel(agentConfig.model);
688
772
  }
689
773
  const detection = cliDetectionForAgent(name, agentConfig, discovery);
690
- return detection.available ? `détecté (${detection.command})` : "non détecté";
774
+ return detection.available ? messages.agents.detected(detection.command) : messages.agents.notDetected;
691
775
  }
692
776
  /**
693
777
  * Résout l'entrée de détection correspondant à un agent CLI dans le résultat de découverte.
@@ -697,16 +781,20 @@ function formatAgentDetection(name, agentConfig, discovery) {
697
781
  * @param discovery - Résultat de la découverte locale des outils.
698
782
  */
699
783
  function cliDetectionForAgent(name, agentConfig, discovery) {
700
- const command = normalizeCommandName(agentConfig.type === "cli" ? agentConfig.command : name);
784
+ const command = normalizeCommandName(agentConfig.type === "cli" || agentConfig.type === "cli-pty" ? agentConfig.command : name);
701
785
  if (command === "codex")
702
786
  return discovery.codex;
703
787
  if (command === "claude")
704
788
  return discovery.claude;
705
789
  if (command === "gemini")
706
790
  return discovery.gemini;
791
+ if (command === "agy")
792
+ return discovery.antigravity;
793
+ if (command === "antigravity")
794
+ return discovery.antigravity;
707
795
  if (command === "opencode")
708
796
  return discovery.opencode;
709
- 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 };
710
798
  }
711
799
  /**
712
800
  * Extrait le nom de base d'une commande en supprimant le chemin et l'extension Windows éventuelle.
@@ -720,181 +808,110 @@ function normalizeCommandName(command) {
720
808
  * @param discovery - Résultat de la découverte locale des outils.
721
809
  * @param config - Config générée à partir de la découverte.
722
810
  */
723
- function printInitDiscovery(discovery, config) {
811
+ function printInitDiscovery(discovery, config, messages) {
724
812
  console.log("");
725
- console.log("Détection locale:");
726
- console.log(`- Codex CLI: ${formatCommandDetection(discovery.codex)}`);
727
- console.log(`- Claude CLI: ${formatCommandDetection(discovery.claude)}`);
728
- console.log(`- Gemini CLI: ${formatCommandDetection(discovery.gemini)}`);
729
- console.log(`- OpenCode CLI: ${formatCommandDetection(discovery.opencode)}`);
730
- console.log(`- Ollama API: ${formatOllamaDetection(discovery.ollama)}`);
813
+ console.log(messages.init.localDetectionTitle);
814
+ console.log(`- Codex CLI: ${formatCommandDetection(discovery.codex, messages)}`);
815
+ console.log(`- Claude CLI: ${formatCommandDetection(discovery.claude, messages)}`);
816
+ console.log(`- Gemini CLI: ${formatCommandDetection(discovery.gemini, messages)}`);
817
+ console.log(`- Antigravity CLI: ${formatCommandDetection(discovery.antigravity, messages)}`);
818
+ console.log(`- OpenCode CLI: ${formatCommandDetection(discovery.opencode, messages)}`);
819
+ console.log(`- Ollama API: ${formatOllamaDetection(discovery.ollama, messages)}`);
731
820
  console.log("");
732
- console.log(`Défauts: ${config.defaults?.agentA ?? "codex"} <-> ${config.defaults?.agentB ?? "ollama-local"}`);
821
+ console.log(config.defaults?.agentA && config.defaults.agentB
822
+ ? messages.init.defaults(config.defaults.agentA, config.defaults.agentB)
823
+ : messages.init.noDefaultPair(formatDetectedAgentSummary(discovery, config.language ?? DEFAULT_LANGUAGE)));
824
+ console.log(messages.init.languageHint(config.language ?? DEFAULT_LANGUAGE));
825
+ }
826
+ function formatDetectedAgentSummary(discovery, language) {
827
+ const names = [
828
+ discovery.codex.available ? "codex" : undefined,
829
+ discovery.claude.available ? "claude" : undefined,
830
+ discovery.gemini.available ? "gemini" : undefined,
831
+ discovery.antigravity.available ? "antigravity" : undefined,
832
+ discovery.opencode.available ? "opencode" : undefined,
833
+ discovery.ollama.available ? "ollama-local" : undefined
834
+ ].filter((name) => Boolean(name));
835
+ if (names.length === 0) {
836
+ return language === "en" ? "no agent detected" : "aucun agent détecté";
837
+ }
838
+ if (names.length === 1) {
839
+ return language === "en"
840
+ ? `only one agent detected (${names[0]})`
841
+ : `un seul agent détecté (${names[0]})`;
842
+ }
843
+ return language === "en"
844
+ ? `no usable pair detected among ${names.join(", ")}`
845
+ : `aucune paire utilisable détectée parmi ${names.join(", ")}`;
733
846
  }
734
847
  /**
735
848
  * Formate le statut de détection d'un outil CLI (disponible ou non).
736
849
  * @param detection - Résultat de détection d'un outil CLI.
737
850
  */
738
- function formatCommandDetection(detection) {
851
+ function formatCommandDetection(detection, messages) {
739
852
  return detection.available
740
- ? `détecté (${detection.command})`
741
- : "non détecté";
853
+ ? messages.init.commandDetected(detection.command)
854
+ : messages.init.commandMissing;
742
855
  }
743
856
  /**
744
857
  * Formate le statut de détection d'Ollama : commande absente, serveur injoignable ou modèles disponibles.
745
858
  * @param detection - Résultat de détection d'Ollama.
746
859
  */
747
- function formatOllamaDetection(detection) {
860
+ function formatOllamaDetection(detection, messages) {
748
861
  if (!detection.available) {
749
862
  return detection.commandAvailable
750
- ? `serveur non joignable (${detection.baseUrl})`
751
- : "non détecté";
863
+ ? messages.init.ollamaServerUnreachable(detection.baseUrl)
864
+ : messages.init.ollamaMissing;
752
865
  }
753
866
  const modelCount = detection.models.length;
754
- return `détectée (${modelCount} modèle${modelCount > 1 ? "s" : ""})`;
867
+ return messages.init.ollamaDetected(modelCount);
755
868
  }
756
869
  /** Affiche le texte d'aide complet sur `stdout`. */
757
- function printHelp() {
758
- console.log(`
759
- PALABRE
760
- _____________________________________________
761
-
762
- Usage rapide:
763
-
764
- palabre init
765
- Crée une config globale et détecte les agents AI disponibles sur la machine.
766
-
767
- palabre agents
768
- Affiche les agents déclarés dans la config.
769
-
770
- palabre config
771
- Assistant pour définir ou supprimer les paramètres par défaut.
772
-
773
- palabre new
774
- Assistant interactif pour choisir les agents, le sujet et les options.
775
-
776
- palabre claude-gemini "Sujet" -t 4
777
- Lance avec un preset et un sujet positionnel.
778
-
779
- palabre "Sujet"
780
- Lance le débat avec paramètres par défaut de la config.
781
-
782
- _____________________________________________
783
-
784
-
785
- Commandes:
786
-
787
- palabre init [--local]
788
- Crée une config locale et détecte Codex, Claude, Gemini, OpenCode et Ollama.
789
-
790
- palabre agents [--config <path>]
791
- Liste les agents déclarés dans la config et leur détection locale.
792
-
793
- palabre presets [--json]
794
- Liste les presets de paires d'agents. \`--json\` émet la liste structurée
795
- pour les intégrations (extension VS Code, scripts).
796
-
797
- palabre config
798
- Assistant pour définir ou supprimer les paramètres par défaut.
799
-
800
- palabre config --set-defaults <agentA> <agentB> [-t <n>] [--summary-agent <name>]
801
- Définit les agents par défaut, et optionnellement les réponses et la synthèse.
802
-
803
- palabre config -t <n>
804
- Définit seulement le nombre de réponses par défaut.
805
-
806
- palabre config --summary-agent <name|none>
807
- Définit ou retire seulement l'agent de synthèse par défaut.
808
-
809
- palabre config --clear-defaults
810
- Supprime les paramètres par défaut.
811
-
812
- palabre doctor [--config <path>]
813
- Vérifie la config et les outils locaux.
814
-
815
- palabre update [--apply]
816
- Affiche ou exécute les étapes de mise à jour d'un checkout git.
817
-
818
- palabre help
819
- Affiche cette aide. Identique à -h ou --help.
820
-
821
- palabre version
822
- Affiche la version. Identique à -v ou --version.
823
-
824
- _____________________________________________
825
-
826
-
827
- Notation:
828
-
829
- [option] signifie facultatif. Ne tape pas les crochets.
830
- <valeur> signifie qu'il faut remplacer ce texte par ta valeur.
831
-
832
- Options générales:
833
-
834
- -h, --help Affiche cette aide
835
- -v, --version Affiche la version
836
- -a, --agents Liste les agents. Identique à palabre agents
837
- --config <path> Chemin vers un fichier de config explicite
838
- --plain Utilise le rendu console simple sans habillage TUI
839
- --json Émet un événement NDJSON par ligne sur stdout (alias de --renderer ndjson)
840
- --renderer <kind> Force le renderer : auto | pretty | plain | ndjson
841
-
842
- Sujet et lancement:
843
-
844
- -s, --subject <text> Sujet du débat, option recommandée
845
- --topic <text> Alias compatible de --subject
846
- --agent-a <name> Premier agent
847
- --agent-b <name> Second agent
848
- --preset <name> Preset de paire d'agents. Exemples: codex-claude, claude-gemini
849
- -t, --turns <number> Nombre total de réponses (1 à 20)
850
- --no-early-stop Désactive l'arrêt anticipé si les agents sont clairement d'accord
851
-
852
- Modèles:
853
-
854
- --model-a <model> Modèle brut transmis à l'agent A
855
- --model-b <model> Modèle brut transmis à l'agent B
856
- --pull-models Autorise Ollama à télécharger un modèle manquant
857
-
858
- Synthèse:
859
-
860
- --summary-agent <name> Agent utilisé pour produire la synthèse finale
861
- --summary-model <model> Modèle brut transmis à l'agent de synthèse
862
- --no-summary Désactive la synthèse finale
863
-
864
- Contexte:
865
-
866
- --files <paths...> Fichiers texte à injecter explicitement dans le contexte
867
- --context <paths...> Scanne fichiers/dossiers texte en respectant les limites de contexte
868
- --show-prompt Affiche le prompt du premier tour sans appeler d'agent
869
-
870
- Configuration:
871
-
872
- --local Avec init/setup, crée ./palabre.config.json
873
- --set-defaults <a b> Avec config, définit les agents par défaut
874
- --summary-agent <name> Avec config, définit l'agent de synthèse par défaut
875
- --summary-agent none Avec config, retire l'agent de synthèse par défaut
876
- --clear-defaults Avec config, supprime les paramètres par défaut
877
- --sync-agents Avec config, ajoute les agents détectés manquants
878
-
879
- Mise à jour:
880
-
881
- --apply Avec update, exécute les étapes de mise à jour
882
-
883
- _____________________________________________
884
-
885
-
886
- Presets disponibles:
887
-
888
- ${listPresetNames().join(", ")}
889
-
890
- _____________________________________________
891
-
892
- `);
870
+ function printHelp(messages, command) {
871
+ const commandHelp = command ? messages.help.renderCommand(command) : undefined;
872
+ console.log(commandHelp ?? messages.help.render(listPresetNames().join(", ")));
873
+ }
874
+ function commandHelpTarget(parsed) {
875
+ if (parsed.command === "help" || parsed.command === "run") {
876
+ return undefined;
877
+ }
878
+ if (parsed.command === "agent")
879
+ return "agents";
880
+ if (parsed.command === "preset")
881
+ return "presets";
882
+ if (parsed.command === "setup")
883
+ return "init";
884
+ return parsed.command;
885
+ }
886
+ /** Résout les messages d'une commande qui peut être affichée avant le flux principal. */
887
+ async function resolveCommandMessages(flags) {
888
+ const explicitLanguage = optionalString(flags.language);
889
+ const configPath = optionalString(flags.config) ?? await resolveDefaultConfigPath();
890
+ let configLanguage;
891
+ try {
892
+ configLanguage = await configExists(configPath)
893
+ ? (await loadConfig(configPath)).language
894
+ : undefined;
895
+ }
896
+ catch {
897
+ configLanguage = undefined;
898
+ }
899
+ return createTranslator(resolveLanguage({ explicitLanguage, configLanguage }));
893
900
  }
894
901
  main().catch((error) => {
902
+ const language = safeStartupLanguage(process.argv.slice(2));
903
+ const messages = createTranslator(language);
895
904
  const message = error instanceof AdapterError
896
- ? formatAdapterError(error)
905
+ ? formatAdapterError(error, messages)
897
906
  : error instanceof Error ? error.message : String(error);
898
- console.error(`Erreur: ${message}`);
907
+ console.error(`${messages.common.errorPrefix}: ${message}`);
899
908
  process.exitCode = 1;
900
909
  });
910
+ function safeStartupLanguage(args) {
911
+ try {
912
+ return resolveLanguage({ explicitLanguage: findRawLanguageFlag(args) });
913
+ }
914
+ catch {
915
+ return DEFAULT_LANGUAGE;
916
+ }
917
+ }