palabre 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/cli-pty.js +30 -10
- package/dist/adapters/cli-shared.js +73 -0
- package/dist/adapters/cli.js +40 -77
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/ollama.js +32 -32
- package/dist/adapters/terminal.js +1 -0
- package/dist/args.js +1 -0
- package/dist/commands/agents.js +7 -1
- package/dist/commands/context.js +7 -1
- package/dist/commands/history.js +6 -1
- package/dist/commands/init.js +8 -3
- package/dist/commands/presets.js +6 -1
- package/dist/commands/shared.js +5 -1
- package/dist/commands/update.js +6 -1
- package/dist/config.js +17 -1
- package/dist/configWizard.js +5 -4
- package/dist/context.js +1 -0
- package/dist/contextScan.js +4 -3
- package/dist/discovery.js +1 -0
- package/dist/doctor.js +1 -0
- package/dist/errors.js +4 -0
- package/dist/exec.js +1 -0
- package/dist/history.js +10 -0
- package/dist/i18n.js +2 -0
- package/dist/index.js +151 -112
- package/dist/limits.js +4 -0
- package/dist/messages/adapter-errors.js +26 -2
- package/dist/messages/config.js +6 -0
- package/dist/messages/index.js +1 -0
- package/dist/messages/renderers.js +10 -2
- package/dist/messages/tui.js +8 -2
- package/dist/new.js +1 -0
- package/dist/ollamaUrl.js +20 -0
- package/dist/orchestrator.js +103 -150
- package/dist/output.js +1 -0
- package/dist/presets.js +1 -0
- package/dist/prompt.js +1 -0
- package/dist/renderers/console.js +1 -1
- package/dist/renderers/tui-prompts.js +339 -0
- package/dist/renderers/tui-renderer.js +224 -0
- package/dist/renderers/tui-screens.js +352 -0
- package/dist/renderers/tui-theme.js +356 -0
- package/dist/renderers/tui.js +7 -1086
- package/dist/runOptions.js +33 -2
- package/dist/session.js +1 -0
- package/dist/tuiController.js +61 -16
- package/dist/tuiState.js +4 -0
- package/dist/types.js +1 -0
- package/dist/update.js +1 -0
- package/dist/version.js +1 -0
- package/package.json +1 -1
package/dist/commands/shared.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Extrait une chaîne non vide depuis une valeur de flag.
|
|
3
|
+
* @param value - Valeur brute produite par le parseur CLI.
|
|
4
|
+
* @returns La chaîne non vide, sinon `undefined`.
|
|
5
|
+
*/
|
|
2
6
|
export function optionalString(value) {
|
|
3
7
|
return typeof value === "string" && value.trim() ? value : undefined;
|
|
4
8
|
}
|
package/dist/commands/update.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
/** @file Commande de diagnostic et application des mises a jour Palabre. */
|
|
1
2
|
import { configExists, loadConfig, resolveDefaultConfigPath } from "../config.js";
|
|
2
3
|
import { createTranslator, resolveLanguage } from "../i18n.js";
|
|
3
4
|
import { applySourceUpdate, formatUpdateInstructions, getUpdateInfo } from "../update.js";
|
|
4
5
|
import { getPackageVersion } from "../version.js";
|
|
5
6
|
import { optionalString } from "./shared.js";
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Affiche les instructions de mise à jour ou applique le workflow d'un checkout source.
|
|
9
|
+
* @param flags - Flags de config, langue et application explicite.
|
|
10
|
+
* @returns Une promesse résolue après affichage ou mise à jour.
|
|
11
|
+
*/
|
|
7
12
|
export async function runUpdateCommand(flags) {
|
|
8
13
|
const info = await getUpdateInfo(await getPackageVersion());
|
|
9
14
|
const configPath = optionalString(flags.config) ?? await resolveDefaultConfigPath();
|
package/dist/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @file Chargement, génération et validation de la config Palabre, ainsi que la synchronisation des agents/modèles Ollama détectés. */
|
|
1
2
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
@@ -234,6 +235,10 @@ function migrateVibePlanAgent(config) {
|
|
|
234
235
|
function isLegacyVibePlanArgs(args) {
|
|
235
236
|
return JSON.stringify(args) === JSON.stringify(["--output", "text", "--agent", "plan", "--trust", "--prompt"]);
|
|
236
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Variante de `syncDetectedAgents` qui indique aussi si la config a changé,
|
|
240
|
+
* même quand aucun agent n'a été ajouté (ex. migration d'arguments legacy).
|
|
241
|
+
*/
|
|
237
242
|
export function syncDetectedAgentsDetailed(config, discovery) {
|
|
238
243
|
const before = JSON.stringify(config.agents);
|
|
239
244
|
const addedAgents = syncDetectedAgents(config, discovery);
|
|
@@ -243,6 +248,12 @@ export function syncDetectedAgentsDetailed(config, discovery) {
|
|
|
243
248
|
changed
|
|
244
249
|
};
|
|
245
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Bascule le modèle de l'agent `ollama-local` vers un modèle installé si celui configuré
|
|
253
|
+
* ne l'est pas. Mute `config` directement.
|
|
254
|
+
* @returns `undefined` si l'agent n'est pas de type `ollama`, si aucun modèle n'est installé,
|
|
255
|
+
* ou si le modèle configuré est déjà installé.
|
|
256
|
+
*/
|
|
246
257
|
export function syncOllamaModel(config, discovery) {
|
|
247
258
|
const agent = config.agents["ollama-local"];
|
|
248
259
|
if (agent?.type !== "ollama" || discovery.ollama.models.length === 0) {
|
|
@@ -258,6 +269,11 @@ export function syncOllamaModel(config, discovery) {
|
|
|
258
269
|
nextModel: agent.model
|
|
259
270
|
};
|
|
260
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Force le modèle de l'agent `ollama-local` à `model`, sans vérifier qu'il est installé.
|
|
274
|
+
* Mute `config` directement.
|
|
275
|
+
* @returns `undefined` si l'agent n'est pas de type `ollama`.
|
|
276
|
+
*/
|
|
261
277
|
export function setOllamaModel(config, model) {
|
|
262
278
|
const agent = config.agents["ollama-local"];
|
|
263
279
|
if (agent?.type !== "ollama") {
|
|
@@ -279,7 +295,7 @@ export function setOllamaBaseUrl(config, baseUrl) {
|
|
|
279
295
|
return agents.length;
|
|
280
296
|
}
|
|
281
297
|
/** Écrit `config` sérialisé en JSON dans `configPath`. Crée le répertoire parent si nécessaire. */
|
|
282
|
-
export async function
|
|
298
|
+
export async function writeConfig(configPath = DEFAULT_CONFIG_PATH, config = exampleConfig) {
|
|
283
299
|
const resolved = path.resolve(configPath);
|
|
284
300
|
await mkdir(path.dirname(resolved), { recursive: true });
|
|
285
301
|
await writeFile(resolved, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
package/dist/configWizard.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
/** @file Wizard interactif `palabre config` (sans sous-commande) pour éditer les defaults au clavier. */
|
|
1
2
|
import { createInterface } from "node:readline/promises";
|
|
2
3
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
-
import { syncDetectedAgents,
|
|
4
|
+
import { syncDetectedAgents, writeConfig } from "./config.js";
|
|
4
5
|
import { discoverLocalTools } from "./discovery.js";
|
|
5
6
|
import { DEFAULT_TURNS, MAX_TURNS, turnsOrDefault, validateTurns } from "./limits.js";
|
|
6
7
|
/**
|
|
@@ -36,7 +37,7 @@ export async function runConfigWizard(configPath, config, messages) {
|
|
|
36
37
|
}
|
|
37
38
|
if (action === "2") {
|
|
38
39
|
delete config.defaults;
|
|
39
|
-
await
|
|
40
|
+
await writeConfig(configPath, config);
|
|
40
41
|
console.log(messages.config.wizardCleared(configPath));
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
@@ -47,7 +48,7 @@ export async function runConfigWizard(configPath, config, messages) {
|
|
|
47
48
|
console.log(messages.config.syncNoMissing(configPath));
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
|
-
await
|
|
51
|
+
await writeConfig(configPath, config);
|
|
51
52
|
console.log(messages.config.syncAdded(configPath, addedAgents.join(", ")));
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
@@ -75,7 +76,7 @@ export async function runConfigWizard(configPath, config, messages) {
|
|
|
75
76
|
else {
|
|
76
77
|
delete config.defaults.summaryAgent;
|
|
77
78
|
}
|
|
78
|
-
await
|
|
79
|
+
await writeConfig(configPath, config);
|
|
79
80
|
console.log(messages.config.wizardDefaultsSet(configPath, formatDefaults(config.defaults, messages)));
|
|
80
81
|
}
|
|
81
82
|
finally {
|
package/dist/context.js
CHANGED
package/dist/contextScan.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
/** @file Contrat JSON v1 de `palabre context scan --json`, consommé par les intégrations. */
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { loadProjectInputs } from "./context.js";
|
|
3
4
|
import { createTranslator } from "./i18n.js";
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* Construit l'aperçu de contexte lisible par une machine, utilisé par les intégrations.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* Le scan réutilise volontairement le même chargeur tolérant que `--context`, afin que
|
|
9
|
+
* les fichiers renvoyés soient exactement ceux que Palabre injecterait dans un débat.
|
|
9
10
|
*/
|
|
10
11
|
export async function buildContextScan(scanPaths, cwd = process.cwd(), messages = createTranslator("fr")) {
|
|
11
12
|
const effectiveScanPaths = scanPaths.length > 0 ? scanPaths : ["."];
|
package/dist/discovery.js
CHANGED
package/dist/doctor.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @file Diagnostic `palabre doctor` : vérifie config, agents détectés et connectivité Ollama. */
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { stat } from "node:fs/promises";
|
|
3
4
|
import { configExists, loadConfig, resolveDefaultConfigPath, resolveOutputDir } from "./config.js";
|
package/dist/errors.js
CHANGED
|
@@ -20,3 +20,7 @@ export function formatAdapterError(error, messages = createTranslator("fr")) {
|
|
|
20
20
|
const hint = messages.adapterErrors.hint(error.kind);
|
|
21
21
|
return hint ? `${error.message}\n${messages.adapterErrors.suggestionPrefix}: ${hint}` : error.message;
|
|
22
22
|
}
|
|
23
|
+
/** Construit l'`AdapterError` standard d'annulation utilisateur, partagée par tous les adapters. */
|
|
24
|
+
export function cancelledError(adapterName) {
|
|
25
|
+
return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
|
|
26
|
+
}
|
package/dist/exec.js
CHANGED
package/dist/history.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
/** @file Liste les exports Markdown récents en reparsant leur table de métadonnées, pour `palabre history` et la TUI. */
|
|
1
2
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
4
|
+
/** Limite de lecture par fichier : la table de métadonnées est toujours en tête de l'export. */
|
|
3
5
|
const maxHeaderBytes = 12_000;
|
|
6
|
+
/**
|
|
7
|
+
* Liste les exports les plus récents d'un dossier de sortie, triés par date de modification.
|
|
8
|
+
* Silencieux si `outputDir` est absent ou inaccessible : retourne `[]` sans lever.
|
|
9
|
+
*/
|
|
4
10
|
export async function listHistoryEntries(outputDir, limit = 10) {
|
|
5
11
|
const resolved = path.resolve(outputDir);
|
|
6
12
|
let entries;
|
|
@@ -19,6 +25,7 @@ export async function listHistoryEntries(outputDir, limit = 10) {
|
|
|
19
25
|
.sort((left, right) => right.mtimeMs - left.mtimeMs)
|
|
20
26
|
.slice(0, limit);
|
|
21
27
|
}
|
|
28
|
+
/** Lit et parse un fichier d'export. Retourne `undefined` sur toute erreur filesystem/parsing plutôt que de lever. */
|
|
22
29
|
async function readHistoryFile(filePath) {
|
|
23
30
|
try {
|
|
24
31
|
const [metadata, raw] = await Promise.all([
|
|
@@ -44,6 +51,7 @@ async function readHistoryFile(filePath) {
|
|
|
44
51
|
return undefined;
|
|
45
52
|
}
|
|
46
53
|
}
|
|
54
|
+
/** Formate le compteur `x/y` adapté au mode, en tolérant les clés FR/EN de la table de métadonnées. */
|
|
47
55
|
function countFromTable(mode, table) {
|
|
48
56
|
if (mode === "ask") {
|
|
49
57
|
const received = table["Reponses recues"] ?? table["Received responses"];
|
|
@@ -54,6 +62,7 @@ function countFromTable(mode, table) {
|
|
|
54
62
|
const requested = table["Tours demandes"] ?? table["Requested turns"];
|
|
55
63
|
return played && requested ? `${played}/${requested}` : played ?? requested ?? "";
|
|
56
64
|
}
|
|
65
|
+
/** Extrait les paires clé/valeur de la table Markdown `| Champ | Valeur |` générée par l'export. */
|
|
57
66
|
function parseMetadataTable(markdown) {
|
|
58
67
|
const fields = {};
|
|
59
68
|
const lines = markdown.split(/\r?\n/);
|
|
@@ -75,6 +84,7 @@ function stripMarkdown(value) {
|
|
|
75
84
|
.replace(/`/g, "")
|
|
76
85
|
.trim();
|
|
77
86
|
}
|
|
87
|
+
/** Reconstruit un sujet lisible depuis le nom de fichier quand la table de métadonnées ne l'indique pas. */
|
|
78
88
|
function topicFromFileName(fileName) {
|
|
79
89
|
return fileName
|
|
80
90
|
.replace(/^palabre-/, "")
|
package/dist/i18n.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createTranslator } from "./messages/index.js";
|
|
2
|
+
/** Langue utilisée quand aucune source explicite (flag, env, config) n'en fournit une valide. */
|
|
2
3
|
export const DEFAULT_LANGUAGE = "fr";
|
|
4
|
+
/** Langues acceptées par `parseLanguage`/`resolveLanguage`. */
|
|
3
5
|
export const SUPPORTED_LANGUAGES = ["fr", "en"];
|
|
4
6
|
/**
|
|
5
7
|
* Valide une langue Palabre.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @file Point d'entrée CLI de Palabre : parsing des arguments, dispatch vers les
|
|
4
|
+
* commandes leaf (`agents`, `presets`, `history`, `context`, `update`, `init`, `config`),
|
|
5
|
+
* résolution de la config/langue, boucle TUI d'accueil et lancement d'un débat ou d'une
|
|
6
|
+
* requête `ask` via l'orchestrateur.
|
|
7
|
+
*/
|
|
8
|
+
import { assertRunnableConfig, configExists, createConfigFromDiscovery, loadConfig, resolveDefaultConfigPath, resolveOutputDir, setOllamaModel, syncDetectedAgentsDetailed, syncOllamaModel, writeConfig } from "./config.js";
|
|
3
9
|
import { loadProjectInputs } from "./context.js";
|
|
4
10
|
import { discoverLocalTools, discoverLocalToolsForConfig } from "./discovery.js";
|
|
5
11
|
import { runDoctor } from "./doctor.js";
|
|
@@ -19,7 +25,7 @@ import { writeDebateMarkdown } from "./output.js";
|
|
|
19
25
|
import { formatUpdateInstructions, getUpdateInfo } from "./update.js";
|
|
20
26
|
import { getStringListFlag, parseArgs } from "./args.js";
|
|
21
27
|
import { clearTuiRunOverrides } from "./tuiState.js";
|
|
22
|
-
import { OllamaUrlError } from "./ollamaUrl.js";
|
|
28
|
+
import { formatOllamaUrlError, OllamaUrlError } from "./ollamaUrl.js";
|
|
23
29
|
import { compareSemver, getLatestPackageVersion, getPackageVersion } from "./version.js";
|
|
24
30
|
import { runAgentsCommand } from "./commands/agents.js";
|
|
25
31
|
import { runContextCommand } from "./commands/context.js";
|
|
@@ -29,7 +35,7 @@ import { runPresetsCommand } from "./commands/presets.js";
|
|
|
29
35
|
import { runUpdateCommand } from "./commands/update.js";
|
|
30
36
|
import { optionalString } from "./commands/shared.js";
|
|
31
37
|
import { runTuiAgentsWizard, runTuiConfigLoop, runTuiRolesWizard, syncInteractiveDetectedAgents } from "./tuiController.js";
|
|
32
|
-
import { resolveRunOptions } from "./runOptions.js";
|
|
38
|
+
import { parseInterfaceFlag, parseModeFlag, resolveRunOptions } from "./runOptions.js";
|
|
33
39
|
/** Point d'entrée principal du CLI Palabre. Dispatche vers la commande appropriée selon les arguments. */
|
|
34
40
|
async function main() {
|
|
35
41
|
const rawArgs = process.argv.slice(2);
|
|
@@ -90,7 +96,7 @@ async function main() {
|
|
|
90
96
|
configLanguage: config.language
|
|
91
97
|
});
|
|
92
98
|
const messages = createTranslator(config.language);
|
|
93
|
-
await
|
|
99
|
+
await writeConfig(configPath, config);
|
|
94
100
|
if (!shouldOpenTuiHome(parsed)) {
|
|
95
101
|
console.log(messages.init.editConfigThenRerun(configPath));
|
|
96
102
|
return;
|
|
@@ -113,78 +119,94 @@ async function main() {
|
|
|
113
119
|
let tuiVersion = "";
|
|
114
120
|
let tuiLatestVersion;
|
|
115
121
|
const handleTuiHomeInput = async (tuiInput) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const nextInput = await promptTuiHomeTopic(tuiMode, messages);
|
|
122
|
-
return handleTuiHomeInput(nextInput);
|
|
123
|
-
}
|
|
124
|
-
if (tuiInput.kind === "history") {
|
|
125
|
-
renderTuiHistory(await listHistoryEntries(resolveOutputDir(config.outputDir)), messages);
|
|
126
|
-
const nextInput = await promptTuiHomeTopic(tuiMode, messages);
|
|
127
|
-
return handleTuiHomeInput(nextInput);
|
|
128
|
-
}
|
|
129
|
-
if (tuiInput.kind === "update") {
|
|
130
|
-
const info = await getUpdateInfo(tuiVersion);
|
|
131
|
-
renderTuiUpdate(formatUpdateInstructions(info, messages), messages);
|
|
132
|
-
const nextInput = await promptTuiHomeTopic(tuiMode, messages);
|
|
133
|
-
return handleTuiHomeInput(nextInput);
|
|
134
|
-
}
|
|
135
|
-
if (tuiInput.kind === "home") {
|
|
136
|
-
return "continue";
|
|
137
|
-
}
|
|
138
|
-
if (tuiInput.kind === "roles") {
|
|
139
|
-
const result = await runTuiRolesWizard(configPath, config, messages, tuiMode, tuiInput.roles);
|
|
140
|
-
if (result.quit)
|
|
141
|
-
return "quit";
|
|
142
|
-
tuiNotice = result.notice;
|
|
143
|
-
return "continue";
|
|
144
|
-
}
|
|
145
|
-
if (tuiInput.kind === "agents") {
|
|
146
|
-
const result = await runTuiAgentsWizard(configPath, config, messages, tuiMode, tuiInput.agents);
|
|
147
|
-
if (result.quit)
|
|
148
|
-
return "quit";
|
|
149
|
-
tuiNotice = result.notice;
|
|
150
|
-
resetTuiRunOverridesOnNextTopic ||= Boolean(result.changedRunDefaults);
|
|
151
|
-
return "continue";
|
|
152
|
-
}
|
|
153
|
-
if (tuiInput.kind === "mode") {
|
|
154
|
-
tuiMode = tuiInput.mode;
|
|
155
|
-
return "continue";
|
|
156
|
-
}
|
|
157
|
-
if (tuiInput.kind === "config") {
|
|
158
|
-
const result = await runTuiConfigLoop(configPath, config, messages, tuiMode);
|
|
159
|
-
if (result.quit)
|
|
122
|
+
let input = tuiInput;
|
|
123
|
+
// Les vues informatives (/help, /history, /update) réaffichent le prompt d'accueil
|
|
124
|
+
// puis reprennent le traitement avec la nouvelle saisie, d'où la boucle.
|
|
125
|
+
for (;;) {
|
|
126
|
+
if (!input) {
|
|
160
127
|
return "quit";
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
128
|
+
}
|
|
129
|
+
if (input.kind === "help") {
|
|
130
|
+
renderTuiHelp(messages);
|
|
131
|
+
input = await promptTuiHomeTopic(tuiMode, messages);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (input.kind === "history") {
|
|
135
|
+
renderTuiHistory(await listHistoryEntries(resolveOutputDir(config.outputDir)), messages);
|
|
136
|
+
input = await promptTuiHomeTopic(tuiMode, messages);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (input.kind === "update") {
|
|
140
|
+
const info = await getUpdateInfo(tuiVersion);
|
|
141
|
+
renderTuiUpdate(formatUpdateInstructions(info, messages), messages);
|
|
142
|
+
input = await promptTuiHomeTopic(tuiMode, messages);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (input.kind === "home") {
|
|
176
146
|
return "continue";
|
|
177
147
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
148
|
+
if (input.kind === "roles") {
|
|
149
|
+
const result = await runTuiRolesWizard(configPath, config, messages, tuiMode, input.roles);
|
|
150
|
+
if (result.quit)
|
|
151
|
+
return "quit";
|
|
152
|
+
tuiNotice = result.notice;
|
|
153
|
+
return "continue";
|
|
154
|
+
}
|
|
155
|
+
if (input.kind === "agents") {
|
|
156
|
+
const result = await runTuiAgentsWizard(configPath, config, messages, tuiMode, input.agents);
|
|
157
|
+
if (result.quit)
|
|
158
|
+
return "quit";
|
|
159
|
+
tuiNotice = result.notice;
|
|
160
|
+
resetTuiRunOverridesOnNextTopic ||= Boolean(result.changedRunDefaults);
|
|
161
|
+
return "continue";
|
|
162
|
+
}
|
|
163
|
+
if (input.kind === "mode") {
|
|
164
|
+
tuiMode = input.mode;
|
|
165
|
+
return "continue";
|
|
166
|
+
}
|
|
167
|
+
if (input.kind === "config") {
|
|
168
|
+
const result = await runTuiConfigLoop(configPath, config, messages, tuiMode);
|
|
169
|
+
if (result.quit)
|
|
170
|
+
return "quit";
|
|
171
|
+
tuiMode = result.mode;
|
|
172
|
+
resetTuiRunOverridesOnNextTopic ||= result.changedRunDefaults;
|
|
173
|
+
language = resolveLanguage({ explicitLanguage: optionalString(parsed.flags.language), configLanguage: config.language });
|
|
174
|
+
messages = createTranslator(language);
|
|
175
|
+
return "continue";
|
|
176
|
+
}
|
|
177
|
+
if (input.kind === "new") {
|
|
178
|
+
parsed.command = "new";
|
|
179
|
+
parsed.commandExplicit = true;
|
|
180
|
+
delete parsed.flags.topic;
|
|
181
|
+
return "run";
|
|
182
|
+
}
|
|
183
|
+
if (input.kind === "retry") {
|
|
184
|
+
if (!optionalString(parsed.flags.topic)) {
|
|
185
|
+
tuiNotice = messages.tui.retryUnavailable;
|
|
186
|
+
return "continue";
|
|
187
|
+
}
|
|
188
|
+
return "retry";
|
|
189
|
+
}
|
|
190
|
+
if (!input.topic) {
|
|
191
|
+
// Saisie réduite à des flags inline (ex. "--context src") : re-prompter au lieu de sortir en erreur.
|
|
192
|
+
tuiNotice = messages.common.topicRequired;
|
|
193
|
+
return "continue";
|
|
194
|
+
}
|
|
195
|
+
parsed.command = "";
|
|
196
|
+
parsed.commandExplicit = false;
|
|
197
|
+
if (hasCompletedTuiSession || resetTuiRunOverridesOnNextTopic) {
|
|
198
|
+
clearTuiRunOverrides(parsed.flags);
|
|
199
|
+
resetTuiRunOverridesOnNextTopic = false;
|
|
200
|
+
}
|
|
201
|
+
parsed.flags.topic = input.topic;
|
|
202
|
+
if (input.files && input.files.length > 0) {
|
|
203
|
+
parsed.flags.files = input.files;
|
|
204
|
+
}
|
|
205
|
+
if (input.context && input.context.length > 0) {
|
|
206
|
+
parsed.flags.context = input.context;
|
|
207
|
+
}
|
|
208
|
+
return "run";
|
|
185
209
|
}
|
|
186
|
-
parsed.flags.topic = tuiInput.topic;
|
|
187
|
-
return "run";
|
|
188
210
|
};
|
|
189
211
|
if (shouldOpenTuiHome(parsed)) {
|
|
190
212
|
const [syncResult, currentVersion, latestVersion] = await Promise.all([
|
|
@@ -308,6 +330,7 @@ async function main() {
|
|
|
308
330
|
}
|
|
309
331
|
}
|
|
310
332
|
}
|
|
333
|
+
/** Construit un signal d'annulation déclenché sur `SIGINT`/`SIGTERM` pour interrompre un débat en cours. */
|
|
311
334
|
function debateAbortSignal() {
|
|
312
335
|
const controller = new AbortController();
|
|
313
336
|
const abort = () => {
|
|
@@ -328,7 +351,7 @@ async function runConfigCommand(flags) {
|
|
|
328
351
|
const explicitLanguage = optionalString(flags.language);
|
|
329
352
|
if (!(await configExists(configPath))) {
|
|
330
353
|
const messages = createTranslator(resolveLanguage({ explicitLanguage }));
|
|
331
|
-
await
|
|
354
|
+
await writeConfig(configPath);
|
|
332
355
|
console.log(messages.config.createdForConfig(configPath));
|
|
333
356
|
return;
|
|
334
357
|
}
|
|
@@ -339,7 +362,7 @@ async function runConfigCommand(flags) {
|
|
|
339
362
|
});
|
|
340
363
|
const messages = createTranslator(language);
|
|
341
364
|
if (flags["ollama-models"]) {
|
|
342
|
-
await runOllamaModelsCommand(config, Boolean(flags.json));
|
|
365
|
+
await runOllamaModelsCommand(config, Boolean(flags.json), messages);
|
|
343
366
|
return;
|
|
344
367
|
}
|
|
345
368
|
const setOllamaModelValue = optionalString(flags["set-ollama-model"]);
|
|
@@ -358,7 +381,7 @@ async function runConfigCommand(flags) {
|
|
|
358
381
|
console.log(messages.config.syncNoMissing(configPath));
|
|
359
382
|
return;
|
|
360
383
|
}
|
|
361
|
-
await
|
|
384
|
+
await writeConfig(configPath, config);
|
|
362
385
|
console.log(result.addedAgents.length > 0
|
|
363
386
|
? messages.config.syncAdded(configPath, result.addedAgents.join(", "))
|
|
364
387
|
: messages.config.syncRefreshed(configPath));
|
|
@@ -427,19 +450,25 @@ async function runConfigCommand(flags) {
|
|
|
427
450
|
if (changesDefaults) {
|
|
428
451
|
config.defaults = nextDefaults;
|
|
429
452
|
}
|
|
430
|
-
await
|
|
453
|
+
await writeConfig(configPath, config);
|
|
431
454
|
console.log(messages.config.updated(configPath, formatDefaultsForMessage(config.defaults ?? {}, messages), config.language ?? DEFAULT_LANGUAGE));
|
|
432
455
|
return;
|
|
433
456
|
}
|
|
434
457
|
if (flags["clear-defaults"]) {
|
|
435
458
|
delete config.defaults;
|
|
436
|
-
await
|
|
459
|
+
await writeConfig(configPath, config);
|
|
437
460
|
console.log(messages.config.cleared(configPath));
|
|
438
461
|
return;
|
|
439
462
|
}
|
|
440
463
|
await runConfigWizard(configPath, config, messages);
|
|
441
464
|
}
|
|
442
|
-
|
|
465
|
+
/**
|
|
466
|
+
* Affiche l'état de l'agent Ollama local (modèle courant, disponibilité de l'API, modèles installés).
|
|
467
|
+
* @param config - Config chargée.
|
|
468
|
+
* @param json - Si `true`, affiche le résultat en JSON plutôt qu'en texte lisible.
|
|
469
|
+
* @param messages - Dictionnaire localisé pour la sortie texte.
|
|
470
|
+
*/
|
|
471
|
+
async function runOllamaModelsCommand(config, json, messages) {
|
|
443
472
|
const discovery = await discoverLocalToolsForConfig(config);
|
|
444
473
|
const agent = config.agents["ollama-local"];
|
|
445
474
|
const currentModel = agent?.type === "ollama" ? agent.model : null;
|
|
@@ -456,10 +485,16 @@ async function runOllamaModelsCommand(config, json) {
|
|
|
456
485
|
console.log(JSON.stringify(payload, null, 2));
|
|
457
486
|
return;
|
|
458
487
|
}
|
|
459
|
-
console.log(
|
|
460
|
-
console.log(
|
|
461
|
-
console.log(
|
|
488
|
+
console.log(messages.config.ollamaModelsCurrent(currentModel));
|
|
489
|
+
console.log(messages.config.ollamaModelsApi(discovery.ollama.available, discovery.ollama.baseUrl));
|
|
490
|
+
console.log(messages.config.ollamaModelsInstalled(discovery.ollama.models));
|
|
462
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Change le modèle configuré pour l'agent `ollama-local` après vérification qu'il est bien installé.
|
|
494
|
+
* @param configPath - Chemin du fichier de config à mettre à jour.
|
|
495
|
+
* @param config - Config chargée.
|
|
496
|
+
* @param model - Nom du modèle Ollama à définir.
|
|
497
|
+
*/
|
|
463
498
|
async function runSetOllamaModelCommand(configPath, config, model, messages) {
|
|
464
499
|
const trimmed = model.trim();
|
|
465
500
|
if (!trimmed) {
|
|
@@ -474,11 +509,16 @@ async function runSetOllamaModelCommand(configPath, config, model, messages) {
|
|
|
474
509
|
throw new Error(messages.config.ollamaModelUnavailable(trimmed));
|
|
475
510
|
}
|
|
476
511
|
const result = setOllamaModel(config, trimmed);
|
|
477
|
-
await
|
|
512
|
+
await writeConfig(configPath, config);
|
|
478
513
|
console.log(result
|
|
479
514
|
? messages.config.ollamaModelUpdated(configPath, result.previousModel, result.nextModel)
|
|
480
515
|
: messages.config.ollamaModelNoChange(configPath, agent.model));
|
|
481
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Aligne automatiquement le modèle de l'agent `ollama-local` sur un modèle réellement installé.
|
|
519
|
+
* @param configPath - Chemin du fichier de config à mettre à jour.
|
|
520
|
+
* @param config - Config chargée.
|
|
521
|
+
*/
|
|
482
522
|
async function runSyncOllamaModelCommand(configPath, config, messages) {
|
|
483
523
|
const discovery = await discoverLocalToolsForConfig(config);
|
|
484
524
|
const agent = config.agents["ollama-local"];
|
|
@@ -493,7 +533,7 @@ async function runSyncOllamaModelCommand(configPath, config, messages) {
|
|
|
493
533
|
console.log(messages.config.ollamaModelNoChange(configPath, agent.model));
|
|
494
534
|
return;
|
|
495
535
|
}
|
|
496
|
-
await
|
|
536
|
+
await writeConfig(configPath, config);
|
|
497
537
|
console.log(messages.config.ollamaModelUpdated(configPath, result.previousModel, result.nextModel));
|
|
498
538
|
}
|
|
499
539
|
/**
|
|
@@ -511,6 +551,11 @@ function isNoneValue(value) {
|
|
|
511
551
|
function formatDefaultsForMessage(defaults, messages) {
|
|
512
552
|
return messages.config.defaultsSummary(defaults.agentA, defaults.agentB, turnsOrDefault(defaults.turns), defaults.summaryAgent, defaults.askSummaryAgent, defaults.mode, defaults.askAgents, defaults.interface);
|
|
513
553
|
}
|
|
554
|
+
/**
|
|
555
|
+
* Déduplique et valide une liste d'agents `ask` fournie via `--ask-agents`.
|
|
556
|
+
* @param agents - Noms d'agents bruts, éventuellement en doublon.
|
|
557
|
+
* @throws Si la liste dépasse `MAX_ASK_AGENTS` ou référence un agent inconnu de la config.
|
|
558
|
+
*/
|
|
514
559
|
function normalizeAskAgentsForConfig(config, agents, messages) {
|
|
515
560
|
const unique = agents
|
|
516
561
|
.map((agent) => agent.trim())
|
|
@@ -534,24 +579,6 @@ function assertKnownAgent(config, agentName, fieldName, messages) {
|
|
|
534
579
|
throw new Error(messages.common.unknownAgentForField(fieldName, agentName, Object.keys(config.agents).join(", ")));
|
|
535
580
|
}
|
|
536
581
|
}
|
|
537
|
-
function parseModeFlag(value, messages) {
|
|
538
|
-
if (!value) {
|
|
539
|
-
return "debate";
|
|
540
|
-
}
|
|
541
|
-
if (value === "debate" || value === "ask") {
|
|
542
|
-
return value;
|
|
543
|
-
}
|
|
544
|
-
throw new Error(messages.common.unknownMode(value, "debate, ask"));
|
|
545
|
-
}
|
|
546
|
-
function parseInterfaceFlag(value, messages) {
|
|
547
|
-
if (!value) {
|
|
548
|
-
return "tui";
|
|
549
|
-
}
|
|
550
|
-
if (value === "tui" || value === "terminal") {
|
|
551
|
-
return value;
|
|
552
|
-
}
|
|
553
|
-
throw new Error(messages.common.unknownMode(value, "tui, terminal"));
|
|
554
|
-
}
|
|
555
582
|
/**
|
|
556
583
|
* Affiche un aperçu du prompt du premier tour sans appeler aucun agent (flag `--show-prompt`).
|
|
557
584
|
* @param config - Config chargée.
|
|
@@ -638,14 +665,12 @@ function createRendererFromFlags(flags, plainOutputFallback, defaultInterface, m
|
|
|
638
665
|
if (flags.json) {
|
|
639
666
|
return createNdjsonRenderer();
|
|
640
667
|
}
|
|
641
|
-
if (flags.tui) {
|
|
642
|
-
return createTuiRenderer(messages);
|
|
643
|
-
}
|
|
644
|
-
if (flags.terminal || flags.plain || plainOutputFallback || defaultInterface === "terminal") {
|
|
645
|
-
return createConsoleRenderer(true, messages);
|
|
646
|
-
}
|
|
647
668
|
return createAutoRenderer(flags, plainOutputFallback, defaultInterface, messages);
|
|
648
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* Choix du renderer par défaut (`--renderer auto`) : TUI si les flags/config le demandent
|
|
672
|
+
* ou si stdout est un TTY, rendu console plain sinon.
|
|
673
|
+
*/
|
|
649
674
|
function createAutoRenderer(flags, plainOutputFallback, defaultInterface, messages) {
|
|
650
675
|
if (flags.tui) {
|
|
651
676
|
return createTuiRenderer(messages);
|
|
@@ -655,6 +680,11 @@ function createAutoRenderer(flags, plainOutputFallback, defaultInterface, messag
|
|
|
655
680
|
}
|
|
656
681
|
return process.stdout.isTTY ? createTuiRenderer(messages) : createConsoleRenderer(true, messages);
|
|
657
682
|
}
|
|
683
|
+
/**
|
|
684
|
+
* Détermine si l'accueil TUI doit s'ouvrir : commande `run` implicite, sans sujet, preset
|
|
685
|
+
* ni flag de rendu déjà fourni. Toute intention explicite de lancer directement un débat
|
|
686
|
+
* (topic, `--renderer`, `--json`, `--plain`, `--terminal`) désactive l'accueil.
|
|
687
|
+
*/
|
|
658
688
|
function shouldOpenTuiHome(parsed) {
|
|
659
689
|
return parsed.command === "run"
|
|
660
690
|
&& !parsed.commandExplicit
|
|
@@ -679,6 +709,11 @@ function printHelp(messages, command) {
|
|
|
679
709
|
const commandHelp = command ? messages.help.renderCommand(command) : undefined;
|
|
680
710
|
console.log(commandHelp ?? messages.help.render(listPresetNames().join(", ")));
|
|
681
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Résout la commande cible pour l'aide contextuelle (`palabre <cmd> --help`), en normalisant
|
|
714
|
+
* les alias (`agent` -> `agents`, `preset` -> `presets`, `setup` -> `init`).
|
|
715
|
+
* @returns `undefined` pour `help`/`run`, qui utilisent l'aide générale.
|
|
716
|
+
*/
|
|
682
717
|
function commandHelpTarget(parsed) {
|
|
683
718
|
if (parsed.command === "help" || parsed.command === "run") {
|
|
684
719
|
return undefined;
|
|
@@ -706,16 +741,16 @@ async function resolveCommandMessages(flags) {
|
|
|
706
741
|
}
|
|
707
742
|
return createTranslator(resolveLanguage({ explicitLanguage, configLanguage }));
|
|
708
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* Formate une erreur non gérée remontée jusqu'au point d'entrée en message lisible,
|
|
746
|
+
* en spécialisant `AdapterError` et `OllamaUrlError` pour rester actionnable.
|
|
747
|
+
*/
|
|
709
748
|
function formatRuntimeError(error, messages) {
|
|
710
749
|
if (error instanceof AdapterError) {
|
|
711
750
|
return formatAdapterError(error, messages);
|
|
712
751
|
}
|
|
713
752
|
if (error instanceof OllamaUrlError) {
|
|
714
|
-
|
|
715
|
-
return messages.common.ollamaUrlEmpty;
|
|
716
|
-
if (error.kind === "protocol")
|
|
717
|
-
return messages.common.ollamaUrlProtocol(error.protocol ?? "");
|
|
718
|
-
return messages.common.ollamaUrlInvalid(error.value);
|
|
753
|
+
return formatOllamaUrlError(error, messages);
|
|
719
754
|
}
|
|
720
755
|
return error instanceof Error ? error.message : String(error);
|
|
721
756
|
}
|
|
@@ -726,6 +761,10 @@ main().catch((error) => {
|
|
|
726
761
|
console.error(`${messages.common.errorPrefix}: ${message}`);
|
|
727
762
|
process.exitCode = 1;
|
|
728
763
|
});
|
|
764
|
+
/**
|
|
765
|
+
* Variante de `findRawLanguageFlag` + `resolveLanguage` qui ne peut pas lever, utilisée dans
|
|
766
|
+
* le gestionnaire d'erreur global où la config n'est pas forcément chargée.
|
|
767
|
+
*/
|
|
729
768
|
function safeStartupLanguage(args) {
|
|
730
769
|
try {
|
|
731
770
|
return resolveLanguage({ explicitLanguage: findRawLanguageFlag(args) });
|
package/dist/limits.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
/** @file Limites produit sur le nombre de tours et d'agents `ask`, et parsing/validation de `--turns`. */
|
|
1
2
|
import { createTranslator } from "./i18n.js";
|
|
3
|
+
/** Nombre de tours par défaut quand `--turns` n'est pas fourni. */
|
|
2
4
|
export const DEFAULT_TURNS = 4;
|
|
5
|
+
/** Nombre maximal d'agents acceptés par `--agents` en mode `ask`. */
|
|
3
6
|
export const MAX_ASK_AGENTS = 4;
|
|
7
|
+
/** Borne haute produit pour `--turns`, au-delà considérée comme une erreur d'utilisation. */
|
|
4
8
|
export const MAX_TURNS = 20;
|
|
5
9
|
/** Convertit `value` en nombre et valide la plage [1, `MAX_TURNS`]. Lève une erreur si invalide. */
|
|
6
10
|
export function parseTurns(value, label = "--turns", messages = createTranslator("fr")) {
|