palabre 0.6.4 → 0.8.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/README.md +99 -63
- package/dist/adapters/cli-pty.js +14 -0
- package/dist/adapters/cli.js +32 -4
- package/dist/adapters/ollama.js +15 -0
- package/dist/agentRegistry.js +3 -2
- package/dist/args.js +22 -1
- package/dist/config.js +78 -4
- package/dist/configWizard.js +29 -6
- package/dist/discovery.js +3 -1
- package/dist/doctor.js +18 -0
- package/dist/index.js +593 -55
- package/dist/messages/agents.js +4 -2
- package/dist/messages/common.js +4 -0
- package/dist/messages/config.js +28 -8
- package/dist/messages/doctor.js +8 -0
- package/dist/messages/help.js +58 -2
- package/dist/messages/index.js +2 -0
- package/dist/messages/init.js +2 -2
- package/dist/messages/new.js +14 -0
- package/dist/messages/orchestrator.js +2 -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 +192 -0
- package/dist/messages/update.js +16 -2
- package/dist/new.js +158 -4
- package/dist/orchestrator.js +199 -6
- package/dist/output.js +31 -8
- package/dist/presets.js +48 -0
- 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 +932 -0
- package/dist/update.js +2 -0
- package/dist/version.js +54 -0
- package/package.json +2 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
export const tuiMessages = {
|
|
2
|
+
fr: {
|
|
3
|
+
tagline: "Orchestrez des conversations entre agents IA",
|
|
4
|
+
modeLabel: (mode) => mode === "ask" ? "Ask" : "Debat",
|
|
5
|
+
modeValue: (mode) => mode === "ask" ? "Ask" : "Debat",
|
|
6
|
+
noValue: "non definis",
|
|
7
|
+
lastAskAgent: "dernier agent ask",
|
|
8
|
+
roles: "Roles",
|
|
9
|
+
summary: "Synthese",
|
|
10
|
+
responses: "Reponses",
|
|
11
|
+
folder: "Dossier",
|
|
12
|
+
docs: "Docs",
|
|
13
|
+
commands: "commandes",
|
|
14
|
+
settings: "reglages",
|
|
15
|
+
changeMode: "changer de mode",
|
|
16
|
+
tipContext: "* Tip Ajoute du contexte avec --context <dossier> ou --files <fichier>.",
|
|
17
|
+
helpTitle: "Commandes TUI",
|
|
18
|
+
helpAsk: "passer en mode Ask pour collecter plusieurs reponses independantes",
|
|
19
|
+
helpDebate: "passer en mode Debat pour lancer une conversation entre deux agents",
|
|
20
|
+
helpAgents: "afficher et choisir les agents actifs",
|
|
21
|
+
helpRoles: "afficher les roles disponibles",
|
|
22
|
+
helpConfig: "configurer Palabre sans sortir de la TUI",
|
|
23
|
+
helpNew: "ouvrir l'assistant guide",
|
|
24
|
+
helpHelp: "afficher cette aide",
|
|
25
|
+
helpQuit: "quitter la TUI",
|
|
26
|
+
helpFallback: "Tout autre texte est utilise comme demande a envoyer aux agents.",
|
|
27
|
+
agentsTitle: "Agents Palabre",
|
|
28
|
+
activeMode: "Mode actif",
|
|
29
|
+
activeAgents: "Agents actifs",
|
|
30
|
+
availableAgents: "Agents disponibles",
|
|
31
|
+
example: "Exemple",
|
|
32
|
+
rolesTitle: "Roles Palabre",
|
|
33
|
+
currentConfig: "Config actuelle",
|
|
34
|
+
availableRoles: "Roles disponibles",
|
|
35
|
+
roleImplementer: "propose une solution concrete",
|
|
36
|
+
roleCritic: "challenge les hypotheses et demande des preuves",
|
|
37
|
+
roleArchitect: "structure les options et compromis",
|
|
38
|
+
roleScout: "explore les pistes et inconnues",
|
|
39
|
+
roleReviewer: "cherche risques et tests manquants",
|
|
40
|
+
roleSummarizer: "synthetise fidelement",
|
|
41
|
+
configTitle: "Configuration Palabre",
|
|
42
|
+
configFile: "Config",
|
|
43
|
+
interface: "Interface",
|
|
44
|
+
language: "Langue",
|
|
45
|
+
availableAgentsShort: "Agents dispo",
|
|
46
|
+
availableCommands: "Commandes disponibles",
|
|
47
|
+
noConfiguredAgents: "aucun agent configure",
|
|
48
|
+
or: "ou",
|
|
49
|
+
defaultModeCommand: (mode) => `utiliser ${mode === "ask" ? "Ask" : "Debat"} par defaut`,
|
|
50
|
+
modeConfigCommand: "changer de mode de configuration",
|
|
51
|
+
backCommand: "revenir a l'accueil",
|
|
52
|
+
quitCommand: "quitter",
|
|
53
|
+
subject: "Sujet",
|
|
54
|
+
configPrompt: "Config",
|
|
55
|
+
agentsPrompt: "Agents",
|
|
56
|
+
rolesPrompt: "Roles",
|
|
57
|
+
sessionDone: "Session terminee",
|
|
58
|
+
askResponse: (response, totalResponses) => `reponse ${response}/${totalResponses}`,
|
|
59
|
+
turnLabel: (turn) => `tour ${turn}`,
|
|
60
|
+
planTitle: "Plan de session",
|
|
61
|
+
planCollectAsk: "Collecter les reponses",
|
|
62
|
+
planLaunchDebate: "Lancer le debat",
|
|
63
|
+
planSummaryComparative: "Synthese comparative",
|
|
64
|
+
planSummaryDisabled: "Synthese desactivee",
|
|
65
|
+
planExport: "Export Markdown",
|
|
66
|
+
ptyNotice: (agents) => `Note: ${agents} utilise un pseudo-terminal; une fenetre peut apparaitre brievement.`,
|
|
67
|
+
unknownCommand: "Commande inconnue. Utilise /back pour revenir.",
|
|
68
|
+
turnsUsage: "Usage: /turns <nombre>",
|
|
69
|
+
summaryUsage: "Usage: /summary <agent|none>",
|
|
70
|
+
interfaceDefault: (value) => `Interface par defaut: ${value}.`,
|
|
71
|
+
languageUpdated: (value) => `Langue mise a jour: ${value}.`,
|
|
72
|
+
askConfigMode: "Configuration Ask.",
|
|
73
|
+
debateConfigMode: "Configuration Debat.",
|
|
74
|
+
askDefaultMode: "Ask devient le mode par defaut.",
|
|
75
|
+
debateDefaultMode: "Debat devient le mode par defaut.",
|
|
76
|
+
agentsUnchanged: "Agents inchanges.",
|
|
77
|
+
rolesUnchanged: "Roles inchanges.",
|
|
78
|
+
askTurnsNotice: "En mode Ask, le nombre de reponses depend des agents selectionnes avec /agents.",
|
|
79
|
+
turnsUpdated: (value) => `Tours mis a jour: ${value}.`,
|
|
80
|
+
askSummaryFallback: "Synthese Ask revenue au fallback.",
|
|
81
|
+
debateSummaryFallback: "Synthese Debat revenue au fallback.",
|
|
82
|
+
askSummaryAgent: (value) => `Synthese Ask: ${value}.`,
|
|
83
|
+
debateSummaryAgent: (value) => `Synthese Debat: ${value}.`,
|
|
84
|
+
askAgentsUpdated: (value) => `Agents Ask mis a jour: ${value}.`,
|
|
85
|
+
debateAgentsUpdated: (value) => `Agents Debat mis a jour: ${value}.`,
|
|
86
|
+
askRolesUpdated: (value) => `Roles Ask mis a jour: ${value}.`,
|
|
87
|
+
debateRolesUpdated: (value) => `Roles Debat mis a jour: ${value}.`,
|
|
88
|
+
rolesError: (message) => `Erreur roles: ${message}`,
|
|
89
|
+
agentsError: (message) => `Erreur agents: ${message}`,
|
|
90
|
+
noAskAgentsConfigured: "Aucun agent Ask configure.",
|
|
91
|
+
noDebateAgentsConfigured: "Agents Debat non definis.",
|
|
92
|
+
rolesCountError: (count, expected, agents) => `${count} role(s) saisi(s), ${expected} attendu(s). Saisis au moins ${expected} roles pour: ${agents}.`,
|
|
93
|
+
unknownRole: (role, available) => `Role inconnu: ${role}. Roles disponibles: ${available}.`,
|
|
94
|
+
debateAgentsUsage: "Usage: /agents <agentA> <agentB>",
|
|
95
|
+
askAgentsUsage: "Usage: /agents <agent...>"
|
|
96
|
+
},
|
|
97
|
+
en: {
|
|
98
|
+
tagline: "Orchestrate conversations between AI agents",
|
|
99
|
+
modeLabel: (mode) => mode === "ask" ? "Ask" : "Debate",
|
|
100
|
+
modeValue: (mode) => mode === "ask" ? "Ask" : "Debate",
|
|
101
|
+
noValue: "not set",
|
|
102
|
+
lastAskAgent: "last ask agent",
|
|
103
|
+
roles: "Roles",
|
|
104
|
+
summary: "Summary",
|
|
105
|
+
responses: "Responses",
|
|
106
|
+
folder: "Folder",
|
|
107
|
+
docs: "Docs",
|
|
108
|
+
commands: "commands",
|
|
109
|
+
settings: "settings",
|
|
110
|
+
changeMode: "change mode",
|
|
111
|
+
tipContext: "* Tip Add context with --context <folder> or --files <file>.",
|
|
112
|
+
helpTitle: "TUI Commands",
|
|
113
|
+
helpAsk: "switch to Ask mode to collect independent responses",
|
|
114
|
+
helpDebate: "switch to Debate mode for a conversation between two agents",
|
|
115
|
+
helpAgents: "show and choose active agents",
|
|
116
|
+
helpRoles: "show available roles",
|
|
117
|
+
helpConfig: "configure Palabre without leaving the TUI",
|
|
118
|
+
helpNew: "open the guided assistant",
|
|
119
|
+
helpHelp: "show this help",
|
|
120
|
+
helpQuit: "quit the TUI",
|
|
121
|
+
helpFallback: "Any other text is used as the request sent to agents.",
|
|
122
|
+
agentsTitle: "Palabre Agents",
|
|
123
|
+
activeMode: "Active mode",
|
|
124
|
+
activeAgents: "Active agents",
|
|
125
|
+
availableAgents: "Available agents",
|
|
126
|
+
example: "Example",
|
|
127
|
+
rolesTitle: "Palabre Roles",
|
|
128
|
+
currentConfig: "Current config",
|
|
129
|
+
availableRoles: "Available roles",
|
|
130
|
+
roleImplementer: "proposes a concrete solution",
|
|
131
|
+
roleCritic: "challenges assumptions and asks for evidence",
|
|
132
|
+
roleArchitect: "structures options and trade-offs",
|
|
133
|
+
roleScout: "explores paths and unknowns",
|
|
134
|
+
roleReviewer: "looks for risks and missing tests",
|
|
135
|
+
roleSummarizer: "summarizes faithfully",
|
|
136
|
+
configTitle: "Palabre Configuration",
|
|
137
|
+
configFile: "Config",
|
|
138
|
+
interface: "Interface",
|
|
139
|
+
language: "Language",
|
|
140
|
+
availableAgentsShort: "Agents",
|
|
141
|
+
availableCommands: "Available commands",
|
|
142
|
+
noConfiguredAgents: "no configured agent",
|
|
143
|
+
or: "or",
|
|
144
|
+
defaultModeCommand: (mode) => `use ${mode === "ask" ? "Ask" : "Debate"} as default`,
|
|
145
|
+
modeConfigCommand: "change configuration mode",
|
|
146
|
+
backCommand: "return to home",
|
|
147
|
+
quitCommand: "quit",
|
|
148
|
+
subject: "Subject",
|
|
149
|
+
configPrompt: "Config",
|
|
150
|
+
agentsPrompt: "Agents",
|
|
151
|
+
rolesPrompt: "Roles",
|
|
152
|
+
sessionDone: "Session complete",
|
|
153
|
+
askResponse: (response, totalResponses) => `response ${response}/${totalResponses}`,
|
|
154
|
+
turnLabel: (turn) => `turn ${turn}`,
|
|
155
|
+
planTitle: "Session plan",
|
|
156
|
+
planCollectAsk: "Collect responses",
|
|
157
|
+
planLaunchDebate: "Start debate",
|
|
158
|
+
planSummaryComparative: "Comparative summary",
|
|
159
|
+
planSummaryDisabled: "Summary disabled",
|
|
160
|
+
planExport: "Markdown export",
|
|
161
|
+
ptyNotice: (agents) => `Note: ${agents} uses a pseudo-terminal; a window may briefly appear.`,
|
|
162
|
+
unknownCommand: "Unknown command. Use /back to return.",
|
|
163
|
+
turnsUsage: "Usage: /turns <number>",
|
|
164
|
+
summaryUsage: "Usage: /summary <agent|none>",
|
|
165
|
+
interfaceDefault: (value) => `Default interface: ${value}.`,
|
|
166
|
+
languageUpdated: (value) => `Language updated: ${value}.`,
|
|
167
|
+
askConfigMode: "Ask configuration.",
|
|
168
|
+
debateConfigMode: "Debate configuration.",
|
|
169
|
+
askDefaultMode: "Ask is now the default mode.",
|
|
170
|
+
debateDefaultMode: "Debate is now the default mode.",
|
|
171
|
+
agentsUnchanged: "Agents unchanged.",
|
|
172
|
+
rolesUnchanged: "Roles unchanged.",
|
|
173
|
+
askTurnsNotice: "In Ask mode, the number of responses depends on agents selected with /agents.",
|
|
174
|
+
turnsUpdated: (value) => `Turns updated: ${value}.`,
|
|
175
|
+
askSummaryFallback: "Ask summary returned to fallback.",
|
|
176
|
+
debateSummaryFallback: "Debate summary returned to fallback.",
|
|
177
|
+
askSummaryAgent: (value) => `Ask summary: ${value}.`,
|
|
178
|
+
debateSummaryAgent: (value) => `Debate summary: ${value}.`,
|
|
179
|
+
askAgentsUpdated: (value) => `Ask agents updated: ${value}.`,
|
|
180
|
+
debateAgentsUpdated: (value) => `Debate agents updated: ${value}.`,
|
|
181
|
+
askRolesUpdated: (value) => `Ask roles updated: ${value}.`,
|
|
182
|
+
debateRolesUpdated: (value) => `Debate roles updated: ${value}.`,
|
|
183
|
+
rolesError: (message) => `Roles error: ${message}`,
|
|
184
|
+
agentsError: (message) => `Agents error: ${message}`,
|
|
185
|
+
noAskAgentsConfigured: "No Ask agent configured.",
|
|
186
|
+
noDebateAgentsConfigured: "Debate agents are not set.",
|
|
187
|
+
rolesCountError: (count, expected, agents) => `${count} role(s) entered, ${expected} expected. Enter at least ${expected} roles for: ${agents}.`,
|
|
188
|
+
unknownRole: (role, available) => `Unknown role: ${role}. Available roles: ${available}.`,
|
|
189
|
+
debateAgentsUsage: "Usage: /agents <agentA> <agentB>",
|
|
190
|
+
askAgentsUsage: "Usage: /agents <agent...>"
|
|
191
|
+
}
|
|
192
|
+
};
|
package/dist/messages/update.js
CHANGED
|
@@ -13,7 +13,14 @@ export const updateMessages = {
|
|
|
13
13
|
lines.push("", "Installation depuis le repo source detectee.", "", ` cd "${options.projectRoot}"`, " git pull --ff-only", " pnpm install", " pnpm build", " pnpm link --global", "", "Pour executer ces etapes automatiquement:", "", " palabre update --apply");
|
|
14
14
|
}
|
|
15
15
|
else {
|
|
16
|
-
lines.push("", "Installation package detectee.", "", " pnpm add --global palabre@latest"
|
|
16
|
+
lines.push("", "Installation package detectee.", "", " pnpm add --global palabre@latest");
|
|
17
|
+
if (options.latestVersion) {
|
|
18
|
+
lines.push("", "Si pnpm garde une ancienne version malgré @latest:", "", ` pnpm add --global palabre@${options.latestVersion}`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
lines.push("", "Pour verifier la derniere version disponible:", "", " pnpm view palabre version");
|
|
22
|
+
}
|
|
23
|
+
lines.push("", "Si tu utilises npm:", "", " npm install --global palabre@latest");
|
|
17
24
|
}
|
|
18
25
|
return lines.join("\n");
|
|
19
26
|
}
|
|
@@ -32,7 +39,14 @@ export const updateMessages = {
|
|
|
32
39
|
lines.push("", "Source repository installation detected.", "", ` cd "${options.projectRoot}"`, " git pull --ff-only", " pnpm install", " pnpm build", " pnpm link --global", "", "To run these steps automatically:", "", " palabre update --apply");
|
|
33
40
|
}
|
|
34
41
|
else {
|
|
35
|
-
lines.push("", "Package installation detected.", "", " pnpm add --global palabre@latest"
|
|
42
|
+
lines.push("", "Package installation detected.", "", " pnpm add --global palabre@latest");
|
|
43
|
+
if (options.latestVersion) {
|
|
44
|
+
lines.push("", "If pnpm keeps an older version despite @latest:", "", ` pnpm add --global palabre@${options.latestVersion}`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
lines.push("", "To check the latest available version:", "", " pnpm view palabre version");
|
|
48
|
+
}
|
|
49
|
+
lines.push("", "If you use npm:", "", " npm install --global palabre@latest");
|
|
36
50
|
}
|
|
37
51
|
return lines.join("\n");
|
|
38
52
|
}
|
package/dist/new.js
CHANGED
|
@@ -21,6 +21,75 @@ export async function runNewWizard(config, messages) {
|
|
|
21
21
|
console.log(messages.new.quitHint);
|
|
22
22
|
console.log(messages.new.defaultHint);
|
|
23
23
|
console.log("");
|
|
24
|
+
const mode = await askMode(rl, config.defaults?.mode ?? "debate", messages);
|
|
25
|
+
if (!mode)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (mode === "ask") {
|
|
28
|
+
const askAgents = await askAgentList(rl, choices, config.defaults?.askAgents ?? defaultAskAgents(config), messages);
|
|
29
|
+
if (!askAgents)
|
|
30
|
+
return undefined;
|
|
31
|
+
const [agentA, agentB] = [askAgents[0], askAgents[1] ?? askAgents[0]];
|
|
32
|
+
if (!agentA || !agentB)
|
|
33
|
+
return undefined;
|
|
34
|
+
const topic = await askRequiredText(rl, messages.new.topic, messages);
|
|
35
|
+
if (!topic)
|
|
36
|
+
return undefined;
|
|
37
|
+
printCommandPreview({ mode, agentA, agentB, askAgents, topic }, messages);
|
|
38
|
+
console.log(messages.new.advancedHint);
|
|
39
|
+
const launchMinimal = await askYesNo(rl, messages.new.launchMinimal, true, messages);
|
|
40
|
+
if (launchMinimal === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (launchMinimal) {
|
|
43
|
+
return {
|
|
44
|
+
mode,
|
|
45
|
+
agentA,
|
|
46
|
+
agentB,
|
|
47
|
+
askAgents,
|
|
48
|
+
topic,
|
|
49
|
+
files: [],
|
|
50
|
+
context: [],
|
|
51
|
+
showPrompt: false,
|
|
52
|
+
plainOutput: false
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const summaryEnabled = await askYesNo(rl, messages.new.summaryEnabled, true, messages);
|
|
56
|
+
if (summaryEnabled === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
let summaryAgent;
|
|
59
|
+
let summaryModel;
|
|
60
|
+
if (summaryEnabled) {
|
|
61
|
+
summaryAgent = await askAgent(rl, choices, messages.new.summaryAgent, config.defaults?.askSummaryAgent ?? config.defaults?.summaryAgent ?? askAgents[askAgents.length - 1], messages);
|
|
62
|
+
if (!summaryAgent)
|
|
63
|
+
return undefined;
|
|
64
|
+
summaryModel = await askOptionalText(rl, messages.new.summaryModelFor(summaryAgent));
|
|
65
|
+
if (summaryModel === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const context = splitPaths(await askOptionalText(rl, messages.new.contextPaths));
|
|
69
|
+
const files = splitPaths(await askOptionalText(rl, messages.new.filesPaths));
|
|
70
|
+
const showPrompt = await askYesNo(rl, messages.new.showPrompt, false, messages);
|
|
71
|
+
if (showPrompt === undefined)
|
|
72
|
+
return undefined;
|
|
73
|
+
const plainOutput = await askYesNo(rl, messages.new.plainOutput, false, messages);
|
|
74
|
+
if (plainOutput === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
const selection = {
|
|
77
|
+
mode,
|
|
78
|
+
agentA,
|
|
79
|
+
agentB,
|
|
80
|
+
askAgents,
|
|
81
|
+
topic,
|
|
82
|
+
summaryAgent,
|
|
83
|
+
summaryModel,
|
|
84
|
+
summaryEnabled,
|
|
85
|
+
files,
|
|
86
|
+
context,
|
|
87
|
+
showPrompt,
|
|
88
|
+
plainOutput
|
|
89
|
+
};
|
|
90
|
+
printCommandPreview(selection, messages);
|
|
91
|
+
return selection;
|
|
92
|
+
}
|
|
24
93
|
const agentA = await askAgent(rl, choices, messages.new.agentA, config.defaults?.agentA, messages);
|
|
25
94
|
if (!agentA)
|
|
26
95
|
return undefined;
|
|
@@ -40,6 +109,7 @@ export async function runNewWizard(config, messages) {
|
|
|
40
109
|
agentA,
|
|
41
110
|
agentB,
|
|
42
111
|
topic,
|
|
112
|
+
mode,
|
|
43
113
|
files: [],
|
|
44
114
|
context: [],
|
|
45
115
|
showPrompt: false,
|
|
@@ -80,6 +150,7 @@ export async function runNewWizard(config, messages) {
|
|
|
80
150
|
agentA,
|
|
81
151
|
agentB,
|
|
82
152
|
topic,
|
|
153
|
+
mode,
|
|
83
154
|
modelA,
|
|
84
155
|
modelB,
|
|
85
156
|
turns,
|
|
@@ -98,6 +169,35 @@ export async function runNewWizard(config, messages) {
|
|
|
98
169
|
rl.close();
|
|
99
170
|
}
|
|
100
171
|
}
|
|
172
|
+
async function askMode(rl, defaultMode, messages) {
|
|
173
|
+
const choices = [
|
|
174
|
+
{ value: "debate", label: messages.new.modeDebate },
|
|
175
|
+
{ value: "ask", label: messages.new.modeAsk }
|
|
176
|
+
];
|
|
177
|
+
const fallback = choices.find((choice) => choice.value === defaultMode)?.value ?? "debate";
|
|
178
|
+
console.log(messages.new.mode);
|
|
179
|
+
choices.forEach((choice, index) => {
|
|
180
|
+
const marker = choice.value === fallback ? "(*)" : " ";
|
|
181
|
+
console.log(` ${index + 1}) ${marker} ${choice.label}`);
|
|
182
|
+
});
|
|
183
|
+
while (true) {
|
|
184
|
+
const answer = await rl.question(`${messages.new.mode} [${fallback}]: `);
|
|
185
|
+
const value = answer.trim().toLowerCase();
|
|
186
|
+
if (isQuit(value))
|
|
187
|
+
return undefined;
|
|
188
|
+
if (!value)
|
|
189
|
+
return fallback;
|
|
190
|
+
const number = Number(value);
|
|
191
|
+
if (Number.isInteger(number) && number >= 1 && number <= choices.length) {
|
|
192
|
+
return choices[number - 1]?.value;
|
|
193
|
+
}
|
|
194
|
+
if (value === "debate" || value === "débat" || value === "debat")
|
|
195
|
+
return "debate";
|
|
196
|
+
if (value === "ask" || value === "demande" || value === "question")
|
|
197
|
+
return "ask";
|
|
198
|
+
console.log(messages.new.invalidModeChoice);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
101
201
|
async function createQuestioner() {
|
|
102
202
|
if (input.isTTY) {
|
|
103
203
|
return createInterface({ input, output });
|
|
@@ -171,6 +271,44 @@ async function askAgent(rl, choices, label, defaultName, messages) {
|
|
|
171
271
|
console.log(messages.new.invalidAgentChoice);
|
|
172
272
|
}
|
|
173
273
|
}
|
|
274
|
+
async function askAgentList(rl, choices, defaultNames, messages) {
|
|
275
|
+
const availableNames = choices.map((choice) => choice.name);
|
|
276
|
+
const fallback = uniqueNames(defaultNames.filter((name) => availableNames.includes(name))).slice(0, 4);
|
|
277
|
+
const defaultSelection = fallback.length > 0 ? fallback : availableNames.slice(0, Math.min(2, availableNames.length));
|
|
278
|
+
console.log(messages.new.askAgents);
|
|
279
|
+
choices.forEach((choice, index) => {
|
|
280
|
+
const marker = defaultSelection.includes(choice.name) ? "(*)" : " ";
|
|
281
|
+
console.log(` ${index + 1}) ${marker} ${choice.name} - ${choice.status}`);
|
|
282
|
+
});
|
|
283
|
+
while (true) {
|
|
284
|
+
const answer = await rl.question(`${messages.new.askAgentsPrompt(defaultSelection.join(" "))}: `);
|
|
285
|
+
const value = answer.trim();
|
|
286
|
+
if (isQuit(value))
|
|
287
|
+
return undefined;
|
|
288
|
+
if (!value)
|
|
289
|
+
return defaultSelection;
|
|
290
|
+
const tokens = value.split(/[,\s]+/).map((token) => token.trim()).filter(Boolean);
|
|
291
|
+
const resolved = uniqueNames(tokens.map((token) => {
|
|
292
|
+
const number = Number(token);
|
|
293
|
+
if (Number.isInteger(number) && number >= 1 && number <= choices.length) {
|
|
294
|
+
return choices[number - 1]?.name;
|
|
295
|
+
}
|
|
296
|
+
return token;
|
|
297
|
+
}).filter((name) => Boolean(name)));
|
|
298
|
+
if (resolved.length === 0) {
|
|
299
|
+
console.log(messages.new.invalidAskAgentsChoice);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (resolved.length > 4) {
|
|
303
|
+
console.log(messages.common.tooManyAskAgents(4));
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (resolved.every((name) => availableNames.includes(name))) {
|
|
307
|
+
return resolved;
|
|
308
|
+
}
|
|
309
|
+
console.log(messages.new.invalidAskAgentsChoice);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
174
312
|
async function askRequiredText(rl, label, messages) {
|
|
175
313
|
while (true) {
|
|
176
314
|
const answer = await rl.question(`${label}: `);
|
|
@@ -230,6 +368,12 @@ function splitPaths(value) {
|
|
|
230
368
|
.map((entry) => entry.trim())
|
|
231
369
|
.filter(Boolean) ?? [];
|
|
232
370
|
}
|
|
371
|
+
function defaultAskAgents(config) {
|
|
372
|
+
return [config.defaults?.agentA, config.defaults?.agentB].filter((agent) => Boolean(agent));
|
|
373
|
+
}
|
|
374
|
+
function uniqueNames(names) {
|
|
375
|
+
return names.filter((name, index) => names.indexOf(name) === index);
|
|
376
|
+
}
|
|
233
377
|
function isQuit(value) {
|
|
234
378
|
return ["q", "quit", "exit"].includes(value.toLowerCase());
|
|
235
379
|
}
|
|
@@ -246,13 +390,23 @@ function printCommandPreview(selection, messages) {
|
|
|
246
390
|
}
|
|
247
391
|
function buildExplicitCommand(selection) {
|
|
248
392
|
const args = ["palabre"];
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
393
|
+
if (selection.mode === "ask") {
|
|
394
|
+
args.push("ask", quoteShellArg(selection.topic));
|
|
395
|
+
const askAgents = selection.askAgents && selection.askAgents.length > 0 ? selection.askAgents : [selection.agentA, selection.agentB];
|
|
396
|
+
args.push("--agents", ...askAgents);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
args.push("--agent-a", selection.agentA);
|
|
400
|
+
args.push("--agent-b", selection.agentB);
|
|
401
|
+
args.push(quoteShellArg(selection.topic));
|
|
402
|
+
}
|
|
252
403
|
appendOptionalArgs(args, selection);
|
|
253
404
|
return args.join(" ");
|
|
254
405
|
}
|
|
255
406
|
function buildShortCommand(selection) {
|
|
407
|
+
if (selection.mode === "ask") {
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
256
410
|
const presetName = findPresetNameForPair(selection.agentA, selection.agentB);
|
|
257
411
|
if (!presetName) {
|
|
258
412
|
return undefined;
|
|
@@ -281,7 +435,7 @@ function appendOptionalArgs(args, selection) {
|
|
|
281
435
|
if (selection.showPrompt)
|
|
282
436
|
args.push("--show-prompt");
|
|
283
437
|
if (selection.plainOutput)
|
|
284
|
-
args.push("--
|
|
438
|
+
args.push("--terminal");
|
|
285
439
|
}
|
|
286
440
|
function quoteShellArg(value) {
|
|
287
441
|
if (/^[A-Za-z0-9._/:\\-]+$/.test(value)) {
|