palabre 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -68
- package/dist/adapters/cli-pty.js +1 -1
- package/dist/adapters/cli.js +33 -3
- package/dist/adapters/ollama.js +1 -1
- package/dist/agentRegistry.js +3 -3
- package/dist/args.js +21 -1
- package/dist/config.js +58 -24
- package/dist/configWizard.js +12 -2
- package/dist/context.js +5 -1
- package/dist/discovery.js +3 -3
- package/dist/doctor.js +4 -1
- package/dist/history.js +85 -0
- package/dist/index.js +742 -94
- package/dist/messages/agents.js +4 -2
- package/dist/messages/common.js +4 -0
- package/dist/messages/config.js +18 -8
- package/dist/messages/help.js +116 -10
- package/dist/messages/index.js +2 -0
- package/dist/messages/init.js +2 -2
- package/dist/messages/new.js +14 -0
- package/dist/messages/output.js +10 -0
- package/dist/messages/preview.js +4 -2
- package/dist/messages/prompt.js +46 -2
- package/dist/messages/renderers.js +2 -2
- package/dist/messages/tui.js +228 -0
- package/dist/messages/update.js +16 -2
- package/dist/new.js +158 -4
- package/dist/orchestrator.js +168 -9
- package/dist/output.js +31 -8
- package/dist/presets.js +39 -39
- package/dist/prompt.js +61 -10
- package/dist/renderers/console.js +39 -3
- package/dist/renderers/ndjson.js +30 -1
- package/dist/renderers/tui.js +1055 -0
- package/dist/tuiState.js +31 -0
- package/dist/update.js +2 -0
- package/package.json +2 -1
- package/palabre.config.example.json +0 -17
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { assertRunnableConfig, configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, resolveOutputDir, setOllamaModel,
|
|
2
|
+
import { assertRunnableConfig, configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, resolveOutputDir, setOllamaModel, syncDetectedAgentsDetailed, syncOllamaModel, writeExampleConfig } from "./config.js";
|
|
3
3
|
import { loadProjectInputs } from "./context.js";
|
|
4
4
|
import { buildContextScan } from "./contextScan.js";
|
|
5
5
|
import { discoverLocalTools } from "./discovery.js";
|
|
@@ -7,17 +7,20 @@ import { runDoctor } from "./doctor.js";
|
|
|
7
7
|
import { AdapterError, formatAdapterError } from "./errors.js";
|
|
8
8
|
import { runConfigWizard } from "./configWizard.js";
|
|
9
9
|
import { createTranslator, DEFAULT_LANGUAGE, parseLanguage, resolveLanguage } from "./i18n.js";
|
|
10
|
-
import { DEFAULT_TURNS, parseTurnsFlag, turnsOrDefault } from "./limits.js";
|
|
10
|
+
import { DEFAULT_TURNS, parseTurnsFlag, turnsOrDefault, validateTurns } from "./limits.js";
|
|
11
11
|
import { formatAgentPrompt } from "./prompt.js";
|
|
12
12
|
import { runNewWizard } from "./new.js";
|
|
13
13
|
import { listPresetNames, listPresetsWithAvailability, resolvePreset } from "./presets.js";
|
|
14
|
+
import { listHistoryEntries } from "./history.js";
|
|
14
15
|
import { createConsoleRenderer } from "./renderers/console.js";
|
|
15
16
|
import { createNdjsonRenderer } from "./renderers/ndjson.js";
|
|
16
|
-
import {
|
|
17
|
+
import { createTuiRenderer, promptTuiAgentsWizard, promptTuiConfigCommand, promptTuiHomeTopic, promptTuiRolesWizard, renderTuiConfig, renderTuiHelp, renderTuiHistory, renderTuiHome } from "./renderers/tui.js";
|
|
18
|
+
import { MAX_ASK_AGENTS, runAsk, runDebate } from "./orchestrator.js";
|
|
17
19
|
import { writeDebateMarkdown } from "./output.js";
|
|
18
20
|
import { applySourceUpdate, formatUpdateInstructions, getUpdateInfo } from "./update.js";
|
|
19
21
|
import { createSessionContext } from "./session.js";
|
|
20
22
|
import { getStringListFlag, parseArgs } from "./args.js";
|
|
23
|
+
import { askAgentSeedsForMode, clearTuiRunOverrides } from "./tuiState.js";
|
|
21
24
|
import { detectedAgentNames, detectionForCommand } from "./agentRegistry.js";
|
|
22
25
|
import { getPackageVersion } from "./version.js";
|
|
23
26
|
/** Point d'entrée principal du CLI Palabre. Dispatche vers la commande appropriée selon les arguments. */
|
|
@@ -35,7 +38,7 @@ async function main() {
|
|
|
35
38
|
return;
|
|
36
39
|
}
|
|
37
40
|
if (parsed.command === "doctor") {
|
|
38
|
-
const result = await runDoctor(optionalString(parsed.flags.config), Boolean(parsed.flags.plain), optionalString(parsed.flags.language));
|
|
41
|
+
const result = await runDoctor(optionalString(parsed.flags.config), Boolean(parsed.flags.plain || parsed.flags.terminal), optionalString(parsed.flags.language));
|
|
39
42
|
console.log(result.output);
|
|
40
43
|
process.exitCode = result.ok ? 0 : 1;
|
|
41
44
|
return;
|
|
@@ -52,6 +55,10 @@ async function main() {
|
|
|
52
55
|
await runPresetsCommand(parsed.flags);
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
58
|
+
if (parsed.command === "history" || parsed.command === "historique") {
|
|
59
|
+
await runHistoryCommand(parsed.flags);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
55
62
|
if (parsed.command === "context") {
|
|
56
63
|
await runContextCommand(parsed.flags, parsed.positionals);
|
|
57
64
|
return;
|
|
@@ -94,91 +101,234 @@ async function main() {
|
|
|
94
101
|
return;
|
|
95
102
|
}
|
|
96
103
|
const configPath = optionalString(parsed.flags.config) ?? await resolveDefaultConfigPath();
|
|
104
|
+
let config;
|
|
105
|
+
let tuiNotice;
|
|
97
106
|
if (!(await configExists(configPath))) {
|
|
98
|
-
|
|
107
|
+
config = createConfigFromDiscovery(await discoverLocalTools());
|
|
99
108
|
config.language = resolveLanguage({
|
|
100
109
|
explicitLanguage: optionalString(parsed.flags.language),
|
|
101
110
|
configLanguage: config.language
|
|
102
111
|
});
|
|
103
112
|
const messages = createTranslator(config.language);
|
|
104
113
|
await writeExampleConfig(configPath, config);
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
if (!shouldOpenTuiHome(parsed)) {
|
|
115
|
+
console.log(messages.init.editConfigThenRerun(configPath));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
tuiNotice = messages.init.configCreated(configPath);
|
|
107
119
|
}
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
else {
|
|
121
|
+
config = await loadConfig(configPath);
|
|
122
|
+
}
|
|
123
|
+
let language = resolveLanguage({
|
|
110
124
|
explicitLanguage: optionalString(parsed.flags.language),
|
|
111
125
|
configLanguage: config.language
|
|
112
126
|
});
|
|
113
|
-
|
|
127
|
+
let messages = createTranslator(language);
|
|
114
128
|
assertRunnableConfig(config, messages, configPath);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
129
|
+
let stayInTuiAfterSession = false;
|
|
130
|
+
let hasCompletedTuiSession = false;
|
|
131
|
+
let resetTuiRunOverridesOnNextTopic = false;
|
|
132
|
+
let tuiMode = config.defaults?.mode ?? "debate";
|
|
133
|
+
let tuiVersion = "";
|
|
134
|
+
const handleTuiHomeInput = async (tuiInput) => {
|
|
135
|
+
if (!tuiInput) {
|
|
136
|
+
return "quit";
|
|
137
|
+
}
|
|
138
|
+
if (tuiInput.kind === "help") {
|
|
139
|
+
renderTuiHelp(messages);
|
|
140
|
+
const nextInput = await promptTuiHomeTopic(tuiMode, messages);
|
|
141
|
+
return handleTuiHomeInput(nextInput);
|
|
142
|
+
}
|
|
143
|
+
if (tuiInput.kind === "history") {
|
|
144
|
+
renderTuiHistory(await listHistoryEntries(resolveOutputDir(config.outputDir)), messages);
|
|
145
|
+
const nextInput = await promptTuiHomeTopic(tuiMode, messages);
|
|
146
|
+
return handleTuiHomeInput(nextInput);
|
|
120
147
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
148
|
+
if (tuiInput.kind === "home") {
|
|
149
|
+
return "continue";
|
|
150
|
+
}
|
|
151
|
+
if (tuiInput.kind === "roles") {
|
|
152
|
+
const result = await runTuiRolesWizard(configPath, config, messages, tuiMode, tuiInput.roles);
|
|
153
|
+
if (result.quit)
|
|
154
|
+
return "quit";
|
|
155
|
+
tuiNotice = result.notice;
|
|
156
|
+
return "continue";
|
|
157
|
+
}
|
|
158
|
+
if (tuiInput.kind === "agents") {
|
|
159
|
+
const result = await runTuiAgentsWizard(configPath, config, messages, tuiMode, tuiInput.agents);
|
|
160
|
+
if (result.quit)
|
|
161
|
+
return "quit";
|
|
162
|
+
tuiNotice = result.notice;
|
|
163
|
+
resetTuiRunOverridesOnNextTopic ||= Boolean(result.changedRunDefaults);
|
|
164
|
+
return "continue";
|
|
165
|
+
}
|
|
166
|
+
if (tuiInput.kind === "mode") {
|
|
167
|
+
tuiMode = tuiInput.mode;
|
|
168
|
+
return "continue";
|
|
169
|
+
}
|
|
170
|
+
if (tuiInput.kind === "config") {
|
|
171
|
+
const result = await runTuiConfigLoop(configPath, config, messages, tuiMode);
|
|
172
|
+
if (result.quit)
|
|
173
|
+
return "quit";
|
|
174
|
+
tuiMode = result.mode;
|
|
175
|
+
resetTuiRunOverridesOnNextTopic ||= result.changedRunDefaults;
|
|
176
|
+
language = resolveLanguage({ explicitLanguage: optionalString(parsed.flags.language), configLanguage: config.language });
|
|
177
|
+
messages = createTranslator(language);
|
|
178
|
+
return "continue";
|
|
179
|
+
}
|
|
180
|
+
if (tuiInput.kind === "new") {
|
|
181
|
+
parsed.command = "new";
|
|
182
|
+
parsed.commandExplicit = true;
|
|
183
|
+
delete parsed.flags.topic;
|
|
184
|
+
return "run";
|
|
185
|
+
}
|
|
186
|
+
if (tuiInput.kind === "retry") {
|
|
187
|
+
if (!optionalString(parsed.flags.topic)) {
|
|
188
|
+
tuiNotice = messages.tui.retryUnavailable;
|
|
189
|
+
return "continue";
|
|
190
|
+
}
|
|
191
|
+
return "retry";
|
|
192
|
+
}
|
|
193
|
+
parsed.command = "";
|
|
194
|
+
parsed.commandExplicit = false;
|
|
195
|
+
if (hasCompletedTuiSession || resetTuiRunOverridesOnNextTopic) {
|
|
196
|
+
clearTuiRunOverrides(parsed.flags);
|
|
197
|
+
resetTuiRunOverridesOnNextTopic = false;
|
|
198
|
+
}
|
|
199
|
+
parsed.flags.topic = tuiInput.topic;
|
|
200
|
+
return "run";
|
|
169
201
|
};
|
|
170
|
-
if (parsed
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
202
|
+
if (shouldOpenTuiHome(parsed)) {
|
|
203
|
+
const syncResult = await syncInteractiveDetectedAgents(configPath, config);
|
|
204
|
+
if (!tuiNotice && syncResult.addedAgents.length > 0) {
|
|
205
|
+
tuiNotice = messages.config.syncAdded(configPath, syncResult.addedAgents.join(", "));
|
|
206
|
+
}
|
|
207
|
+
stayInTuiAfterSession = true;
|
|
208
|
+
tuiVersion = await getPackageVersion();
|
|
209
|
+
for (;;) {
|
|
210
|
+
renderTuiHome(config, configPath, messages, { mode: tuiMode, version: tuiVersion });
|
|
211
|
+
const tuiInput = await promptTuiHomeTopic(tuiMode, messages, { notice: tuiNotice });
|
|
212
|
+
tuiNotice = undefined;
|
|
213
|
+
const action = await handleTuiHomeInput(tuiInput);
|
|
214
|
+
if (action === "quit")
|
|
215
|
+
return;
|
|
216
|
+
if (action === "continue")
|
|
217
|
+
continue;
|
|
218
|
+
parsed.flags.mode = tuiMode;
|
|
219
|
+
parsed.flags.renderer = "tui";
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
174
222
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
223
|
+
for (;;) {
|
|
224
|
+
if (parsed.command === "new") {
|
|
225
|
+
const selection = await runNewWizard(config, messages);
|
|
226
|
+
if (!selection) {
|
|
227
|
+
console.log(messages.new.cancelled);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
parsed.flags["agent-a"] = selection.agentA;
|
|
231
|
+
parsed.flags["agent-b"] = selection.agentB;
|
|
232
|
+
parsed.flags.topic = selection.topic;
|
|
233
|
+
if (selection.modelA)
|
|
234
|
+
parsed.flags["model-a"] = selection.modelA;
|
|
235
|
+
if (selection.modelB)
|
|
236
|
+
parsed.flags["model-b"] = selection.modelB;
|
|
237
|
+
if (selection.turns)
|
|
238
|
+
parsed.flags.turns = String(selection.turns);
|
|
239
|
+
if (selection.summaryAgent)
|
|
240
|
+
parsed.flags["summary-agent"] = selection.summaryAgent;
|
|
241
|
+
if (selection.summaryModel)
|
|
242
|
+
parsed.flags["summary-model"] = selection.summaryModel;
|
|
243
|
+
if (selection.summaryEnabled === false)
|
|
244
|
+
parsed.flags["no-summary"] = true;
|
|
245
|
+
if (selection.showPrompt)
|
|
246
|
+
parsed.flags["show-prompt"] = true;
|
|
247
|
+
if (selection.plainOutput)
|
|
248
|
+
parsed.flags.plain = true;
|
|
249
|
+
if (selection.files.length > 0)
|
|
250
|
+
parsed.flags.files = selection.files;
|
|
251
|
+
if (selection.context.length > 0)
|
|
252
|
+
parsed.flags.context = selection.context;
|
|
253
|
+
if (selection.mode)
|
|
254
|
+
parsed.flags.mode = selection.mode;
|
|
255
|
+
if (selection.askAgents && selection.askAgents.length > 0)
|
|
256
|
+
parsed.flags.agents = selection.askAgents;
|
|
257
|
+
parsed.command = "";
|
|
258
|
+
parsed.commandExplicit = false;
|
|
259
|
+
}
|
|
260
|
+
const topic = optionalString(parsed.flags.topic) ?? "";
|
|
261
|
+
const context = await loadProjectInputs(getStringListFlag(parsed.flags.files), getStringListFlag(parsed.flags.context), process.cwd(), messages);
|
|
262
|
+
const presetName = optionalString(parsed.flags.preset);
|
|
263
|
+
const preset = presetName ? resolvePreset(presetName, messages) : undefined;
|
|
264
|
+
if (!topic) {
|
|
265
|
+
throw new Error(messages.common.topicRequired);
|
|
266
|
+
}
|
|
267
|
+
const mode = parseModeFlag(optionalString(parsed.flags.mode) ?? config.defaults?.mode, messages);
|
|
268
|
+
const explicitAskAgents = getStringListFlag(parsed.flags.agents);
|
|
269
|
+
const askAgentSeeds = askAgentSeedsForMode(mode, explicitAskAgents, config.defaults?.askAgents);
|
|
270
|
+
const agentA = resolveAgentName("agent A", parsed.flags["agent-a"], preset?.agentA, askAgentSeeds[0] ?? config.defaults?.agentA, messages);
|
|
271
|
+
const agentB = resolveAgentName("agent B", parsed.flags["agent-b"], preset?.agentB, askAgentSeeds[1] ?? askAgentSeeds[0] ?? config.defaults?.agentB, messages);
|
|
272
|
+
const askAgents = mode === "ask" ? resolveAskAgents(explicitAskAgents, config.defaults?.askAgents, [agentA, agentB], messages) : undefined;
|
|
273
|
+
const options = {
|
|
274
|
+
mode,
|
|
275
|
+
language,
|
|
276
|
+
topic,
|
|
277
|
+
agentA,
|
|
278
|
+
agentB,
|
|
279
|
+
askAgents,
|
|
280
|
+
turns: parseTurnsFlag(parsed.flags.turns, config.defaults?.turns ?? DEFAULT_TURNS, "--turns", messages),
|
|
281
|
+
session: createSessionContext(),
|
|
282
|
+
files: context.files,
|
|
283
|
+
modelA: optionalString(parsed.flags["model-a"]),
|
|
284
|
+
modelB: optionalString(parsed.flags["model-b"]),
|
|
285
|
+
pullModels: Boolean(parsed.flags["pull-models"]),
|
|
286
|
+
summaryAgent: resolveSummaryAgentOption(parsed.flags["summary-agent"], config.defaults, mode),
|
|
287
|
+
summaryModel: optionalString(parsed.flags["summary-model"]),
|
|
288
|
+
summaryEnabled: !parsed.flags["no-summary"],
|
|
289
|
+
earlyStopOnAgreement: !parsed.flags["no-early-stop"],
|
|
290
|
+
plainOutput: Boolean(parsed.flags.plain || parsed.flags.terminal),
|
|
291
|
+
signal: debateAbortSignal()
|
|
292
|
+
};
|
|
293
|
+
if (parsed.flags["show-prompt"]) {
|
|
294
|
+
printContextWarnings(context.warnings, messages);
|
|
295
|
+
printPromptPreview(config, options, language, messages);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
process.exitCode = undefined;
|
|
299
|
+
const renderer = createRendererFromFlags(parsed.flags, options.plainOutput, config.defaults?.interface, messages);
|
|
300
|
+
context.warnings.forEach((warning) => renderer.warning(warning));
|
|
301
|
+
const result = options.mode === "ask"
|
|
302
|
+
? await runAsk(config, options, renderer, messages)
|
|
303
|
+
: await runDebate(config, options, renderer, messages);
|
|
304
|
+
const outputPath = await writeDebateMarkdown(resolveOutputDir(config.outputDir), result.options, result.messages, result.summary, result.stopReason, messages, result.failure);
|
|
305
|
+
renderer.done(outputPath);
|
|
306
|
+
hasCompletedTuiSession = stayInTuiAfterSession;
|
|
307
|
+
if (result.failure) {
|
|
308
|
+
process.exitCode = result.failure.kind === "cancelled" ? 130 : 1;
|
|
309
|
+
}
|
|
310
|
+
if (!stayInTuiAfterSession) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
tuiMode = mode;
|
|
314
|
+
for (;;) {
|
|
315
|
+
const nextInput = await promptTuiHomeTopic(tuiMode, messages, { notice: tuiNotice });
|
|
316
|
+
tuiNotice = undefined;
|
|
317
|
+
const action = await handleTuiHomeInput(nextInput);
|
|
318
|
+
if (action === "quit")
|
|
319
|
+
return;
|
|
320
|
+
if (action === "continue") {
|
|
321
|
+
renderTuiHome(config, configPath, messages, { mode: tuiMode, version: tuiVersion });
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (action === "retry") {
|
|
325
|
+
parsed.flags.renderer = "tui";
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
parsed.flags.mode = tuiMode;
|
|
329
|
+
parsed.flags.renderer = "tui";
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
182
332
|
}
|
|
183
333
|
}
|
|
184
334
|
function debateAbortSignal() {
|
|
@@ -245,20 +395,32 @@ async function runConfigCommand(flags) {
|
|
|
245
395
|
}
|
|
246
396
|
if (flags["sync-agents"]) {
|
|
247
397
|
const discovery = await discoverLocalTools();
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
398
|
+
const result = syncDetectedAgentsDetailed(config, discovery);
|
|
399
|
+
if (!result.changed) {
|
|
250
400
|
console.log(messages.config.syncNoMissing(configPath));
|
|
251
401
|
return;
|
|
252
402
|
}
|
|
253
403
|
await writeExampleConfig(configPath, config);
|
|
254
|
-
console.log(
|
|
404
|
+
console.log(result.addedAgents.length > 0
|
|
405
|
+
? messages.config.syncAdded(configPath, result.addedAgents.join(", "))
|
|
406
|
+
: messages.config.syncRefreshed(configPath));
|
|
255
407
|
return;
|
|
256
408
|
}
|
|
257
409
|
const defaultAgents = getStringListFlag(flags["set-defaults"]);
|
|
258
410
|
const hasTurnsFlag = flags.turns !== undefined;
|
|
259
411
|
const summaryAgentValue = optionalString(flags["summary-agent"]);
|
|
412
|
+
const askSummaryAgentValue = optionalString(flags["ask-summary-agent"]);
|
|
413
|
+
const interfaceValue = optionalString(flags.interface);
|
|
414
|
+
const modeValue = optionalString(flags.mode);
|
|
415
|
+
const askAgentsValue = getStringListFlag(flags["ask-agents"]);
|
|
260
416
|
const languageValue = explicitLanguage;
|
|
261
|
-
const changesDefaults = defaultAgents.length > 0
|
|
417
|
+
const changesDefaults = defaultAgents.length > 0
|
|
418
|
+
|| hasTurnsFlag
|
|
419
|
+
|| summaryAgentValue !== undefined
|
|
420
|
+
|| askSummaryAgentValue !== undefined
|
|
421
|
+
|| interfaceValue !== undefined
|
|
422
|
+
|| modeValue !== undefined
|
|
423
|
+
|| askAgentsValue.length > 0;
|
|
262
424
|
if (changesDefaults || languageValue !== undefined) {
|
|
263
425
|
const nextDefaults = { ...(config.defaults ?? {}) };
|
|
264
426
|
if (defaultAgents.length > 0) {
|
|
@@ -283,6 +445,24 @@ async function runConfigCommand(flags) {
|
|
|
283
445
|
nextDefaults.summaryAgent = summaryAgentValue;
|
|
284
446
|
}
|
|
285
447
|
}
|
|
448
|
+
if (askSummaryAgentValue !== undefined) {
|
|
449
|
+
if (isNoneValue(askSummaryAgentValue)) {
|
|
450
|
+
delete nextDefaults.askSummaryAgent;
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
assertKnownAgent(config, askSummaryAgentValue, "defaults.askSummaryAgent", messages);
|
|
454
|
+
nextDefaults.askSummaryAgent = askSummaryAgentValue;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (interfaceValue !== undefined) {
|
|
458
|
+
nextDefaults.interface = parseInterfaceFlag(interfaceValue, messages);
|
|
459
|
+
}
|
|
460
|
+
if (modeValue !== undefined) {
|
|
461
|
+
nextDefaults.mode = parseModeFlag(modeValue, messages);
|
|
462
|
+
}
|
|
463
|
+
if (askAgentsValue.length > 0) {
|
|
464
|
+
nextDefaults.askAgents = normalizeAskAgentsForConfig(config, askAgentsValue, messages);
|
|
465
|
+
}
|
|
286
466
|
if (languageValue !== undefined) {
|
|
287
467
|
config.language = parseLanguage(languageValue, "--language");
|
|
288
468
|
}
|
|
@@ -301,6 +481,357 @@ async function runConfigCommand(flags) {
|
|
|
301
481
|
}
|
|
302
482
|
await runConfigWizard(configPath, config, messages);
|
|
303
483
|
}
|
|
484
|
+
async function runTuiConfigLoop(configPath, config, messages, initialMode) {
|
|
485
|
+
let mode = initialMode;
|
|
486
|
+
let notice;
|
|
487
|
+
let currentMessages = messages;
|
|
488
|
+
let changedRunDefaults = false;
|
|
489
|
+
for (;;) {
|
|
490
|
+
renderTuiConfig(config, configPath, mode, currentMessages, { message: notice });
|
|
491
|
+
notice = undefined;
|
|
492
|
+
const input = await promptTuiConfigCommand(mode, currentMessages);
|
|
493
|
+
if (input.kind === "quit") {
|
|
494
|
+
return { mode, quit: true, changedRunDefaults };
|
|
495
|
+
}
|
|
496
|
+
if (input.kind === "back") {
|
|
497
|
+
return { mode, quit: false, changedRunDefaults };
|
|
498
|
+
}
|
|
499
|
+
if (input.kind === "unknown") {
|
|
500
|
+
notice = input.message;
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
if (input.kind === "mode") {
|
|
504
|
+
mode = mode === "ask" ? "debate" : "ask";
|
|
505
|
+
config.defaults = { ...(config.defaults ?? {}), mode };
|
|
506
|
+
await writeExampleConfig(configPath, config);
|
|
507
|
+
changedRunDefaults = true;
|
|
508
|
+
notice = mode === "ask" ? currentMessages.tui.askDefaultMode : currentMessages.tui.debateDefaultMode;
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (input.kind === "default-mode") {
|
|
512
|
+
config.defaults = { ...(config.defaults ?? {}), mode };
|
|
513
|
+
await writeExampleConfig(configPath, config);
|
|
514
|
+
changedRunDefaults = true;
|
|
515
|
+
notice = mode === "ask" ? currentMessages.tui.askDefaultMode : currentMessages.tui.debateDefaultMode;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (input.kind === "interface") {
|
|
519
|
+
config.defaults = { ...(config.defaults ?? {}), interface: input.interfaceName };
|
|
520
|
+
await writeExampleConfig(configPath, config);
|
|
521
|
+
notice = currentMessages.tui.interfaceDefault(input.interfaceName);
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (input.kind === "language") {
|
|
525
|
+
config.language = parseLanguage(input.language, "--language");
|
|
526
|
+
await writeExampleConfig(configPath, config);
|
|
527
|
+
currentMessages = createTranslator(config.language ?? DEFAULT_LANGUAGE);
|
|
528
|
+
notice = currentMessages.tui.languageUpdated(input.language);
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
if (input.kind === "agents") {
|
|
532
|
+
try {
|
|
533
|
+
const agentsInput = input.agents.length > 0
|
|
534
|
+
? { kind: "agents", agents: input.agents }
|
|
535
|
+
: await promptTuiAgentsWizard(config, mode, currentMessages);
|
|
536
|
+
if (agentsInput.kind === "quit") {
|
|
537
|
+
return { mode, quit: true, changedRunDefaults };
|
|
538
|
+
}
|
|
539
|
+
if (agentsInput.kind === "back" || agentsInput.agents.length === 0) {
|
|
540
|
+
notice = currentMessages.tui.agentsUnchanged;
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
if (mode === "ask") {
|
|
544
|
+
const agents = normalizeTuiAskAgents(config, agentsInput.agents, currentMessages);
|
|
545
|
+
config.defaults = { ...(config.defaults ?? {}), askAgents: agents };
|
|
546
|
+
await writeExampleConfig(configPath, config);
|
|
547
|
+
changedRunDefaults = true;
|
|
548
|
+
notice = currentMessages.tui.askAgentsUpdated(agents.join(", "));
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
const [agentA, agentB] = normalizeTuiDebateAgents(config, agentsInput.agents, currentMessages);
|
|
552
|
+
config.defaults = { ...(config.defaults ?? {}), agentA, agentB };
|
|
553
|
+
await writeExampleConfig(configPath, config);
|
|
554
|
+
changedRunDefaults = true;
|
|
555
|
+
notice = currentMessages.tui.debateAgentsUpdated(`${agentA} <-> ${agentB}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
catch (error) {
|
|
559
|
+
notice = error instanceof Error ? error.message : String(error);
|
|
560
|
+
}
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (input.kind === "roles") {
|
|
564
|
+
try {
|
|
565
|
+
const rolesInput = input.roles.length > 0
|
|
566
|
+
? { kind: "roles", roles: input.roles }
|
|
567
|
+
: await promptTuiRolesWizard(config, mode, currentMessages);
|
|
568
|
+
if (rolesInput.kind === "quit") {
|
|
569
|
+
return { mode, quit: true, changedRunDefaults };
|
|
570
|
+
}
|
|
571
|
+
if (rolesInput.kind === "back" || rolesInput.roles.length === 0) {
|
|
572
|
+
notice = currentMessages.tui.rolesUnchanged;
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
notice = applyTuiRoles(config, mode, rolesInput.roles, currentMessages);
|
|
576
|
+
await writeExampleConfig(configPath, config);
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
notice = error instanceof Error ? error.message : String(error);
|
|
580
|
+
}
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
if (input.kind === "turns") {
|
|
584
|
+
if (mode === "ask") {
|
|
585
|
+
notice = currentMessages.tui.askTurnsNotice;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
validateTurns(input.turns, "--turns", currentMessages);
|
|
590
|
+
config.defaults = { ...(config.defaults ?? {}), turns: input.turns };
|
|
591
|
+
await writeExampleConfig(configPath, config);
|
|
592
|
+
changedRunDefaults = true;
|
|
593
|
+
notice = currentMessages.tui.turnsUpdated(input.turns);
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
notice = error instanceof Error ? error.message : String(error);
|
|
597
|
+
}
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
if (input.kind === "summary") {
|
|
601
|
+
try {
|
|
602
|
+
const nextDefaults = { ...(config.defaults ?? {}) };
|
|
603
|
+
if (input.agent !== undefined) {
|
|
604
|
+
assertKnownAgent(config, input.agent, mode === "ask" ? "defaults.askSummaryAgent" : "defaults.summaryAgent", currentMessages);
|
|
605
|
+
}
|
|
606
|
+
if (mode === "ask") {
|
|
607
|
+
if (input.agent === undefined) {
|
|
608
|
+
delete nextDefaults.askSummaryAgent;
|
|
609
|
+
notice = currentMessages.tui.askSummaryFallback;
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
nextDefaults.askSummaryAgent = input.agent;
|
|
613
|
+
notice = currentMessages.tui.askSummaryAgent(input.agent);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else if (input.agent === undefined) {
|
|
617
|
+
delete nextDefaults.summaryAgent;
|
|
618
|
+
notice = currentMessages.tui.debateSummaryFallback;
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
nextDefaults.summaryAgent = input.agent;
|
|
622
|
+
notice = currentMessages.tui.debateSummaryAgent(input.agent);
|
|
623
|
+
}
|
|
624
|
+
config.defaults = nextDefaults;
|
|
625
|
+
await writeExampleConfig(configPath, config);
|
|
626
|
+
changedRunDefaults = true;
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
notice = error instanceof Error ? error.message : String(error);
|
|
630
|
+
}
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (input.kind === "ollama-info") {
|
|
634
|
+
try {
|
|
635
|
+
notice = await formatTuiOllamaInfo(config, currentMessages);
|
|
636
|
+
}
|
|
637
|
+
catch (error) {
|
|
638
|
+
notice = error instanceof Error ? error.message : String(error);
|
|
639
|
+
}
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
if (input.kind === "ollama-model") {
|
|
643
|
+
try {
|
|
644
|
+
notice = await setTuiOllamaModel(configPath, config, input.model, currentMessages);
|
|
645
|
+
changedRunDefaults = true;
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
notice = error instanceof Error ? error.message : String(error);
|
|
649
|
+
}
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
if (input.kind === "ollama-sync") {
|
|
653
|
+
try {
|
|
654
|
+
notice = await syncTuiOllamaModel(configPath, config, currentMessages);
|
|
655
|
+
changedRunDefaults = true;
|
|
656
|
+
}
|
|
657
|
+
catch (error) {
|
|
658
|
+
notice = error instanceof Error ? error.message : String(error);
|
|
659
|
+
}
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function formatTuiOllamaInfo(config, messages) {
|
|
665
|
+
const discovery = await discoverLocalTools();
|
|
666
|
+
const agent = config.agents["ollama-local"];
|
|
667
|
+
if (agent?.type !== "ollama") {
|
|
668
|
+
throw new Error(messages.config.ollamaModelNoAgent);
|
|
669
|
+
}
|
|
670
|
+
if (!discovery.ollama.available) {
|
|
671
|
+
return messages.tui.ollamaUnavailable(discovery.ollama.baseUrl);
|
|
672
|
+
}
|
|
673
|
+
const installed = discovery.ollama.models.length > 0
|
|
674
|
+
? discovery.ollama.models.join(", ")
|
|
675
|
+
: messages.config.ollamaModelNoInstalledModels;
|
|
676
|
+
const api = `${discovery.ollama.baseUrl}`;
|
|
677
|
+
return messages.tui.ollamaInfo(agent.model, installed, api);
|
|
678
|
+
}
|
|
679
|
+
async function setTuiOllamaModel(configPath, config, model, messages) {
|
|
680
|
+
const trimmed = model.trim();
|
|
681
|
+
if (!trimmed) {
|
|
682
|
+
throw new Error(messages.tui.ollamaModelUsage);
|
|
683
|
+
}
|
|
684
|
+
const discovery = await discoverLocalTools();
|
|
685
|
+
const agent = config.agents["ollama-local"];
|
|
686
|
+
if (agent?.type !== "ollama") {
|
|
687
|
+
throw new Error(messages.config.ollamaModelNoAgent);
|
|
688
|
+
}
|
|
689
|
+
if (!discovery.ollama.models.includes(trimmed)) {
|
|
690
|
+
throw new Error(messages.config.ollamaModelUnavailable(trimmed));
|
|
691
|
+
}
|
|
692
|
+
const result = setOllamaModel(config, trimmed);
|
|
693
|
+
await writeExampleConfig(configPath, config);
|
|
694
|
+
return result
|
|
695
|
+
? messages.config.ollamaModelUpdated(configPath, result.previousModel, result.nextModel)
|
|
696
|
+
: messages.config.ollamaModelNoChange(configPath, agent.model);
|
|
697
|
+
}
|
|
698
|
+
async function syncTuiOllamaModel(configPath, config, messages) {
|
|
699
|
+
const discovery = await discoverLocalTools();
|
|
700
|
+
const agent = config.agents["ollama-local"];
|
|
701
|
+
if (agent?.type !== "ollama") {
|
|
702
|
+
throw new Error(messages.config.ollamaModelNoAgent);
|
|
703
|
+
}
|
|
704
|
+
if (discovery.ollama.models.length === 0) {
|
|
705
|
+
throw new Error(messages.config.ollamaModelNoInstalledModels);
|
|
706
|
+
}
|
|
707
|
+
const result = syncOllamaModel(config, discovery);
|
|
708
|
+
if (!result) {
|
|
709
|
+
return messages.config.ollamaModelNoChange(configPath, agent.model);
|
|
710
|
+
}
|
|
711
|
+
await writeExampleConfig(configPath, config);
|
|
712
|
+
return messages.config.ollamaModelUpdated(configPath, result.previousModel, result.nextModel);
|
|
713
|
+
}
|
|
714
|
+
async function syncInteractiveDetectedAgents(configPath, config) {
|
|
715
|
+
const discovery = await discoverLocalTools();
|
|
716
|
+
const result = syncDetectedAgentsDetailed(config, discovery);
|
|
717
|
+
if (result.changed) {
|
|
718
|
+
await writeExampleConfig(configPath, config);
|
|
719
|
+
}
|
|
720
|
+
return {
|
|
721
|
+
addedAgents: result.addedAgents
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function normalizeTuiDebateAgents(config, agents, messages) {
|
|
725
|
+
const unique = agents.map((agent) => agent.trim()).filter((agent, index, list) => agent && list.indexOf(agent) === index);
|
|
726
|
+
if (unique.length !== 2) {
|
|
727
|
+
throw new Error(messages.tui.debateAgentsUsage);
|
|
728
|
+
}
|
|
729
|
+
assertKnownAgent(config, unique[0], "defaults.agentA", messages);
|
|
730
|
+
assertKnownAgent(config, unique[1], "defaults.agentB", messages);
|
|
731
|
+
return [unique[0], unique[1]];
|
|
732
|
+
}
|
|
733
|
+
function normalizeTuiAskAgents(config, agents, messages) {
|
|
734
|
+
const unique = agents.map((agent) => agent.trim()).filter((agent, index, list) => agent && list.indexOf(agent) === index);
|
|
735
|
+
if (unique.length === 0) {
|
|
736
|
+
throw new Error(messages.tui.askAgentsUsage);
|
|
737
|
+
}
|
|
738
|
+
if (unique.length > MAX_ASK_AGENTS) {
|
|
739
|
+
throw new Error(messages.common.tooManyAskAgents(MAX_ASK_AGENTS));
|
|
740
|
+
}
|
|
741
|
+
unique.forEach((agent) => assertKnownAgent(config, agent, "defaults.askAgents", messages));
|
|
742
|
+
return unique;
|
|
743
|
+
}
|
|
744
|
+
async function runTuiAgentsWizard(configPath, config, messages, mode, inlineAgents = []) {
|
|
745
|
+
try {
|
|
746
|
+
const agentsInput = inlineAgents.length > 0
|
|
747
|
+
? { kind: "agents", agents: inlineAgents }
|
|
748
|
+
: await promptTuiAgentsWizard(config, mode, messages);
|
|
749
|
+
if (agentsInput.kind === "quit") {
|
|
750
|
+
return { quit: true, changedRunDefaults: false };
|
|
751
|
+
}
|
|
752
|
+
if (agentsInput.kind === "back" || agentsInput.agents.length === 0) {
|
|
753
|
+
return { quit: false, changedRunDefaults: false };
|
|
754
|
+
}
|
|
755
|
+
const notice = applyTuiAgents(config, mode, agentsInput.agents, messages);
|
|
756
|
+
await writeExampleConfig(configPath, config);
|
|
757
|
+
return { notice, quit: false, changedRunDefaults: true };
|
|
758
|
+
}
|
|
759
|
+
catch (error) {
|
|
760
|
+
return { notice: messages.tui.agentsError(error instanceof Error ? error.message : String(error)), quit: false, changedRunDefaults: false };
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
async function runTuiRolesWizard(configPath, config, messages, mode, inlineRoles = []) {
|
|
764
|
+
try {
|
|
765
|
+
const rolesInput = inlineRoles.length > 0
|
|
766
|
+
? { kind: "roles", roles: inlineRoles }
|
|
767
|
+
: await promptTuiRolesWizard(config, mode, messages);
|
|
768
|
+
if (rolesInput.kind === "quit") {
|
|
769
|
+
return { quit: true };
|
|
770
|
+
}
|
|
771
|
+
if (rolesInput.kind === "back" || rolesInput.roles.length === 0) {
|
|
772
|
+
return { quit: false };
|
|
773
|
+
}
|
|
774
|
+
const notice = applyTuiRoles(config, mode, rolesInput.roles, messages);
|
|
775
|
+
await writeExampleConfig(configPath, config);
|
|
776
|
+
return { notice, quit: false };
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
return { notice: messages.tui.rolesError(error instanceof Error ? error.message : String(error)), quit: false };
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
function applyTuiAgents(config, mode, agentNames, messages) {
|
|
783
|
+
if (mode === "ask") {
|
|
784
|
+
const agents = normalizeTuiAskAgents(config, agentNames, messages);
|
|
785
|
+
config.defaults = { ...(config.defaults ?? {}), askAgents: agents };
|
|
786
|
+
return messages.tui.askAgentsUpdated(agents.join(", "));
|
|
787
|
+
}
|
|
788
|
+
const [agentA, agentB] = normalizeTuiDebateAgents(config, agentNames, messages);
|
|
789
|
+
config.defaults = { ...(config.defaults ?? {}), agentA, agentB };
|
|
790
|
+
return messages.tui.debateAgentsUpdated(`${agentA} <-> ${agentB}`);
|
|
791
|
+
}
|
|
792
|
+
function applyTuiRoles(config, mode, roleNames, messages) {
|
|
793
|
+
const agents = activeAgentsForMode(config, mode);
|
|
794
|
+
if (agents.length === 0) {
|
|
795
|
+
throw new Error(mode === "ask" ? messages.tui.noAskAgentsConfigured : messages.tui.noDebateAgentsConfigured);
|
|
796
|
+
}
|
|
797
|
+
const roles = normalizeTuiRoles(roleNames, agents, mode, messages);
|
|
798
|
+
agents.forEach((agent, index) => {
|
|
799
|
+
config.agents[agent].role = roles[index];
|
|
800
|
+
});
|
|
801
|
+
return mode === "ask"
|
|
802
|
+
? messages.tui.askRolesUpdated(roles.join(", "))
|
|
803
|
+
: messages.tui.debateRolesUpdated(roles.join(" <-> "));
|
|
804
|
+
}
|
|
805
|
+
function activeAgentsForMode(config, mode) {
|
|
806
|
+
const defaults = config.defaults ?? {};
|
|
807
|
+
if (mode === "ask") {
|
|
808
|
+
if (defaults.askAgents && defaults.askAgents.length > 0) {
|
|
809
|
+
return defaults.askAgents.filter((agent) => Boolean(config.agents[agent]));
|
|
810
|
+
}
|
|
811
|
+
return [defaults.agentA, defaults.agentB].filter((agent) => Boolean(agent && config.agents[agent]));
|
|
812
|
+
}
|
|
813
|
+
return [defaults.agentA, defaults.agentB].filter((agent) => Boolean(agent && config.agents[agent]));
|
|
814
|
+
}
|
|
815
|
+
function normalizeTuiRoles(roleNames, agents, mode, messages) {
|
|
816
|
+
const roles = roleNames.map((role) => role.trim().toLowerCase()).filter(Boolean);
|
|
817
|
+
const expectedCount = agents.length;
|
|
818
|
+
if (roles.length < expectedCount) {
|
|
819
|
+
const agentLabel = mode === "ask"
|
|
820
|
+
? agents.join(", ")
|
|
821
|
+
: agents.join(" <-> ");
|
|
822
|
+
throw new Error(messages.tui.rolesCountError(roles.length, expectedCount, agentLabel));
|
|
823
|
+
}
|
|
824
|
+
return roles.slice(0, expectedCount).map((role) => {
|
|
825
|
+
if (isAgentRole(role)) {
|
|
826
|
+
return role;
|
|
827
|
+
}
|
|
828
|
+
throw new Error(messages.tui.unknownRole(role, VALID_AGENT_ROLES.join(", ")));
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
function isAgentRole(value) {
|
|
832
|
+
return VALID_AGENT_ROLES.includes(value);
|
|
833
|
+
}
|
|
834
|
+
const VALID_AGENT_ROLES = ["implementer", "reviewer", "architect", "scout", "critic", "summarizer"];
|
|
304
835
|
async function runOllamaModelsCommand(config, json) {
|
|
305
836
|
const discovery = await discoverLocalTools();
|
|
306
837
|
const agent = config.agents["ollama-local"];
|
|
@@ -371,7 +902,19 @@ function isNoneValue(value) {
|
|
|
371
902
|
* @returns Chaîne résumant la paire d'agents, le nombre de réponses et l'agent de synthèse.
|
|
372
903
|
*/
|
|
373
904
|
function formatDefaultsForMessage(defaults, messages) {
|
|
374
|
-
return messages.config.defaultsSummary(defaults.agentA, defaults.agentB, turnsOrDefault(defaults.turns), defaults.summaryAgent);
|
|
905
|
+
return messages.config.defaultsSummary(defaults.agentA, defaults.agentB, turnsOrDefault(defaults.turns), defaults.summaryAgent, defaults.askSummaryAgent, defaults.mode, defaults.askAgents, defaults.interface);
|
|
906
|
+
}
|
|
907
|
+
function normalizeAskAgentsForConfig(config, agents, messages) {
|
|
908
|
+
const unique = agents
|
|
909
|
+
.map((agent) => agent.trim())
|
|
910
|
+
.filter((agent, index, list) => agent && list.indexOf(agent) === index);
|
|
911
|
+
if (unique.length > MAX_ASK_AGENTS) {
|
|
912
|
+
throw new Error(messages.common.tooManyAskAgents(MAX_ASK_AGENTS));
|
|
913
|
+
}
|
|
914
|
+
for (const agent of unique) {
|
|
915
|
+
assertKnownAgent(config, agent, "defaults.askAgents", messages);
|
|
916
|
+
}
|
|
917
|
+
return unique;
|
|
375
918
|
}
|
|
376
919
|
/**
|
|
377
920
|
* Lève une erreur si `agentName` n'est pas déclaré dans la config.
|
|
@@ -400,38 +943,88 @@ function resolveAgentName(label, explicitValue, presetValue, defaultValue, messa
|
|
|
400
943
|
}
|
|
401
944
|
return resolved;
|
|
402
945
|
}
|
|
946
|
+
function resolveSummaryAgentOption(explicitValue, defaults, mode) {
|
|
947
|
+
const explicit = optionalString(explicitValue);
|
|
948
|
+
if (explicit) {
|
|
949
|
+
return explicit;
|
|
950
|
+
}
|
|
951
|
+
if (mode === "ask") {
|
|
952
|
+
return defaults?.askSummaryAgent ?? defaults?.summaryAgent;
|
|
953
|
+
}
|
|
954
|
+
return defaults?.summaryAgent;
|
|
955
|
+
}
|
|
956
|
+
function parseModeFlag(value, messages) {
|
|
957
|
+
if (!value) {
|
|
958
|
+
return "debate";
|
|
959
|
+
}
|
|
960
|
+
if (value === "debate" || value === "ask") {
|
|
961
|
+
return value;
|
|
962
|
+
}
|
|
963
|
+
throw new Error(messages.common.unknownMode(value, "debate, ask"));
|
|
964
|
+
}
|
|
965
|
+
function parseInterfaceFlag(value, messages) {
|
|
966
|
+
if (!value) {
|
|
967
|
+
return "tui";
|
|
968
|
+
}
|
|
969
|
+
if (value === "tui" || value === "terminal") {
|
|
970
|
+
return value;
|
|
971
|
+
}
|
|
972
|
+
throw new Error(messages.common.unknownMode(value, "tui, terminal"));
|
|
973
|
+
}
|
|
974
|
+
function resolveAskAgents(explicitAgents, defaultAgents, fallbackAgents, messages) {
|
|
975
|
+
const selected = explicitAgents.length > 0
|
|
976
|
+
? explicitAgents
|
|
977
|
+
: defaultAgents && defaultAgents.length > 0 ? defaultAgents : fallbackAgents;
|
|
978
|
+
const unique = selected.filter((agent, index) => agent.trim() && selected.indexOf(agent) === index);
|
|
979
|
+
if (unique.length > MAX_ASK_AGENTS) {
|
|
980
|
+
throw new Error(messages.common.tooManyAskAgents(MAX_ASK_AGENTS));
|
|
981
|
+
}
|
|
982
|
+
return unique;
|
|
983
|
+
}
|
|
403
984
|
/**
|
|
404
985
|
* Affiche un aperçu du prompt du premier tour sans appeler aucun agent (flag `--show-prompt`).
|
|
405
986
|
* @param config - Config chargée.
|
|
406
987
|
* @param options - Options du débat résolues.
|
|
407
988
|
*/
|
|
408
989
|
function printPromptPreview(config, options, language, messages) {
|
|
409
|
-
const
|
|
990
|
+
const previewAgent = options.mode === "ask" ? options.askAgents?.[0] ?? options.agentA : options.agentA;
|
|
991
|
+
const peerName = options.mode === "ask" ? "independent-agents" : options.agentB;
|
|
992
|
+
const agentConfig = config.agents[previewAgent];
|
|
410
993
|
if (!agentConfig) {
|
|
411
|
-
throw new Error(messages.common.unknownAgent(
|
|
994
|
+
throw new Error(messages.common.unknownAgent(previewAgent));
|
|
412
995
|
}
|
|
413
996
|
const prompt = formatAgentPrompt({
|
|
414
997
|
topic: options.topic,
|
|
415
998
|
turn: 1,
|
|
416
|
-
totalTurns: options.turns,
|
|
417
|
-
selfName:
|
|
418
|
-
peerName
|
|
999
|
+
totalTurns: options.mode === "ask" ? options.askAgents?.length ?? 1 : options.turns,
|
|
1000
|
+
selfName: previewAgent,
|
|
1001
|
+
peerName,
|
|
419
1002
|
selfRole: agentConfig.role,
|
|
1003
|
+
mode: options.mode === "ask" ? "ask" : "debate",
|
|
420
1004
|
language: options.language,
|
|
421
1005
|
session: options.session,
|
|
422
1006
|
files: options.files,
|
|
423
1007
|
transcript: []
|
|
424
1008
|
});
|
|
425
1009
|
console.log(messages.preview.title);
|
|
426
|
-
console.log(messages.preview.agent(
|
|
427
|
-
console.log(messages.preview.peer(
|
|
1010
|
+
console.log(messages.preview.agent(previewAgent, agentConfig.role));
|
|
1011
|
+
console.log(messages.preview.peer(peerName));
|
|
428
1012
|
console.log(messages.preview.pullModels(options.pullModels));
|
|
429
|
-
console.log(messages.preview.summary(options.summaryEnabled ? options
|
|
1013
|
+
console.log(messages.preview.summary(options.summaryEnabled ? previewSummaryAgent(options) : messages.preview.disabled));
|
|
430
1014
|
console.log(messages.preview.interfaceLanguage(language));
|
|
431
1015
|
console.log("");
|
|
432
1016
|
console.log(prompt);
|
|
433
1017
|
console.log("");
|
|
434
|
-
console.log(messages.preview.note);
|
|
1018
|
+
console.log(options.mode === "ask" ? messages.preview.askNote : messages.preview.note);
|
|
1019
|
+
}
|
|
1020
|
+
function previewSummaryAgent(options) {
|
|
1021
|
+
if (options.summaryAgent) {
|
|
1022
|
+
return options.summaryAgent;
|
|
1023
|
+
}
|
|
1024
|
+
if (options.mode === "ask" && options.askAgents && options.askAgents.length > 0) {
|
|
1025
|
+
return options.askAgents[options.askAgents.length - 1] ?? options.agentB;
|
|
1026
|
+
}
|
|
1027
|
+
return options.agentB;
|
|
435
1028
|
}
|
|
436
1029
|
/**
|
|
437
1030
|
* Extrait une chaîne non vide depuis une valeur de flag, ou renvoie `undefined`.
|
|
@@ -455,7 +1048,7 @@ function findRawLanguageFlag(args) {
|
|
|
455
1048
|
return undefined;
|
|
456
1049
|
}
|
|
457
1050
|
/** Liste des kinds de renderer acceptés par `--renderer`. */
|
|
458
|
-
const SUPPORTED_RENDERERS = ["auto", "pretty", "plain", "ndjson"];
|
|
1051
|
+
const SUPPORTED_RENDERERS = ["auto", "pretty", "plain", "tui", "ndjson"];
|
|
459
1052
|
/**
|
|
460
1053
|
* Instancie le renderer en fonction des flags CLI.
|
|
461
1054
|
*
|
|
@@ -467,7 +1060,7 @@ const SUPPORTED_RENDERERS = ["auto", "pretty", "plain", "ndjson"];
|
|
|
467
1060
|
*
|
|
468
1061
|
* Lève si la valeur de `--renderer` n'est pas dans `SUPPORTED_RENDERERS`.
|
|
469
1062
|
*/
|
|
470
|
-
function createRendererFromFlags(flags, plainOutputFallback, messages) {
|
|
1063
|
+
function createRendererFromFlags(flags, plainOutputFallback, defaultInterface, messages) {
|
|
471
1064
|
const explicit = optionalString(flags.renderer);
|
|
472
1065
|
if (explicit) {
|
|
473
1066
|
if (!SUPPORTED_RENDERERS.includes(explicit)) {
|
|
@@ -481,14 +1074,41 @@ function createRendererFromFlags(flags, plainOutputFallback, messages) {
|
|
|
481
1074
|
return createConsoleRenderer(true, messages);
|
|
482
1075
|
case "pretty":
|
|
483
1076
|
return createConsoleRenderer(false, messages);
|
|
1077
|
+
case "tui":
|
|
1078
|
+
return createTuiRenderer(messages);
|
|
484
1079
|
case "auto":
|
|
485
|
-
return
|
|
1080
|
+
return createAutoRenderer(flags, plainOutputFallback, defaultInterface, messages);
|
|
486
1081
|
}
|
|
487
1082
|
}
|
|
488
1083
|
if (flags.json) {
|
|
489
1084
|
return createNdjsonRenderer();
|
|
490
1085
|
}
|
|
491
|
-
|
|
1086
|
+
if (flags.tui) {
|
|
1087
|
+
return createTuiRenderer(messages);
|
|
1088
|
+
}
|
|
1089
|
+
if (flags.terminal || flags.plain || plainOutputFallback || defaultInterface === "terminal") {
|
|
1090
|
+
return createConsoleRenderer(true, messages);
|
|
1091
|
+
}
|
|
1092
|
+
return createAutoRenderer(flags, plainOutputFallback, defaultInterface, messages);
|
|
1093
|
+
}
|
|
1094
|
+
function createAutoRenderer(flags, plainOutputFallback, defaultInterface, messages) {
|
|
1095
|
+
if (flags.tui) {
|
|
1096
|
+
return createTuiRenderer(messages);
|
|
1097
|
+
}
|
|
1098
|
+
if (flags.terminal || flags.plain || plainOutputFallback || defaultInterface === "terminal") {
|
|
1099
|
+
return createConsoleRenderer(true, messages);
|
|
1100
|
+
}
|
|
1101
|
+
return process.stdout.isTTY ? createTuiRenderer(messages) : createConsoleRenderer(true, messages);
|
|
1102
|
+
}
|
|
1103
|
+
function shouldOpenTuiHome(parsed) {
|
|
1104
|
+
return parsed.command === "run"
|
|
1105
|
+
&& !parsed.commandExplicit
|
|
1106
|
+
&& parsed.positionals.length === 0
|
|
1107
|
+
&& optionalString(parsed.flags.topic) === undefined
|
|
1108
|
+
&& optionalString(parsed.flags.renderer) === undefined
|
|
1109
|
+
&& parsed.flags.json !== true
|
|
1110
|
+
&& parsed.flags.plain !== true
|
|
1111
|
+
&& parsed.flags.terminal !== true;
|
|
492
1112
|
}
|
|
493
1113
|
/**
|
|
494
1114
|
* Exécute la commande `palabre presets`.
|
|
@@ -527,6 +1147,32 @@ async function runPresetsCommand(flags) {
|
|
|
527
1147
|
console.log("");
|
|
528
1148
|
console.log(messages.presets.total(presets.length));
|
|
529
1149
|
}
|
|
1150
|
+
async function runHistoryCommand(flags) {
|
|
1151
|
+
const configPath = optionalString(flags.config) ?? await resolveDefaultConfigPath();
|
|
1152
|
+
const config = await configExists(configPath)
|
|
1153
|
+
? await loadConfig(configPath)
|
|
1154
|
+
: undefined;
|
|
1155
|
+
const language = resolveLanguage({
|
|
1156
|
+
explicitLanguage: optionalString(flags.language),
|
|
1157
|
+
configLanguage: config?.language
|
|
1158
|
+
});
|
|
1159
|
+
const messages = createTranslator(language);
|
|
1160
|
+
const entries = await listHistoryEntries(resolveOutputDir(config?.outputDir));
|
|
1161
|
+
if (flags.json) {
|
|
1162
|
+
process.stdout.write(JSON.stringify({ v: 1, history: entries }) + "\n");
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
console.log(messages.tui.historyTitle);
|
|
1166
|
+
console.log("");
|
|
1167
|
+
if (entries.length === 0) {
|
|
1168
|
+
console.log(messages.tui.historyEmpty);
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
for (const entry of entries) {
|
|
1172
|
+
console.log(`- ${entry.date || entry.fileName} | ${entry.mode} | ${entry.topic}`);
|
|
1173
|
+
console.log(` ${entry.path}`);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
530
1176
|
async function runContextCommand(flags, positionals) {
|
|
531
1177
|
const language = resolveLanguage({ explicitLanguage: optionalString(flags.language) });
|
|
532
1178
|
const messages = createTranslator(language);
|
|
@@ -590,7 +1236,7 @@ function printAgents(configPath, config, discovery, messages) {
|
|
|
590
1236
|
}
|
|
591
1237
|
}
|
|
592
1238
|
console.log("");
|
|
593
|
-
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));
|
|
1239
|
+
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, config.defaults?.askSummaryAgent));
|
|
594
1240
|
}
|
|
595
1241
|
/**
|
|
596
1242
|
* Renvoie un libellé indiquant si l'agent est agent A, agent B ou agent de synthèse par défaut.
|
|
@@ -605,6 +1251,8 @@ function formatAgentDefaults(name, config, messages) {
|
|
|
605
1251
|
labels.push(messages.agents.defaultAgentB);
|
|
606
1252
|
if (config.defaults?.summaryAgent === name)
|
|
607
1253
|
labels.push(messages.agents.defaultSummary);
|
|
1254
|
+
if (config.defaults?.askSummaryAgent === name)
|
|
1255
|
+
labels.push(messages.agents.defaultAskSummary);
|
|
608
1256
|
return labels.join(", ");
|
|
609
1257
|
}
|
|
610
1258
|
/**
|
|
@@ -657,9 +1305,9 @@ function printInitDiscovery(discovery, config, messages) {
|
|
|
657
1305
|
console.log(messages.init.localDetectionTitle);
|
|
658
1306
|
console.log(`- Codex CLI: ${formatCommandDetection(discovery.codex, messages)}`);
|
|
659
1307
|
console.log(`- Claude CLI: ${formatCommandDetection(discovery.claude, messages)}`);
|
|
660
|
-
console.log(`- Gemini CLI: ${formatCommandDetection(discovery.gemini, messages)}`);
|
|
661
1308
|
console.log(`- Antigravity CLI: ${formatCommandDetection(discovery.antigravity, messages)}`);
|
|
662
1309
|
console.log(`- OpenCode CLI: ${formatCommandDetection(discovery.opencode, messages)}`);
|
|
1310
|
+
console.log(`- Mistral Vibe CLI: ${formatCommandDetection(discovery.vibe, messages)}`);
|
|
663
1311
|
console.log(`- Ollama API: ${formatOllamaDetection(discovery.ollama, messages)}`);
|
|
664
1312
|
console.log("");
|
|
665
1313
|
console.log(config.defaults?.agentA && config.defaults.agentB
|