palabre 0.2.0 → 0.5.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/config.js +33 -7
- package/dist/configWizard.js +45 -40
- package/dist/context.js +16 -14
- package/dist/doctor.js +139 -135
- package/dist/errors.js +4 -31
- package/dist/i18n.js +30 -0
- package/dist/index.js +290 -245
- package/dist/limits.js +11 -10
- package/dist/messages/adapter-errors.js +36 -0
- package/dist/messages/agents.js +38 -0
- package/dist/messages/common.js +28 -0
- package/dist/messages/config.js +88 -0
- package/dist/messages/context.js +24 -0
- package/dist/messages/doctor.js +126 -0
- package/dist/messages/help.js +280 -0
- package/dist/messages/index.js +38 -0
- package/dist/messages/init.js +30 -0
- package/dist/messages/limits.js +12 -0
- package/dist/messages/new.js +66 -0
- package/dist/messages/orchestrator.js +14 -0
- package/dist/messages/output.js +64 -0
- package/dist/messages/presets.js +26 -0
- package/dist/messages/preview.js +22 -0
- package/dist/messages/prompt.js +102 -0
- package/dist/messages/renderers.js +38 -0
- package/dist/messages/update.js +40 -0
- package/dist/new.js +42 -42
- package/dist/orchestrator.js +23 -18
- package/dist/output.js +34 -33
- package/dist/presets.js +78 -2
- package/dist/prompt.js +43 -58
- package/dist/renderers/console.js +32 -26
- package/dist/update.js +10 -21
- package/package.json +1 -1
- package/palabre.config.example.json +1 -0
package/dist/doctor.js
CHANGED
|
@@ -1,204 +1,222 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { stat } from "node:fs/promises";
|
|
3
|
-
import { configExists, loadConfig, resolveDefaultConfigPath } from "./config.js";
|
|
3
|
+
import { configExists, loadConfig, resolveDefaultConfigPath, resolveOutputDir } from "./config.js";
|
|
4
4
|
import { discoverLocalTools } from "./discovery.js";
|
|
5
|
+
import { createTranslator, resolveLanguage } from "./i18n.js";
|
|
5
6
|
import { DEFAULT_TURNS, MAX_TURNS } from "./limits.js";
|
|
6
7
|
/**
|
|
7
8
|
* Exécute le diagnostic complet : config, outils locaux et agents.
|
|
8
9
|
* Retourne toujours un résultat (pas de throw) ; les erreurs de config sont reportées comme lignes `error`.
|
|
9
10
|
*/
|
|
10
|
-
export async function runDoctor(explicitConfigPath, plain = false) {
|
|
11
|
+
export async function runDoctor(explicitConfigPath, plain = false, explicitLanguage) {
|
|
11
12
|
const lines = [];
|
|
12
13
|
const configPath = explicitConfigPath ?? await resolveDefaultConfigPath();
|
|
13
14
|
const hasConfig = await configExists(configPath);
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const config = hasConfig ? await loadConfigSafely(configPath) : undefined;
|
|
16
|
+
const language = resolveLanguage({
|
|
17
|
+
explicitLanguage,
|
|
18
|
+
configLanguage: config?.language
|
|
19
|
+
});
|
|
20
|
+
const t = createTranslator(language);
|
|
21
|
+
lines.push(info(t.doctor.title, "title"));
|
|
22
|
+
lines.push(info(t.doctor.currentDirectory(process.cwd()), "cwd"));
|
|
16
23
|
lines.push(hasConfig
|
|
17
|
-
? ok(
|
|
18
|
-
: error(
|
|
24
|
+
? ok(t.doctor.configFound(configPath))
|
|
25
|
+
: error(t.doctor.configMissing(configPath)));
|
|
19
26
|
if (!hasConfig) {
|
|
20
|
-
lines.push(info(
|
|
21
|
-
return render(lines, plain);
|
|
27
|
+
lines.push(info(t.doctor.noConfigAction));
|
|
28
|
+
return render(lines, plain, t);
|
|
22
29
|
}
|
|
23
|
-
const config = await loadConfigSafely(configPath, lines);
|
|
24
30
|
if (!config) {
|
|
25
|
-
|
|
31
|
+
const loadError = await getConfigLoadError(configPath);
|
|
32
|
+
lines.push(error(t.doctor.configUnreadable(loadError)));
|
|
33
|
+
lines.push(info(t.doctor.configUnreadableAction));
|
|
34
|
+
return render(lines, plain, t);
|
|
26
35
|
}
|
|
27
|
-
|
|
36
|
+
lines.push(ok(t.doctor.configReadable));
|
|
37
|
+
lines.push(ok(t.doctor.interfaceLanguage(language)));
|
|
38
|
+
await inspectConfig(config, lines, t);
|
|
28
39
|
const discovery = await discoverLocalTools();
|
|
29
|
-
lines.push(info(
|
|
30
|
-
lines.push(formatCommand("Codex CLI", discovery.codex.available, discovery.codex.command, discovery.codex.path));
|
|
31
|
-
lines.push(formatCommand("Claude CLI", discovery.claude.available, discovery.claude.command, discovery.claude.path));
|
|
32
|
-
lines.push(formatCommand("Gemini CLI", discovery.gemini.available, discovery.gemini.command, discovery.gemini.path));
|
|
33
|
-
lines.push(formatCommand("OpenCode CLI", discovery.opencode.available, discovery.opencode.command, discovery.opencode.path));
|
|
40
|
+
lines.push(info(t.doctor.localTools, "tools"));
|
|
41
|
+
lines.push(formatCommand("Codex CLI", discovery.codex.available, discovery.codex.command, discovery.codex.path, t));
|
|
42
|
+
lines.push(formatCommand("Claude CLI", discovery.claude.available, discovery.claude.command, discovery.claude.path, t));
|
|
43
|
+
lines.push(formatCommand("Gemini CLI", discovery.gemini.available, discovery.gemini.command, discovery.gemini.path, t));
|
|
44
|
+
lines.push(formatCommand("OpenCode CLI", discovery.opencode.available, discovery.opencode.command, discovery.opencode.path, t));
|
|
34
45
|
lines.push(discovery.ollama.available
|
|
35
|
-
? ok(
|
|
46
|
+
? ok(t.doctor.ollamaReachable(discovery.ollama.baseUrl, discovery.ollama.models.length))
|
|
36
47
|
: warn(discovery.ollama.commandAvailable
|
|
37
|
-
?
|
|
38
|
-
:
|
|
39
|
-
inspectDetectedMissingAgents(config, discovery, lines);
|
|
40
|
-
inspectAgents(config, discovery, lines);
|
|
41
|
-
return render(lines, plain);
|
|
48
|
+
? t.doctor.ollamaInstalledNoApi(discovery.ollama.baseUrl, formatErrorSuffix(discovery.ollama.error))
|
|
49
|
+
: t.doctor.ollamaMissingNoApi(discovery.ollama.baseUrl, formatErrorSuffix(discovery.ollama.error))));
|
|
50
|
+
inspectDetectedMissingAgents(config, discovery, lines, t);
|
|
51
|
+
inspectAgents(config, discovery, lines, t);
|
|
52
|
+
return render(lines, plain, t);
|
|
42
53
|
}
|
|
43
|
-
async function loadConfigSafely(configPath
|
|
54
|
+
async function loadConfigSafely(configPath) {
|
|
44
55
|
try {
|
|
45
|
-
|
|
46
|
-
lines.push(ok("Config JSON lisible."));
|
|
47
|
-
return config;
|
|
56
|
+
return await loadConfig(configPath);
|
|
48
57
|
}
|
|
49
|
-
catch
|
|
50
|
-
const message = loadError instanceof Error ? loadError.message : String(loadError);
|
|
51
|
-
lines.push(error(`Config illisible: ${message}`));
|
|
52
|
-
lines.push(info("Action: corrige le JSON ou relance `palabre init --config <path>` vers un nouveau fichier."));
|
|
58
|
+
catch {
|
|
53
59
|
return undefined;
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
|
-
async function
|
|
62
|
+
async function getConfigLoadError(configPath) {
|
|
63
|
+
try {
|
|
64
|
+
await loadConfig(configPath);
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
catch (loadError) {
|
|
68
|
+
return loadError instanceof Error ? loadError.message : String(loadError);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function inspectConfig(config, lines, t) {
|
|
57
72
|
const agentNames = Object.keys(config.agents ?? {});
|
|
58
73
|
if (agentNames.length === 0) {
|
|
59
|
-
lines.push(error(
|
|
74
|
+
lines.push(error(t.doctor.noAgents));
|
|
60
75
|
}
|
|
61
76
|
else if (agentNames.length === 1) {
|
|
62
|
-
lines.push(warn(
|
|
77
|
+
lines.push(warn(t.doctor.oneAgent(agentNames[0])));
|
|
63
78
|
}
|
|
64
79
|
else {
|
|
65
|
-
lines.push(ok(
|
|
80
|
+
lines.push(ok(t.doctor.agentCount(agentNames.length, agentNames.join(", "))));
|
|
66
81
|
}
|
|
67
|
-
inspectDefaultAgent("defaults.agentA", config.defaults?.agentA, config, lines);
|
|
68
|
-
inspectDefaultAgent("defaults.agentB", config.defaults?.agentB, config, lines);
|
|
69
|
-
inspectDefaultPair(config, lines);
|
|
70
|
-
inspectDefaultTurns(config.defaults?.turns, lines);
|
|
82
|
+
inspectDefaultAgent("defaults.agentA", config.defaults?.agentA, config, lines, t);
|
|
83
|
+
inspectDefaultAgent("defaults.agentB", config.defaults?.agentB, config, lines, t);
|
|
84
|
+
inspectDefaultPair(config, lines, t);
|
|
85
|
+
inspectDefaultTurns(config.defaults?.turns, lines, t);
|
|
71
86
|
if (config.defaults?.summaryAgent) {
|
|
72
|
-
inspectDefaultAgent("defaults.summaryAgent", config.defaults.summaryAgent, config, lines);
|
|
87
|
+
inspectDefaultAgent("defaults.summaryAgent", config.defaults.summaryAgent, config, lines, t);
|
|
73
88
|
}
|
|
74
89
|
else {
|
|
75
|
-
lines.push(warn(
|
|
90
|
+
lines.push(warn(t.doctor.summaryAgentMissing));
|
|
76
91
|
}
|
|
77
|
-
await inspectOutputDir(config.outputDir, lines);
|
|
92
|
+
await inspectOutputDir(config.outputDir, lines, t);
|
|
78
93
|
}
|
|
79
|
-
function inspectDefaultAgent(label, agentName, config, lines) {
|
|
94
|
+
function inspectDefaultAgent(label, agentName, config, lines, t) {
|
|
80
95
|
if (!agentName) {
|
|
81
|
-
lines.push(warn(
|
|
96
|
+
lines.push(warn(t.doctor.defaultAgentMissing(label)));
|
|
82
97
|
return;
|
|
83
98
|
}
|
|
84
99
|
if (!config.agents[agentName]) {
|
|
85
|
-
lines.push(error(
|
|
100
|
+
lines.push(error(t.doctor.defaultAgentUnknown(label, agentName)));
|
|
86
101
|
return;
|
|
87
102
|
}
|
|
88
|
-
lines.push(ok(
|
|
103
|
+
lines.push(ok(t.doctor.defaultAgentOk(label, agentName)));
|
|
89
104
|
}
|
|
90
|
-
function inspectDefaultPair(config, lines) {
|
|
105
|
+
function inspectDefaultPair(config, lines, t) {
|
|
91
106
|
const { agentA, agentB } = config.defaults ?? {};
|
|
92
107
|
if (!agentA || !agentB) {
|
|
93
|
-
lines.push(warn(
|
|
108
|
+
lines.push(warn(t.doctor.defaultPairIncomplete));
|
|
94
109
|
return;
|
|
95
110
|
}
|
|
96
111
|
if (agentA === agentB) {
|
|
97
|
-
lines.push(warn(
|
|
112
|
+
lines.push(warn(t.doctor.sameDefaultAgent(agentA)));
|
|
98
113
|
}
|
|
99
114
|
}
|
|
100
|
-
function inspectDefaultTurns(turns, lines) {
|
|
115
|
+
function inspectDefaultTurns(turns, lines, t) {
|
|
101
116
|
const value = turns ?? DEFAULT_TURNS;
|
|
102
117
|
if (turns === undefined) {
|
|
103
|
-
lines.push(info(
|
|
118
|
+
lines.push(info(t.doctor.defaultTurnsMissing(DEFAULT_TURNS)));
|
|
104
119
|
return;
|
|
105
120
|
}
|
|
106
121
|
if (!Number.isInteger(value) || value < 1 || value > MAX_TURNS) {
|
|
107
|
-
lines.push(error(
|
|
122
|
+
lines.push(error(t.doctor.defaultTurnsInvalid(String(turns), MAX_TURNS)));
|
|
108
123
|
return;
|
|
109
124
|
}
|
|
110
|
-
lines.push(ok(
|
|
125
|
+
lines.push(ok(t.doctor.defaultTurnsOk(value)));
|
|
111
126
|
}
|
|
112
|
-
async function inspectOutputDir(outputDir, lines) {
|
|
113
|
-
const
|
|
127
|
+
async function inspectOutputDir(outputDir, lines, t) {
|
|
128
|
+
const effectiveOutputDir = resolveOutputDir(outputDir);
|
|
129
|
+
const resolved = path.resolve(effectiveOutputDir);
|
|
114
130
|
if (!outputDir) {
|
|
115
|
-
lines.push(info(
|
|
116
|
-
|
|
131
|
+
lines.push(info(t.doctor.outputDirMissing(resolved)));
|
|
132
|
+
}
|
|
133
|
+
else if (outputDir.trim() === ".") {
|
|
134
|
+
lines.push(info(t.doctor.outputDirLegacy(resolved)));
|
|
117
135
|
}
|
|
118
136
|
try {
|
|
119
137
|
const stats = await stat(resolved);
|
|
120
138
|
if (!stats.isDirectory()) {
|
|
121
|
-
lines.push(error(
|
|
139
|
+
lines.push(error(t.doctor.outputDirIsFile(resolved)));
|
|
122
140
|
return;
|
|
123
141
|
}
|
|
124
|
-
lines.push(ok(
|
|
142
|
+
lines.push(ok(t.doctor.outputDirConfigured(resolved)));
|
|
125
143
|
}
|
|
126
144
|
catch {
|
|
127
|
-
lines.push(warn(
|
|
145
|
+
lines.push(warn(t.doctor.outputDirWillCreate(resolved)));
|
|
128
146
|
}
|
|
129
147
|
}
|
|
130
|
-
function inspectDetectedMissingAgents(config, discovery, lines) {
|
|
148
|
+
function inspectDetectedMissingAgents(config, discovery, lines, t) {
|
|
131
149
|
const missing = detectedAgentNames(discovery).filter((name) => !config.agents[name]);
|
|
132
150
|
if (missing.length === 0) {
|
|
133
151
|
return;
|
|
134
152
|
}
|
|
135
|
-
lines.push(warn(
|
|
153
|
+
lines.push(warn(t.doctor.detectedMissing(missing.join(", "))));
|
|
136
154
|
}
|
|
137
|
-
function inspectAgents(config, discovery, lines) {
|
|
138
|
-
lines.push(info(
|
|
155
|
+
function inspectAgents(config, discovery, lines, t) {
|
|
156
|
+
lines.push(info(t.doctor.configuredAgents, "agents"));
|
|
139
157
|
for (const [name, agent] of Object.entries(config.agents)) {
|
|
140
|
-
inspectAgentShape(name, agent, lines);
|
|
158
|
+
inspectAgentShape(name, agent, lines, t);
|
|
141
159
|
if (agent.type === "cli") {
|
|
142
|
-
inspectCliAgent(name, agent, discovery, lines);
|
|
160
|
+
inspectCliAgent(name, agent, discovery, lines, t);
|
|
143
161
|
continue;
|
|
144
162
|
}
|
|
145
|
-
inspectOllamaAgent(name, agent, discovery, lines);
|
|
163
|
+
inspectOllamaAgent(name, agent, discovery, lines, t);
|
|
146
164
|
}
|
|
147
165
|
}
|
|
148
|
-
function inspectAgentShape(name, agent, lines) {
|
|
166
|
+
function inspectAgentShape(name, agent, lines, t) {
|
|
149
167
|
if (!agent.role) {
|
|
150
|
-
lines.push(error(
|
|
168
|
+
lines.push(error(t.doctor.roleMissing(name)));
|
|
151
169
|
}
|
|
152
170
|
if (agent.type === "cli") {
|
|
153
171
|
if (!agent.command || !agent.command.trim()) {
|
|
154
|
-
lines.push(error(
|
|
172
|
+
lines.push(error(t.doctor.cliCommandMissing(name)));
|
|
155
173
|
}
|
|
156
174
|
if (agent.promptMode && !["stdin", "argument"].includes(agent.promptMode)) {
|
|
157
|
-
lines.push(error(
|
|
175
|
+
lines.push(error(t.doctor.promptModeInvalid(name, agent.promptMode)));
|
|
158
176
|
}
|
|
159
177
|
if (agent.timeoutMs !== undefined && (!Number.isFinite(agent.timeoutMs) || agent.timeoutMs <= 0)) {
|
|
160
|
-
lines.push(error(
|
|
178
|
+
lines.push(error(t.doctor.positiveTimeout(name, "timeoutMs")));
|
|
161
179
|
}
|
|
162
180
|
if (agent.idleTimeoutMs !== undefined && (!Number.isFinite(agent.idleTimeoutMs) || agent.idleTimeoutMs <= 0)) {
|
|
163
|
-
lines.push(error(
|
|
181
|
+
lines.push(error(t.doctor.positiveTimeout(name, "idleTimeoutMs")));
|
|
164
182
|
}
|
|
165
183
|
return;
|
|
166
184
|
}
|
|
167
185
|
if (!agent.model || !agent.model.trim()) {
|
|
168
|
-
lines.push(error(
|
|
186
|
+
lines.push(error(t.doctor.ollamaModelMissing(name)));
|
|
169
187
|
}
|
|
170
188
|
if (agent.baseUrl && !/^https?:\/\//.test(agent.baseUrl)) {
|
|
171
|
-
lines.push(error(
|
|
189
|
+
lines.push(error(t.doctor.ollamaBaseUrlInvalid(name, agent.baseUrl)));
|
|
172
190
|
}
|
|
173
191
|
if (agent.timeoutMs !== undefined && (!Number.isFinite(agent.timeoutMs) || agent.timeoutMs <= 0)) {
|
|
174
|
-
lines.push(error(
|
|
192
|
+
lines.push(error(t.doctor.positiveTimeout(name, "timeoutMs")));
|
|
175
193
|
}
|
|
176
194
|
}
|
|
177
|
-
function inspectCliAgent(name, agent, discovery, lines) {
|
|
195
|
+
function inspectCliAgent(name, agent, discovery, lines, t) {
|
|
178
196
|
const known = knownCliDetection(agent.command, discovery);
|
|
179
197
|
const prefix = `${name} [cli:${agent.role}] command=${agent.command}`;
|
|
180
198
|
if (!known) {
|
|
181
|
-
lines.push(info(
|
|
199
|
+
lines.push(info(t.doctor.customCommand(prefix)));
|
|
182
200
|
return;
|
|
183
201
|
}
|
|
184
202
|
lines.push(known.available
|
|
185
|
-
? ok(
|
|
186
|
-
: warn(
|
|
203
|
+
? ok(t.doctor.cliDetected(prefix, known.path ?? known.command))
|
|
204
|
+
: warn(t.doctor.cliMissing(prefix)));
|
|
187
205
|
}
|
|
188
|
-
function inspectOllamaAgent(name, agent, discovery, lines) {
|
|
206
|
+
function inspectOllamaAgent(name, agent, discovery, lines, t) {
|
|
189
207
|
const prefix = `${name} [ollama:${agent.role}] model=${agent.model}`;
|
|
190
208
|
if (!discovery.ollama.available) {
|
|
191
|
-
lines.push(warn(
|
|
209
|
+
lines.push(warn(t.doctor.ollamaNotVerifiable(prefix)));
|
|
192
210
|
return;
|
|
193
211
|
}
|
|
194
212
|
if (agent.validateModel === false) {
|
|
195
|
-
lines.push(info(
|
|
213
|
+
lines.push(info(t.doctor.ollamaValidateFalse(prefix)));
|
|
196
214
|
return;
|
|
197
215
|
}
|
|
198
216
|
const installed = discovery.ollama.models.includes(agent.model);
|
|
199
217
|
lines.push(installed
|
|
200
|
-
? ok(
|
|
201
|
-
: warn(
|
|
218
|
+
? ok(t.doctor.ollamaInstalled(prefix))
|
|
219
|
+
: warn(t.doctor.ollamaMissing(prefix, agent.model)));
|
|
202
220
|
}
|
|
203
221
|
function detectedAgentNames(discovery) {
|
|
204
222
|
return [
|
|
@@ -209,10 +227,10 @@ function detectedAgentNames(discovery) {
|
|
|
209
227
|
discovery.ollama.available ? "ollama-local" : undefined
|
|
210
228
|
].filter((name) => Boolean(name));
|
|
211
229
|
}
|
|
212
|
-
function formatCommand(label, available, command, resolvedPath) {
|
|
230
|
+
function formatCommand(label, available, command, resolvedPath, t) {
|
|
213
231
|
return available
|
|
214
|
-
? ok(
|
|
215
|
-
: warn(
|
|
232
|
+
? ok(t.doctor.commandDetected(label, resolvedPath ?? command))
|
|
233
|
+
: warn(t.doctor.commandMissing(label));
|
|
216
234
|
}
|
|
217
235
|
function knownCliDetection(command, discovery) {
|
|
218
236
|
const normalized = path.basename(command).toLowerCase().replace(/\.(exe|cmd|bat)$/i, "");
|
|
@@ -226,17 +244,17 @@ function knownCliDetection(command, discovery) {
|
|
|
226
244
|
return discovery.opencode;
|
|
227
245
|
return undefined;
|
|
228
246
|
}
|
|
229
|
-
function render(lines, plain) {
|
|
247
|
+
function render(lines, plain, t) {
|
|
230
248
|
const hasErrors = lines.some((line) => line.level === "error");
|
|
231
249
|
return {
|
|
232
250
|
ok: !hasErrors,
|
|
233
|
-
output: plain ? renderPlain(lines) : renderPretty(lines)
|
|
251
|
+
output: plain ? renderPlain(lines, t) : renderPretty(lines, t)
|
|
234
252
|
};
|
|
235
253
|
}
|
|
236
|
-
function renderPlain(lines) {
|
|
237
|
-
return lines.map(formatLine).join("\n");
|
|
254
|
+
function renderPlain(lines, t) {
|
|
255
|
+
return lines.map((line) => formatLine(line, t)).join("\n");
|
|
238
256
|
}
|
|
239
|
-
function renderPretty(lines) {
|
|
257
|
+
function renderPretty(lines, t) {
|
|
240
258
|
const configLines = [];
|
|
241
259
|
const toolLines = [];
|
|
242
260
|
const agentLines = [];
|
|
@@ -244,17 +262,17 @@ function renderPretty(lines) {
|
|
|
244
262
|
let current = "config";
|
|
245
263
|
let cwd = process.cwd();
|
|
246
264
|
for (const line of lines) {
|
|
247
|
-
if (line.
|
|
265
|
+
if (line.marker === "title")
|
|
248
266
|
continue;
|
|
249
|
-
if (line.
|
|
250
|
-
cwd = line.text.replace(
|
|
267
|
+
if (line.marker === "cwd") {
|
|
268
|
+
cwd = line.text.replace(/^.*?: /, "");
|
|
251
269
|
continue;
|
|
252
270
|
}
|
|
253
|
-
if (line.
|
|
271
|
+
if (line.marker === "tools") {
|
|
254
272
|
current = "tools";
|
|
255
273
|
continue;
|
|
256
274
|
}
|
|
257
|
-
if (line.
|
|
275
|
+
if (line.marker === "agents") {
|
|
258
276
|
current = "agents";
|
|
259
277
|
continue;
|
|
260
278
|
}
|
|
@@ -273,68 +291,54 @@ function renderPretty(lines) {
|
|
|
273
291
|
}
|
|
274
292
|
const errorCount = lines.filter((line) => line.level === "error").length;
|
|
275
293
|
const warnCount = lines.filter((line) => line.level === "warn").length;
|
|
276
|
-
const status = errorCount
|
|
277
|
-
? `${errorCount} erreur(s), ${warnCount} avertissement(s)`
|
|
278
|
-
: warnCount > 0 ? `${warnCount} avertissement(s)` : "OK";
|
|
294
|
+
const status = t.doctor.status(errorCount, warnCount);
|
|
279
295
|
return [
|
|
280
|
-
...renderDoctorHeader(status),
|
|
296
|
+
...renderDoctorHeader(status, t),
|
|
281
297
|
"",
|
|
282
|
-
...renderSection(
|
|
298
|
+
...renderSection(t.doctor.sections.configuration, [info(t.doctor.currentDirectory(cwd)), ...configLines], t),
|
|
283
299
|
"",
|
|
284
|
-
...renderSection(
|
|
300
|
+
...renderSection(t.doctor.sections.tools, toolLines, t),
|
|
285
301
|
"",
|
|
286
|
-
...renderSection(
|
|
287
|
-
...(actionLines.length > 0 ? ["", ...renderSection(
|
|
302
|
+
...renderSection(t.doctor.sections.agents, agentLines, t),
|
|
303
|
+
...(actionLines.length > 0 ? ["", ...renderSection(t.doctor.sections.check, actionLines, t)] : []),
|
|
288
304
|
""
|
|
289
305
|
].join("\n");
|
|
290
306
|
}
|
|
291
|
-
function renderDoctorHeader(status) {
|
|
292
|
-
const title =
|
|
307
|
+
function renderDoctorHeader(status, t) {
|
|
308
|
+
const title = t.doctor.title;
|
|
293
309
|
return [
|
|
294
310
|
`┌─ ${title} ${"─".repeat(Math.max(1, 58 - title.length))}`,
|
|
295
|
-
`│
|
|
311
|
+
`│ ${t.doctor.statusLabel}: ${status}`,
|
|
296
312
|
`└${"─".repeat(73)}`
|
|
297
313
|
];
|
|
298
314
|
}
|
|
299
|
-
function renderSection(title, lines) {
|
|
315
|
+
function renderSection(title, lines, t) {
|
|
300
316
|
if (lines.length === 0) {
|
|
301
|
-
return [title,
|
|
317
|
+
return [title, ` ${t.doctor.prettyLevelLabels.info} ${t.doctor.nothingToDisplay}`];
|
|
302
318
|
}
|
|
303
319
|
return [
|
|
304
320
|
title,
|
|
305
321
|
"─".repeat(Math.max(16, title.length + 8)),
|
|
306
|
-
...lines.map((line) => ` ${formatPrettyLine(line)}`)
|
|
322
|
+
...lines.map((line) => ` ${formatPrettyLine(line, t)}`)
|
|
307
323
|
];
|
|
308
324
|
}
|
|
309
|
-
function formatPrettyLine(line) {
|
|
310
|
-
|
|
311
|
-
ok: "OK ",
|
|
312
|
-
warn: "WARN ",
|
|
313
|
-
error: "ERREUR",
|
|
314
|
-
info: "INFO "
|
|
315
|
-
};
|
|
316
|
-
return `${labels[line.level]} ${line.text}`;
|
|
325
|
+
function formatPrettyLine(line, t) {
|
|
326
|
+
return `${t.doctor.prettyLevelLabels[line.level]} ${line.text}`;
|
|
317
327
|
}
|
|
318
|
-
function formatLine(line) {
|
|
319
|
-
|
|
320
|
-
ok: "OK",
|
|
321
|
-
warn: "WARN",
|
|
322
|
-
error: "ERREUR",
|
|
323
|
-
info: "INFO"
|
|
324
|
-
};
|
|
325
|
-
return `[${labels[line.level]}] ${line.text}`;
|
|
328
|
+
function formatLine(line, t) {
|
|
329
|
+
return `[${t.doctor.levelLabels[line.level]}] ${line.text}`;
|
|
326
330
|
}
|
|
327
|
-
function ok(text) {
|
|
328
|
-
return { level: "ok", text };
|
|
331
|
+
function ok(text, marker) {
|
|
332
|
+
return { level: "ok", text, marker };
|
|
329
333
|
}
|
|
330
|
-
function warn(text) {
|
|
331
|
-
return { level: "warn", text };
|
|
334
|
+
function warn(text, marker) {
|
|
335
|
+
return { level: "warn", text, marker };
|
|
332
336
|
}
|
|
333
|
-
function error(text) {
|
|
334
|
-
return { level: "error", text };
|
|
337
|
+
function error(text, marker) {
|
|
338
|
+
return { level: "error", text, marker };
|
|
335
339
|
}
|
|
336
|
-
function info(text) {
|
|
337
|
-
return { level: "info", text };
|
|
340
|
+
function info(text, marker) {
|
|
341
|
+
return { level: "info", text, marker };
|
|
338
342
|
}
|
|
339
343
|
function formatErrorSuffix(errorMessage) {
|
|
340
344
|
return errorMessage ? ` (${errorMessage})` : "";
|
package/dist/errors.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createTranslator } from "./i18n.js";
|
|
1
2
|
/**
|
|
2
3
|
* Erreur typée levée par les adapters.
|
|
3
4
|
* `kind` est stable et utilisé par l'orchestrateur pour classifier l'échec sans inspecter le message.
|
|
@@ -15,35 +16,7 @@ export class AdapterError extends Error {
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
/** Formate le message d'erreur avec une suggestion actionnable selon `error.kind`. */
|
|
18
|
-
export function formatAdapterError(error) {
|
|
19
|
-
const hint =
|
|
20
|
-
return hint ? `${error.message}\
|
|
21
|
-
}
|
|
22
|
-
function hintForFailure(kind) {
|
|
23
|
-
switch (kind) {
|
|
24
|
-
case "command-not-found":
|
|
25
|
-
return "Verifie que la CLI est installee, authentifiee et disponible dans le PATH.";
|
|
26
|
-
case "spawn-failed":
|
|
27
|
-
return "Sur Windows, essaye le wrapper .cmd ou active \"shell\": true dans la config agent.";
|
|
28
|
-
case "timeout":
|
|
29
|
-
return "Augmente timeoutMs ou teste la commande directement dans le terminal.";
|
|
30
|
-
case "idle-timeout":
|
|
31
|
-
return "Desactive idleTimeoutMs pour les CLIs IA qui restent silencieuses pendant la generation.";
|
|
32
|
-
case "empty-output":
|
|
33
|
-
return "Teste la commande en dehors de Palabre et verifie que le prompt est bien lu via stdin ou argument.";
|
|
34
|
-
case "usage-limit":
|
|
35
|
-
return "Attends la fenetre indiquee par la CLI, change de modele ou relance avec un autre agent/preset disponible.";
|
|
36
|
-
case "non-zero-exit":
|
|
37
|
-
return "Teste la commande directement, puis ajuste args, permissions, modele ou authentification de la CLI.";
|
|
38
|
-
case "model-unavailable":
|
|
39
|
-
return "Installe le modele Ollama ou relance avec --pull-models pour autoriser le telechargement.";
|
|
40
|
-
case "unsupported-model":
|
|
41
|
-
return "Verifie le nom du modele, ton abonnement, ou retire --model-a/--model-b/--summary-model pour laisser la CLI utiliser son modele par defaut.";
|
|
42
|
-
case "model-pull-failed":
|
|
43
|
-
return "Verifie le nom du modele, ta connexion et l'espace disque disponible.";
|
|
44
|
-
case "http-error":
|
|
45
|
-
return "Verifie que le service local est lance et que baseUrl est correct.";
|
|
46
|
-
default:
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
19
|
+
export function formatAdapterError(error, messages = createTranslator("fr")) {
|
|
20
|
+
const hint = messages.adapterErrors.hint(error.kind);
|
|
21
|
+
return hint ? `${error.message}\n${messages.adapterErrors.suggestionPrefix}: ${hint}` : error.message;
|
|
49
22
|
}
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createTranslator } from "./messages/index.js";
|
|
2
|
+
export const DEFAULT_LANGUAGE = "fr";
|
|
3
|
+
export const SUPPORTED_LANGUAGES = ["fr", "en"];
|
|
4
|
+
/**
|
|
5
|
+
* Valide une langue Palabre.
|
|
6
|
+
* Le contrat reste volontairement strict tant que l'interface ne supporte que
|
|
7
|
+
* le français et l'anglais.
|
|
8
|
+
*/
|
|
9
|
+
export function parseLanguage(value, source = "language") {
|
|
10
|
+
const normalized = value?.trim().toLowerCase();
|
|
11
|
+
if (!normalized) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
if (SUPPORTED_LANGUAGES.includes(normalized)) {
|
|
15
|
+
return normalized;
|
|
16
|
+
}
|
|
17
|
+
throw new Error(createTranslator(DEFAULT_LANGUAGE).common.invalidLanguage(source, value ?? "", SUPPORTED_LANGUAGES));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Résout la langue de l'interface selon la précédence :
|
|
21
|
+
* flag CLI explicite -> `PALABRE_LANGUAGE` -> config -> français.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveLanguage(options = {}) {
|
|
24
|
+
const env = options.env ?? process.env;
|
|
25
|
+
return parseLanguage(options.explicitLanguage, "--language")
|
|
26
|
+
?? parseLanguage(env.PALABRE_LANGUAGE, "PALABRE_LANGUAGE")
|
|
27
|
+
?? parseLanguage(options.configLanguage, "config.language")
|
|
28
|
+
?? DEFAULT_LANGUAGE;
|
|
29
|
+
}
|
|
30
|
+
export { createTranslator };
|