palabre 0.6.0 → 0.6.3

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.
@@ -29,6 +29,16 @@ Usage:
29
29
  Flags:
30
30
  --json sortie structuree pour integrations
31
31
  --config <path> chemin de config explicite
32
+ `,
33
+ context: `
34
+ Scanne le contexte projet avec les memes regles que --context.
35
+
36
+ Usage:
37
+ palabre context scan [paths...] [flags]
38
+
39
+ Flags:
40
+ --json sortie structuree pour integrations
41
+ --language <fr|en> force la langue des avertissements
32
42
  `,
33
43
  config: `
34
44
  Configure les agents par defaut, la synthese, le nombre de reponses et la langue.
@@ -124,6 +134,16 @@ Usage:
124
134
  Flags:
125
135
  --json structured output for integrations
126
136
  --config <path> explicit config path
137
+ `,
138
+ context: `
139
+ Scans project context with the same rules as --context.
140
+
141
+ Usage:
142
+ palabre context scan [paths...] [flags]
143
+
144
+ Flags:
145
+ --json structured output for integrations
146
+ --language <fr|en> forces warning language
127
147
  `,
128
148
  config: `
129
149
  Configures default agents, summary, response count, and language.
@@ -212,6 +232,7 @@ Commandes:
212
232
  new Assistant interactif de debat
213
233
  agents Lister les agents configures
214
234
  presets Lister les presets disponibles
235
+ context Scanner le contexte projet
215
236
  config Modifier les parametres par defaut
216
237
  doctor Verifier la config et les outils locaux
217
238
  update Afficher ou appliquer les etapes de mise a jour
@@ -256,6 +277,7 @@ Commands:
256
277
  new Interactive debate assistant
257
278
  agents List configured agents
258
279
  presets List available presets
280
+ context Scan project context
259
281
  config Edit default settings
260
282
  doctor Check config and local tools
261
283
  update Show or apply update steps
@@ -2,12 +2,31 @@ export const orchestratorMessages = {
2
2
  fr: {
3
3
  agreementStopReason: "Accord clair detecte apres un tour complet.",
4
4
  earlyStop: (reason) => `Arret anticipe: ${reason}`,
5
+ agreementPatterns: [
6
+ "accord complet",
7
+ "accord total",
8
+ "aucun desaccord",
9
+ "aucune incertitude",
10
+ "rien a trancher",
11
+ "rien a ajouter",
12
+ "question factuelle resolue"
13
+ ],
5
14
  ollamaNoContext: (agentNames) => `${agentNames} ne lit pas le filesystem. Ajoute --files ou --context pour fournir un contexte projet.`,
6
15
  unknownSummaryAgent: (agentName) => `Agent de synthese inconnu: ${agentName}`
7
16
  },
8
17
  en: {
9
18
  agreementStopReason: "Clear agreement detected after a complete round.",
10
19
  earlyStop: (reason) => `Early stop: ${reason}`,
20
+ agreementPatterns: [
21
+ "full agreement",
22
+ "complete agreement",
23
+ "total agreement",
24
+ "no disagreement",
25
+ "no remaining uncertainty",
26
+ "nothing to settle",
27
+ "nothing to add",
28
+ "factual question resolved"
29
+ ],
11
30
  ollamaNoContext: (agentNames) => `${agentNames} cannot read the filesystem. Add --files or --context to provide project context.`,
12
31
  unknownSummaryAgent: (agentName) => `Unknown summary agent: ${agentName}`
13
32
  }
@@ -3,6 +3,7 @@ export const outputMessages = {
3
3
  title: "# PALABRE Debate",
4
4
  contextTitle: "## Contexte",
5
5
  exchangesTitle: "## Echanges",
6
+ failureTitle: "## Interruption",
6
7
  finalSummaryTitle: "## Synthese finale",
7
8
  tableField: "Champ",
8
9
  tableValue: "Valeur",
@@ -27,13 +28,19 @@ export const outputMessages = {
27
28
  sessionStartedAt: "Session demarree a",
28
29
  agent: "Agent",
29
30
  role: "Role",
30
- date: "Date"
31
+ date: "Date",
32
+ failurePhase: "Phase",
33
+ failureAgent: "Agent",
34
+ failureTurn: "Tour",
35
+ failureKind: "Type d'erreur",
36
+ failureMessage: "Message"
31
37
  }
32
38
  },
33
39
  en: {
34
40
  title: "# PALABRE Debate",
35
41
  contextTitle: "## Context",
36
42
  exchangesTitle: "## Exchanges",
43
+ failureTitle: "## Interruption",
37
44
  finalSummaryTitle: "## Final summary",
38
45
  tableField: "Field",
39
46
  tableValue: "Value",
@@ -58,7 +65,12 @@ export const outputMessages = {
58
65
  sessionStartedAt: "Session started at",
59
66
  agent: "Agent",
60
67
  role: "Role",
61
- date: "Date"
68
+ date: "Date",
69
+ failurePhase: "Phase",
70
+ failureAgent: "Agent",
71
+ failureTurn: "Turn",
72
+ failureKind: "Error kind",
73
+ failureMessage: "Message"
62
74
  }
63
75
  }
64
76
  };
