palabre 0.10.0 → 0.10.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 +6 -0
- package/dist/index.js +19 -0
- package/dist/new.js +64 -11
- package/dist/renderers/tui-prompts.js +5 -1
- package/dist/renderers/tui-renderer.js +5 -1
- package/dist/renderers/tui-theme.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,9 @@ Requirements: Node.js 20 or newer, and at least two already installed/authentica
|
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
npm install -g palabre
|
|
33
|
+
# or: pnpm add --global palabre
|
|
34
|
+
# or: yarn global add palabre
|
|
35
|
+
# or: bun add --global palabre
|
|
33
36
|
palabre --version
|
|
34
37
|
palabre --help
|
|
35
38
|
```
|
|
@@ -139,6 +142,9 @@ Prérequis : Node.js 20 ou plus, et au moins deux agents déjà installés/authe
|
|
|
139
142
|
|
|
140
143
|
```bash
|
|
141
144
|
npm install -g palabre
|
|
145
|
+
# ou : pnpm add --global palabre
|
|
146
|
+
# ou : yarn global add palabre
|
|
147
|
+
# ou : bun add --global palabre
|
|
142
148
|
palabre --version
|
|
143
149
|
palabre --help
|
|
144
150
|
```
|
package/dist/index.js
CHANGED
|
@@ -240,6 +240,25 @@ async function main() {
|
|
|
240
240
|
if (parsed.command === "new") {
|
|
241
241
|
const selection = await runNewWizard(config, messages);
|
|
242
242
|
if (!selection) {
|
|
243
|
+
if (stayInTuiAfterSession) {
|
|
244
|
+
tuiNotice = messages.new.cancelled;
|
|
245
|
+
parsed.command = "";
|
|
246
|
+
parsed.commandExplicit = false;
|
|
247
|
+
for (;;) {
|
|
248
|
+
renderTuiHome(config, configPath, messages, { mode: tuiMode, version: tuiVersion, latestVersion: tuiLatestVersion });
|
|
249
|
+
const nextInput = await promptTuiHomeTopic(tuiMode, messages, { notice: tuiNotice });
|
|
250
|
+
tuiNotice = undefined;
|
|
251
|
+
const action = await handleTuiHomeInput(nextInput);
|
|
252
|
+
if (action === "quit")
|
|
253
|
+
return;
|
|
254
|
+
if (action === "continue" || action === "retry")
|
|
255
|
+
continue;
|
|
256
|
+
parsed.flags.mode = tuiMode;
|
|
257
|
+
parsed.flags.renderer = "tui";
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
243
262
|
console.log(messages.new.cancelled);
|
|
244
263
|
return;
|
|
245
264
|
}
|
package/dist/new.js
CHANGED
|
@@ -5,6 +5,8 @@ import { isAgentDetected } from "./agentRegistry.js";
|
|
|
5
5
|
import { discoverLocalTools } from "./discovery.js";
|
|
6
6
|
import { findPresetNameForPair } from "./presets.js";
|
|
7
7
|
import { MAX_TURNS, turnsOrDefault, validateTurns } from "./limits.js";
|
|
8
|
+
import { accent, bold, brandHeader, card, clearScreen, dim, padBlock, supportsInteractiveOutput, surfaceWidth } from "./renderers/tui-theme.js";
|
|
9
|
+
const interruptedAnswer = "\u0000palabre-interrupted";
|
|
8
10
|
/**
|
|
9
11
|
* Lance le wizard interactif `palabre new`.
|
|
10
12
|
* Détecte les outils locaux, liste les agents de la config et guide la composition du débat.
|
|
@@ -18,10 +20,7 @@ export async function runNewWizard(config, messages) {
|
|
|
18
20
|
}
|
|
19
21
|
const rl = await createQuestioner();
|
|
20
22
|
try {
|
|
21
|
-
|
|
22
|
-
console.log(messages.new.quitHint);
|
|
23
|
-
console.log(messages.new.defaultHint);
|
|
24
|
-
console.log("");
|
|
23
|
+
renderWizardIntro(messages);
|
|
25
24
|
const mode = await askMode(rl, config.defaults?.mode ?? "debate", messages);
|
|
26
25
|
if (!mode)
|
|
27
26
|
return undefined;
|
|
@@ -176,11 +175,21 @@ async function askMode(rl, defaultMode, messages) {
|
|
|
176
175
|
{ value: "ask", label: messages.new.modeAsk }
|
|
177
176
|
];
|
|
178
177
|
const fallback = choices.find((choice) => choice.value === defaultMode)?.value ?? "debate";
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
178
|
+
if (supportsInteractiveOutput) {
|
|
179
|
+
const lines = choices.map((choice, index) => {
|
|
180
|
+
const marker = choice.value === fallback ? accent("(*)") : " ";
|
|
181
|
+
return `${bold(`${index + 1})`)} ${marker} ${choice.label}`;
|
|
182
|
+
});
|
|
183
|
+
console.log(padBlock(card(lines, surfaceWidth(), messages.new.mode)).join("\n"));
|
|
184
|
+
console.log("");
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.log(messages.new.mode);
|
|
188
|
+
choices.forEach((choice, index) => {
|
|
189
|
+
const marker = choice.value === fallback ? "(*)" : " ";
|
|
190
|
+
console.log(` ${index + 1}) ${marker} ${choice.label}`);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
184
193
|
while (true) {
|
|
185
194
|
const answer = await rl.question(`${messages.new.mode} [${fallback}]: `);
|
|
186
195
|
const value = answer.trim().toLowerCase();
|
|
@@ -201,7 +210,33 @@ async function askMode(rl, defaultMode, messages) {
|
|
|
201
210
|
}
|
|
202
211
|
async function createQuestioner() {
|
|
203
212
|
if (input.isTTY) {
|
|
204
|
-
|
|
213
|
+
const rl = createInterface({ input, output });
|
|
214
|
+
return {
|
|
215
|
+
question(prompt) {
|
|
216
|
+
return new Promise((resolve, reject) => {
|
|
217
|
+
let settled = false;
|
|
218
|
+
const cleanup = () => rl.off("SIGINT", onSigint);
|
|
219
|
+
const settle = (value) => {
|
|
220
|
+
if (settled)
|
|
221
|
+
return;
|
|
222
|
+
settled = true;
|
|
223
|
+
cleanup();
|
|
224
|
+
resolve(value);
|
|
225
|
+
};
|
|
226
|
+
const onSigint = () => settle(interruptedAnswer);
|
|
227
|
+
rl.once("SIGINT", onSigint);
|
|
228
|
+
rl.question(prompt).then(settle, (error) => {
|
|
229
|
+
if (settled)
|
|
230
|
+
return;
|
|
231
|
+
cleanup();
|
|
232
|
+
reject(error);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
close() {
|
|
237
|
+
rl.close();
|
|
238
|
+
}
|
|
239
|
+
};
|
|
205
240
|
}
|
|
206
241
|
const lines = await readPipedLines();
|
|
207
242
|
let index = 0;
|
|
@@ -376,7 +411,25 @@ function uniqueNames(names) {
|
|
|
376
411
|
return names.filter((name, index) => names.indexOf(name) === index);
|
|
377
412
|
}
|
|
378
413
|
function isQuit(value) {
|
|
379
|
-
return ["q", "quit", "exit"].includes(value.toLowerCase());
|
|
414
|
+
return value === interruptedAnswer || ["q", "quit", "exit"].includes(value.toLowerCase());
|
|
415
|
+
}
|
|
416
|
+
function renderWizardIntro(messages) {
|
|
417
|
+
if (!supportsInteractiveOutput) {
|
|
418
|
+
console.log(messages.new.title);
|
|
419
|
+
console.log(messages.new.quitHint);
|
|
420
|
+
console.log(messages.new.defaultHint);
|
|
421
|
+
console.log("");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
clearScreen();
|
|
425
|
+
console.log("");
|
|
426
|
+
console.log(padBlock([brandHeader(messages.new.title)]).join("\n"));
|
|
427
|
+
console.log("");
|
|
428
|
+
console.log(padBlock([
|
|
429
|
+
dim(messages.new.quitHint),
|
|
430
|
+
dim(messages.new.defaultHint)
|
|
431
|
+
]).join("\n"));
|
|
432
|
+
console.log("");
|
|
380
433
|
}
|
|
381
434
|
function printCommandPreview(selection, messages) {
|
|
382
435
|
const explicitCommand = buildExplicitCommand(selection);
|
|
@@ -7,6 +7,10 @@ import { createInterface } from "node:readline/promises";
|
|
|
7
7
|
import { stdin as input, stdout as output } from "node:process";
|
|
8
8
|
import { renderTuiAgentsHelp, renderTuiRolesHelp } from "./tui-screens.js";
|
|
9
9
|
import { accent, bold, composerCard, dim, glyphs, labeledRule, padBlock, surfacePadding, surfaceWidth, violet, wrapLine } from "./tui-theme.js";
|
|
10
|
+
/** Traduit une interruption du composer : premier Ctrl+C = accueil, second = fermeture. */
|
|
11
|
+
export function tuiHomeInterruptInput(kind) {
|
|
12
|
+
return kind === "back" ? { kind: "home" } : undefined;
|
|
13
|
+
}
|
|
10
14
|
/** Parse `/ollama-url <url>` : renvoie `unknown` avec le message d'usage si l'argument est absent. */
|
|
11
15
|
export function parseTuiOllamaUrlCommand(parts, messages) {
|
|
12
16
|
const value = parts[1];
|
|
@@ -85,7 +89,7 @@ export async function promptTuiHomeTopic(mode = "debate", messages, options = {}
|
|
|
85
89
|
try {
|
|
86
90
|
const result = await questionWithInterrupt(rl, tuiPrompt(mode, messages.tui.subject, messages, options.notice));
|
|
87
91
|
if (result.kind !== "answer") {
|
|
88
|
-
return
|
|
92
|
+
return tuiHomeInterruptInput(result.kind);
|
|
89
93
|
}
|
|
90
94
|
const answer = result.value;
|
|
91
95
|
const value = answer.trim();
|
|
@@ -24,6 +24,7 @@ class TuiRenderer {
|
|
|
24
24
|
spinnerFrame = 0;
|
|
25
25
|
currentSection = "debate";
|
|
26
26
|
currentAgent;
|
|
27
|
+
sessionMode = "debate";
|
|
27
28
|
/** Titre du tour en attente, rendu en tête du prochain bloc `message`. */
|
|
28
29
|
pendingHeader;
|
|
29
30
|
constructor(messages, color, interactive) {
|
|
@@ -32,6 +33,7 @@ class TuiRenderer {
|
|
|
32
33
|
this.interactive = interactive;
|
|
33
34
|
}
|
|
34
35
|
start(options, agents = []) {
|
|
36
|
+
this.sessionMode = options.mode;
|
|
35
37
|
if (this.interactive) {
|
|
36
38
|
clearScreen();
|
|
37
39
|
}
|
|
@@ -109,13 +111,15 @@ class TuiRenderer {
|
|
|
109
111
|
const width = this.width();
|
|
110
112
|
const folderPath = path.dirname(outputPath);
|
|
111
113
|
const fileName = path.basename(outputPath);
|
|
114
|
+
const modeCommand = this.sessionMode === "ask" ? "/debat" : "/ask";
|
|
112
115
|
process.stdout.write(`\n${padBlock(panel([
|
|
113
116
|
`${success(glyphs().check)} ${bold(this.messages.tui.sessionDone)}`,
|
|
114
117
|
"",
|
|
115
118
|
row(this.messages.tui.exportedFile, terminalLink(outputPath, compactFileName(fileName, width - 24))),
|
|
116
119
|
row(this.messages.tui.exportedFolder, terminalLink(folderPath, compactPath(folderPath, width - 24))),
|
|
117
120
|
"",
|
|
118
|
-
dim(this.messages.tui.
|
|
121
|
+
`${accent("/retry")} ${dim(this.messages.tui.helpRetry)} ${accent("/new")} ${dim(this.messages.tui.helpNew)} ${accent(modeCommand)} ${dim(this.messages.tui.changeMode)}`,
|
|
122
|
+
`${accent("/history")} ${dim(this.messages.tui.helpHistory)} ${accent("/config")} ${dim(this.messages.tui.helpConfig)} ${accent("/help")} ${dim(this.messages.tui.helpHelp)}`
|
|
119
123
|
], width)).join("\n")}\n\n`);
|
|
120
124
|
}
|
|
121
125
|
renderSessionHeader(options, agents) {
|
|
@@ -344,7 +344,7 @@ export const codes = {
|
|
|
344
344
|
reset: "\u001b[0m",
|
|
345
345
|
bright: "\u001b[1m",
|
|
346
346
|
dim: "\u001b[2m",
|
|
347
|
-
logoViolet: "\u001b[38;2;
|
|
347
|
+
logoViolet: "\u001b[38;2;139;105;218m",
|
|
348
348
|
violet: "\u001b[38;5;141m",
|
|
349
349
|
cyan: "\u001b[36m",
|
|
350
350
|
gray: "\u001b[38;5;244m",
|