@@ -29,6 +29,7 @@ export const promptMessages = {
29
29
  cwd: (value) => `- Dossier courant: ${value}`,
30
30
  sessionStartedAt: (value) => `- Session demarree a: ${value}`,
31
31
  turnProgress: (turn, totalTurns) => `- Tour courant: ${turn}/${totalTurns}`,
32
+ responseLanguageInstruction: "Langue de reponse obligatoire: francais. Reponds uniquement en francais, meme si le sujet ou le transcript contient une autre langue.",
32
33
  objectiveTitle: "Objectif:",
33
34
  debateObjectives: [
34
35
  "- Apporte une reponse utile, concrete et courte.",
@@ -55,7 +56,8 @@ export const promptMessages = {
55
56
  actionsHeading: "### Actions proposees",
56
57
  conclusionHeading: "### Conclusion",
57
58
  finalProseInstruction: "Un court paragraphe de synthese en prose, sans liste, qui resume le sens general du debat et la decision ou direction la plus raisonnable.",
58
- summaryAnswerTitle: "Synthese:"
59
+ summaryAnswerTitle: "Synthese:",
60
+ ollamaSystemPrompt: "Tu participes a un debat technique orchestre. Reste precis, utile et honnete sur tes limites."
59
61
  },
60
62
  en: {
61
63
  subject: (topic) => `Subject: ${topic}`,
@@ -71,6 +73,7 @@ export const promptMessages = {
71
73
  cwd: (value) => `- Current directory: ${value}`,
72
74
  sessionStartedAt: (value) => `- Session started at: ${value}`,
73
75
  turnProgress: (turn, totalTurns) => `- Current turn: ${turn}/${totalTurns}`,
76
+ responseLanguageInstruction: "Required response language: English. Answer only in English, even if the subject or transcript contains another language.",
74
77
  objectiveTitle: "Objective:",
75
78
  debateObjectives: [
76
79
  "- Provide a useful, concrete, and concise answer.",
@@ -97,6 +100,7 @@ export const promptMessages = {
97
100
  actionsHeading: "### Proposed actions",
98
101
  conclusionHeading: "### Conclusion",
99
102
  finalProseInstruction: "A short prose summary paragraph, without a list, that captures the general meaning of the debate and the most reasonable decision or direction.",
100
- summaryAnswerTitle: "Summary:"
103
+ summaryAnswerTitle: "Summary:",
104
+ ollamaSystemPrompt: "You are taking part in an orchestrated technical debate. Stay precise, useful, and honest about your limits."
101
105
  }
102
106
  };
package/dist/new.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createInterface } from "node:readline/promises";
2
2
  import { stdin as input, stdout as output } from "node:process";
3
+ import { isAgentDetected } from "./agentRegistry.js";
3
4
  import { discoverLocalTools } from "./discovery.js";
4
5
  import { findPresetNameForPair } from "./presets.js";
5
6
  import { MAX_TURNS, turnsOrDefault, validateTurns } from "./limits.js";
@@ -136,25 +137,6 @@ function buildAgentChoices(config, discovery, messages) {
136
137
  })
137
138
  .sort((left, right) => Number(right.detected) - Number(left.detected) || left.name.localeCompare(right.name));
138
139
  }
139
- function isAgentDetected(name, config, discovery) {
140
- if (config.type === "ollama") {
141
- return discovery.ollama.available;
142
- }
143
- const normalized = normalizeCommandName(config.command || name);
144
- if (normalized === "codex")
145
- return discovery.codex.available;
146
- if (normalized === "claude")
147
- return discovery.claude.available;
148
- if (normalized === "gemini")
149
- return discovery.gemini.available;
150
- if (normalized === "agy")
151
- return discovery.antigravity.available;
152
- if (normalized === "antigravity")
153
- return discovery.antigravity.available;
154
- if (normalized === "opencode")
155
- return discovery.opencode.available;
156
- return true;
157
- }
158
140
  function agentStatus(_name, config, discovery, detected, messages) {
159
141
  if (config.type === "ollama") {
160
142
  return detected
@@ -248,13 +230,6 @@ function splitPaths(value) {
248
230
  .map((entry) => entry.trim())
249
231
  .filter(Boolean) ?? [];
250
232
  }
251
- function normalizeCommandName(command) {
252
- return command
253
- .split(/[\\/]/)
254
- .pop()
255
- ?.toLowerCase()
256
- .replace(/\.(exe|cmd|bat|ps1)$/i, "") ?? command.toLowerCase();
257
- }
258
233
  function isQuit(value) {
259
234
  return ["q", "quit", "exit"].includes(value.toLowerCase());
260
235
  }
@@ -1,4 +1,5 @@
1
1
  import { createAgent } from "./adapters/index.js";
2
+ import { AdapterError } from "./errors.js";
2
3
  import { createTranslator } from "./i18n.js";
3
4
  /**
4
5
  * Point d'entrée de l'orchestration.
@@ -36,18 +37,39 @@ export async function runDebate(config, options, renderer, messages = createTran
36
37
  const turn = index + 1;
37
38
  renderer?.turnStart(turn, options.turns, current.name, current.role);
38
39
  renderer?.thinkingStart(current.name, current.role);
39
- const response = await current.generate({
40
- topic: options.topic,
41
- turn,
42
- totalTurns: options.turns,
43
- selfName: current.name,
44
- peerName: peer.name,
45
- selfRole: current.role,
46
- language: options.language,
47
- session: options.session,
48
- files: options.files,
49
- transcript
50
- }).finally(() => renderer?.thinkingEnd());
40
+ let response;
41
+ try {
42
+ response = await current.generate({
43
+ topic: options.topic,
44
+ turn,
45
+ totalTurns: options.turns,
46
+ selfName: current.name,
47
+ peerName: peer.name,
48
+ selfRole: current.role,
49
+ language: options.language,
50
+ session: options.session,
51
+ files: options.files,
52
+ transcript
53
+ });
54
+ }
55
+ catch (error) {
56
+ const failure = toDebateFailure(error, {
57
+ phase: "debate",
58
+ agent: current.name,
59
+ role: current.role,
60
+ turn
61
+ });
62
+ renderer?.error(failure);
63
+ return {
64
+ options,
65
+ messages: transcript,
66
+ stopReason,
67
+ failure
68
+ };
69
+ }
70
+ finally {
71
+ renderer?.thinkingEnd();
72
+ }
51
73
  const message = {
52
74
  agent: current.name,
53
75
  role: current.role,
@@ -56,47 +78,53 @@ export async function runDebate(config, options, renderer, messages = createTran
56
78
  };
57
79
  transcript.push(message);
58
80
  renderer?.message(message.content);
59
- if (shouldStopOnAgreement(options, transcript)) {
81
+ if (shouldStopOnAgreement(options, transcript, messages)) {
60
82
  stopReason = messages.orchestrator.agreementStopReason;
61
83
  renderer?.notice(messages.orchestrator.earlyStop(stopReason));
62
84
  break;
63
85
  }
64
86
  }
65
- const summary = options.summaryEnabled
66
- ? await generateSummary(config, options, transcript, renderer, messages)
67
- : undefined;
87
+ let summary;
88
+ let failure;
89
+ if (options.summaryEnabled) {
90
+ try {
91
+ summary = await generateSummary(config, options, transcript, renderer, messages);
92
+ }
93
+ catch (error) {
94
+ failure = toDebateFailure(error, {
95
+ phase: "summary",
96
+ agent: options.summaryAgent ?? options.agentB,
97
+ turn: transcript.length + 1
98
+ });
99
+ renderer?.error(failure);
100
+ }
101
+ }
68
102
  return {
69
103
  options,
70
104
  messages: transcript,
71
105
  summary,
72
- stopReason
106
+ stopReason,
107
+ failure
73
108
  };
74
109
  }
75
110
  /**
76
111
  * Heuristique d'arrêt sur accord explicite.
77
112
  * Ne s'active qu'après un tour complet (nombre pair de messages) pour éviter les faux positifs.
113
+ * Les phrases d'accord proviennent du dictionnaire i18n pour suivre la langue d'interface.
78
114
  * Intentionnellement prudente : ne remplace pas une évaluation sémantique réelle.
79
115
  */
80
- function shouldStopOnAgreement(options, messages) {
81
- if (!options.earlyStopOnAgreement || messages.length < 2 || messages.length % 2 !== 0) {
116
+ function shouldStopOnAgreement(options, transcript, messages) {
117
+ if (!options.earlyStopOnAgreement || transcript.length < 2 || transcript.length % 2 !== 0) {
82
118
  return false;
83
119
  }
84
- const latest = normalizeForAgreement(messages[messages.length - 1]?.content ?? "");
120
+ const latest = normalizeForAgreement(transcript[transcript.length - 1]?.content ?? "");
85
121
  if (!latest) {
86
122
  return false;
87
123
  }
88
- const positivePatterns = [
89
- "accord complet",
90
- "accord total",
91
- "aucun desaccord",
92
- "aucune incertitude",
93
- "rien a trancher",
94
- "rien a ajouter",
95
- "question factuelle resolue"
96
- ];
97
- if (positivePatterns.some((pattern) => latest.includes(pattern))) {
124
+ if (messages.orchestrator.agreementPatterns.some((pattern) => latest.includes(pattern))) {
98
125
  return true;
99
126
  }
127
+ // Combinaison française historique : confirmation explicite + absence de point ouvert.
100
128
  return (latest.includes("confirme") || latest.includes("acte")) &&
101
129
  (latest.includes("aucun") || latest.includes("rien a trancher") || latest.includes("rien a ajouter"));
102
130
  }
@@ -164,6 +192,27 @@ async function generateSummary(config, options, transcript, renderer, messages =
164
192
  renderer?.message(summary.content);
165
193
  return summary;
166
194
  }
195
+ function toDebateFailure(error, context) {
196
+ if (error instanceof AdapterError) {
197
+ return {
198
+ phase: context.phase,
199
+ agent: context.agent ?? error.adapterName,
200
+ role: context.role,
201
+ turn: context.turn,
202
+ kind: error.kind,
203
+ message: error.message,
204
+ details: error.details
205
+ };
206
+ }
207
+ return {
208
+ phase: context.phase,
209
+ agent: context.agent,
210
+ role: context.role,
211
+ turn: context.turn,
212
+ kind: "unknown",
213
+ message: error instanceof Error ? error.message : String(error)
214
+ };
215
+ }
167
216
  /** Résout le model override pour un agent donné. Retourne `undefined` si l'agent n'est ni A ni B. */
168
217
  function modelForAgent(options, agent) {
169
218
  if (agent === options.agentA) {
package/dist/output.js CHANGED
@@ -5,12 +5,12 @@ import { createTranslator } from "./i18n.js";
5
5
  * Écrit le débat au format Markdown dans `outputDir`.
6
6
  * Crée le répertoire si absent. Retourne le chemin absolu du fichier créé.
7
7
  */
8
- export async function writeDebateMarkdown(outputDir, options, debateMessages, summary, stopReason, messages = createTranslator("fr")) {
8
+ export async function writeDebateMarkdown(outputDir, options, debateMessages, summary, stopReason, messages = createTranslator("fr"), failure) {
9
9
  const safeDate = new Date().toISOString().replace(/[:.]/g, "-");
10
10
  const fileName = `palabre-${slugifyTopic(options.topic)}-${safeDate}.debate.md`;
11
11
  const filePath = path.resolve(outputDir, fileName);
12
12
  await mkdir(path.dirname(filePath), { recursive: true });
13
- await writeFile(filePath, renderDebateMarkdown(options, debateMessages, summary, stopReason, messages), "utf8");
13
+ await writeFile(filePath, renderDebateMarkdown(options, debateMessages, summary, stopReason, messages, failure), "utf8");
14
14
  return filePath;
15
15
  }
16
16
  function slugifyTopic(topic) {
@@ -28,7 +28,7 @@ function slugifyTopic(topic) {
28
28
  * Produit la représentation Markdown complète du débat.
29
29
  * Fonction pure : aucun effet de bord sur le filesystem.
30
30
  */
31
- export function renderDebateMarkdown(options, debateMessages, summary, stopReason, messages = createTranslator("fr")) {
31
+ export function renderDebateMarkdown(options, debateMessages, summary, stopReason, messages = createTranslator("fr"), failure) {
32
32
  const lines = [
33
33
  messages.output.title,
34
34
  "",
@@ -44,6 +44,9 @@ export function renderDebateMarkdown(options, debateMessages, summary, stopReaso
44
44
  for (const message of debateMessages) {
45
45
  lines.push(`### ${message.agent} (${message.role})`, "", normalizeMarkdownForWindowsPreview(message.content.trim()), "");
46
46
  }
47
+ if (failure) {
48
+ lines.push("---", "", messages.output.failureTitle, "", ...renderFailureBlock(failure, messages), "");
49
+ }
47
50
  lines.push("---", "", messages.output.finalSummaryTitle, "", ...renderSummaryBlock(options, summary, messages));
48
51
  return `${lines.join("\n")}\n`;
49
52
  }
@@ -67,6 +70,20 @@ function renderSummaryBlock(options, summary, messages) {
67
70
  ""
68
71
  ];
69
72
  }
73
+ function renderFailureBlock(failure, messages) {
74
+ const rows = [
75
+ [messages.output.fields.failurePhase, failure.phase],
76
+ [messages.output.fields.failureAgent, failure.agent ?? messages.output.no],
77
+ [messages.output.fields.failureTurn, failure.turn === undefined ? messages.output.no : String(failure.turn)],
78
+ [messages.output.fields.failureKind, failure.kind],
79
+ [messages.output.fields.failureMessage, failure.message]
80
+ ];
81
+ return [
82
+ `| ${messages.output.tableField} | ${messages.output.tableValue} |`,
83
+ "| --- | --- |",
84
+ ...rows.map(([label, value]) => `| ${escapeTableCell(label)} | ${escapeTableCell(value)} |`)
85
+ ];
86
+ }
70
87
  function normalizeMarkdownForWindowsPreview(content) {
71
88
  return content.replace(/:\*\*/g, "&#58;**");
72
89
  }
package/dist/presets.js CHANGED
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import { detectionForCommand } from "./agentRegistry.js";
2
2
  const presets = {
3
3
  "codex-claude": {
4
4
  agentA: "codex",
@@ -181,7 +181,7 @@ function checkAgentAvailability(agentName, config, discovery, messages) {
181
181
  }
182
182
  return available(agentName);
183
183
  }
184
- const detection = knownCliDetection(agent, discovery);
184
+ const detection = detectionForCommand(agent.command, discovery);
185
185
  if (!detection) {
186
186
  // Les CLIs custom déclarées par l'utilisateur restent considérées utilisables :
187
187
  // Palabre ne peut pas connaître leur sémantique sans les lancer.
@@ -191,25 +191,6 @@ function checkAgentAvailability(agentName, config, discovery, messages) {
191
191
  ? available(agentName)
192
192
  : unavailable(agentName, messages?.presets.missingCommand(agentName, detection.command) ?? `commande non détectée pour ${agentName}: ${detection.command}`);
193
193
  }
194
- function knownCliDetection(agent, discovery) {
195
- const command = normalizeCommandName(agent.command);
196
- if (command === "codex")
197
- return discovery.codex;
198
- if (command === "claude")
199
- return discovery.claude;
200
- if (command === "gemini")
201
- return discovery.gemini;
202
- if (command === "agy")
203
- return discovery.antigravity;
204
- if (command === "antigravity")
205
- return discovery.antigravity;
206
- if (command === "opencode")
207
- return discovery.opencode;
208
- return undefined;
209
- }
210
- function normalizeCommandName(command) {
211
- return path.basename(command).toLowerCase().replace(/\.(exe|cmd|ps1|bat)$/i, "");
212
- }
213
194
  function available(agent) {
214
195
  return { agent, available: true, reason: "" };
215
196
  }
package/dist/prompt.js CHANGED
@@ -25,6 +25,8 @@ export function formatAgentPrompt(input) {
25
25
  messages.sessionStartedAt(input.session.startedAt),
26
26
  messages.turnProgress(input.turn, input.totalTurns),
27
27
  "",
28
+ messages.responseLanguageInstruction,
29
+ "",
28
30
  messages.objectiveTitle,
29
31
  ...messages.debateObjectives,
30
32
  "",
@@ -56,6 +58,8 @@ function formatSummaryPrompt(input, messages) {
56
58
  messages.cwd(input.session.cwd),
57
59
  messages.sessionStartedAt(input.session.startedAt),
58
60
  "",
61
+ messages.responseLanguageInstruction,
62
+ "",
59
63
  messages.objectiveTitle,
60
64
  ...messages.summaryObjectives,
61
65
  "",
@@ -99,6 +99,10 @@ class PrettyConsoleRenderer {
99
99
  ""
100
100
  ].join("\n"));
101
101
  }
102
+ error(failure) {
103
+ this.thinkingEnd();
104
+ process.stderr.write(`\n${this.c("red", this.messages.common.errorPrefix)} ${formatFailureLocation(failure, this.messages)}: ${failure.message}\n`);
105
+ }
102
106
  /** Affiche le chemin du fichier de sortie en vert à la fin du débat. */
103
107
  done(outputPath) {
104
108
  process.stdout.write(`\n\n${this.c("green", this.messages.renderers.exported(outputPath))}\n\n`);
@@ -175,6 +179,9 @@ class PlainConsoleRenderer {
175
179
  summaryStart(agent, role) {
176
180
  process.stdout.write(`\n[${this.messages.renderers.summaryTitle}] ${agent} (${role})...\n`);
177
181
  }
182
+ error(failure) {
183
+ process.stderr.write(`\n${this.messages.common.errorPrefix}: ${formatFailureLocation(failure, this.messages)}: ${failure.message}\n`);
184
+ }
178
185
  /** Affiche le chemin du fichier de sortie à la fin du débat. */
179
186
  done(outputPath) {
180
187
  process.stdout.write(`\n${this.messages.renderers.exported(outputPath)}\n`);
@@ -220,6 +227,13 @@ function formatContext(options, messages) {
220
227
  }
221
228
  return messages.renderers.injectedFiles(count);
222
229
  }
230
+ function formatFailureLocation(failure, messages) {
231
+ if (failure.phase === "summary") {
232
+ return messages.renderers.summaryTitle;
233
+ }
234
+ const turn = failure.turn === undefined ? "" : `, turn ${failure.turn}`;
235
+ return `${failure.agent ?? "?"} (${failure.role ?? "?"}${turn})`;
236
+ }
223
237
  /** Codes d'échappement ANSI utilisés par `PrettyConsoleRenderer`. */
224
238
  const codes = {
225
239
  reset: "\u001b[0m",
@@ -228,6 +242,7 @@ const codes = {
228
242
  cyan: "\u001b[36m",
229
243
  green: "\u001b[32m",
230
244
  magenta: "\u001b[35m",
245
+ red: "\u001b[31m",
231
246
  yellow: "\u001b[33m",
232
247
  orange: "\u001b[38;5;208m",
233
248
  pink: "\u001b[38;5;205m"
@@ -101,6 +101,10 @@ export class NdjsonRenderer {
101
101
  this.currentRole = role;
102
102
  this.emit({ type: "summary-start", agent, role });
103
103
  }
104
+ /** Émet une erreur runtime structurée. */
105
+ error(failure) {
106
+ this.emit({ type: "error", ...failure });
107
+ }
104
108
  /** Émet `done` avec le chemin du `.debate.md` écrit. */
105
109
  done(outputPath) {
106
110
  this.emit({ type: "done", outputPath });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palabre",
3
- "version": "0.6.0",
3
+ "version": "0.6.3",
4
4
  "description": "Orchestrateur de debat entre agents IA locaux, CLIs et Ollama.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -38,7 +38,8 @@
38
38
  "prepack": "pnpm build",
39
39
  "start": "node ./dist/index.js",
40
40
  "test": "pnpm build:test && node --test .tmp/test-dist/tests/*.test.js",
41
- "build:test": "node -e \"fs.rmSync('.tmp/test-dist',{recursive:true,force:true})\" && tsc -p tsconfig.test.json"
41
+ "build:test": "node -e \"fs.rmSync('.tmp/test-dist',{recursive:true,force:true})\" && tsc -p tsconfig.test.json",
42
+ "smoke:real-presets": "node -e \"fs.rmSync('.tmp/smoke-real',{recursive:true,force:true})\" && tsc --target ES2022 --module NodeNext --moduleResolution NodeNext --types node --outDir .tmp/smoke-real scripts/smoke_real_presets.ts && node .tmp/smoke-real/smoke_real_presets.js"
42
43
  },
43
44
  "engines": {
44
45
  "node": ">=20"