palabre 0.3.0 → 0.6.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 +6 -4
- package/dist/adapters/cli-pty.js +183 -0
- package/dist/adapters/cli.js +6 -6
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/terminal.js +13 -0
- package/dist/config.js +55 -8
- package/dist/configWizard.js +45 -40
- package/dist/context.js +16 -14
- package/dist/discovery.js +3 -1
- package/dist/doctor.js +147 -137
- package/dist/errors.js +4 -31
- package/dist/i18n.js +30 -0
- package/dist/index.js +275 -258
- 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 +46 -42
- package/dist/orchestrator.js +23 -18
- package/dist/output.js +34 -33
- package/dist/presets.js +122 -2
- package/dist/prompt.js +43 -58
- package/dist/renderers/console.js +33 -27
- package/dist/update.js +10 -21
- package/package.json +4 -1
- package/palabre.config.example.json +1 -0
package/dist/context.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { createTranslator } from "./i18n.js";
|
|
3
4
|
const MAX_FILE_BYTES = 64 * 1024;
|
|
4
5
|
const MAX_TOTAL_BYTES = 192 * 1024;
|
|
5
6
|
const DEFAULT_EXCLUDED_NAMES = new Set([
|
|
@@ -32,8 +33,8 @@ const TEXT_EXTENSIONS = new Set([
|
|
|
32
33
|
* Mode strict (`--files`) : charge uniquement les fichiers explicitement listés.
|
|
33
34
|
* Lève une erreur si un chemin est un dossier, un binaire, ou dépasse 64 KiB / 192 KiB au total.
|
|
34
35
|
*/
|
|
35
|
-
export async function loadProjectFiles(paths, cwd = process.cwd()) {
|
|
36
|
-
const result = await loadProjectInputs(paths, [], cwd);
|
|
36
|
+
export async function loadProjectFiles(paths, cwd = process.cwd(), messages = createTranslator("fr")) {
|
|
37
|
+
const result = await loadProjectInputs(paths, [], cwd, messages);
|
|
37
38
|
return result.files;
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
@@ -41,13 +42,14 @@ export async function loadProjectFiles(paths, cwd = process.cwd()) {
|
|
|
41
42
|
* Les fichiers explicites sont chargés en premier et comptent dans le budget total.
|
|
42
43
|
* Les chemins de contexte acceptent fichiers et dossiers ; les fichiers ignorés génèrent des warnings, pas des erreurs.
|
|
43
44
|
*/
|
|
44
|
-
export async function loadProjectInputs(filePaths, contextPaths, cwd = process.cwd()) {
|
|
45
|
+
export async function loadProjectInputs(filePaths, contextPaths, cwd = process.cwd(), messages = createTranslator("fr")) {
|
|
45
46
|
const state = {
|
|
46
47
|
files: [],
|
|
47
48
|
warnings: [],
|
|
48
49
|
seen: new Set(),
|
|
49
50
|
totalBytes: 0,
|
|
50
|
-
gitignoreRules: await loadGitignoreRules(cwd)
|
|
51
|
+
gitignoreRules: await loadGitignoreRules(cwd),
|
|
52
|
+
messages
|
|
51
53
|
};
|
|
52
54
|
await addExplicitFiles(filePaths, cwd, state);
|
|
53
55
|
await addContextPaths(contextPaths, cwd, state);
|
|
@@ -62,14 +64,14 @@ async function addExplicitFiles(paths, cwd, state) {
|
|
|
62
64
|
const absolutePath = path.resolve(cwd, inputPath);
|
|
63
65
|
const fileStat = await stat(absolutePath);
|
|
64
66
|
if (!fileStat.isFile()) {
|
|
65
|
-
throw new Error(
|
|
67
|
+
throw new Error(state.messages.context.explicitMustBeFile(inputPath));
|
|
66
68
|
}
|
|
67
69
|
if (fileStat.size > MAX_FILE_BYTES) {
|
|
68
|
-
throw new Error(
|
|
70
|
+
throw new Error(state.messages.context.explicitTooLarge(inputPath, fileStat.size, MAX_FILE_BYTES));
|
|
69
71
|
}
|
|
70
72
|
const content = await readFile(absolutePath, "utf8");
|
|
71
73
|
if (content.includes("\u0000")) {
|
|
72
|
-
throw new Error(
|
|
74
|
+
throw new Error(state.messages.context.explicitBinary(inputPath));
|
|
73
75
|
}
|
|
74
76
|
addFileToState(cwd, state, absolutePath, content, fileStat.size, "explicit");
|
|
75
77
|
}
|
|
@@ -84,7 +86,7 @@ async function addContextPaths(paths, cwd, state) {
|
|
|
84
86
|
continue;
|
|
85
87
|
}
|
|
86
88
|
if (!fileStat.isDirectory()) {
|
|
87
|
-
state.warnings.push(
|
|
89
|
+
state.warnings.push(state.messages.context.ignoredNotFileOrDirectory(inputPath));
|
|
88
90
|
continue;
|
|
89
91
|
}
|
|
90
92
|
await walkContextDirectory(absolutePath, cwd, state);
|
|
@@ -113,21 +115,21 @@ async function addContextFile(absolutePath, cwd, state) {
|
|
|
113
115
|
return;
|
|
114
116
|
}
|
|
115
117
|
if (!isLikelyTextFile(absolutePath)) {
|
|
116
|
-
state.warnings.push(
|
|
118
|
+
state.warnings.push(state.messages.context.ignoredNonTextExtension(relativePath));
|
|
117
119
|
return;
|
|
118
120
|
}
|
|
119
121
|
const fileStat = await stat(absolutePath);
|
|
120
122
|
if (fileStat.size > MAX_FILE_BYTES) {
|
|
121
|
-
state.warnings.push(
|
|
123
|
+
state.warnings.push(state.messages.context.ignoredTooLarge(relativePath, fileStat.size));
|
|
122
124
|
return;
|
|
123
125
|
}
|
|
124
126
|
if (state.totalBytes + fileStat.size > MAX_TOTAL_BYTES) {
|
|
125
|
-
state.warnings.push(
|
|
127
|
+
state.warnings.push(state.messages.context.ignoredTotalLimit(relativePath));
|
|
126
128
|
return;
|
|
127
129
|
}
|
|
128
130
|
const content = await readFile(absolutePath, "utf8");
|
|
129
131
|
if (content.includes("\u0000")) {
|
|
130
|
-
state.warnings.push(
|
|
132
|
+
state.warnings.push(state.messages.context.ignoredBinary(relativePath));
|
|
131
133
|
return;
|
|
132
134
|
}
|
|
133
135
|
addFileToState(cwd, state, absolutePath, content, fileStat.size, "context");
|
|
@@ -138,9 +140,9 @@ function addFileToState(cwd, state, absolutePath, content, sizeBytes, source) {
|
|
|
138
140
|
}
|
|
139
141
|
if (state.totalBytes + sizeBytes > MAX_TOTAL_BYTES) {
|
|
140
142
|
if (source === "explicit") {
|
|
141
|
-
throw new Error(
|
|
143
|
+
throw new Error(state.messages.context.explicitTotalTooLarge(state.totalBytes + sizeBytes, MAX_TOTAL_BYTES));
|
|
142
144
|
}
|
|
143
|
-
state.warnings.push(
|
|
145
|
+
state.warnings.push(state.messages.context.ignoredTotalLimit(normalizePath(path.relative(cwd, absolutePath))));
|
|
144
146
|
return;
|
|
145
147
|
}
|
|
146
148
|
state.seen.add(absolutePath);
|
package/dist/discovery.js
CHANGED
|
@@ -5,10 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
* Sur Windows, tente `claude.exe` avant `claude`.
|
|
6
6
|
*/
|
|
7
7
|
export async function discoverLocalTools() {
|
|
8
|
-
const [codex, claude, gemini, opencode, ollamaCommand] = await Promise.all([
|
|
8
|
+
const [codex, claude, gemini, antigravity, opencode, ollamaCommand] = await Promise.all([
|
|
9
9
|
detectCommand("codex"),
|
|
10
10
|
detectFirstCommand(process.platform === "win32" ? ["claude.exe", "claude"] : ["claude"]),
|
|
11
11
|
detectCommand("gemini"),
|
|
12
|
+
detectCommand("agy"),
|
|
12
13
|
detectCommand("opencode"),
|
|
13
14
|
detectCommand("ollama")
|
|
14
15
|
]);
|
|
@@ -17,6 +18,7 @@ export async function discoverLocalTools() {
|
|
|
17
18
|
codex,
|
|
18
19
|
claude,
|
|
19
20
|
gemini,
|
|
21
|
+
antigravity,
|
|
20
22
|
opencode,
|
|
21
23
|
ollama: {
|
|
22
24
|
...ollamaServer,
|
package/dist/doctor.js
CHANGED
|
@@ -1,218 +1,238 @@
|
|
|
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("
|
|
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("Antigravity CLI", discovery.antigravity.available, discovery.antigravity.command, discovery.antigravity.path, t));
|
|
45
|
+
lines.push(formatCommand("OpenCode CLI", discovery.opencode.available, discovery.opencode.command, discovery.opencode.path, t));
|
|
34
46
|
lines.push(discovery.ollama.available
|
|
35
|
-
? ok(
|
|
47
|
+
? ok(t.doctor.ollamaReachable(discovery.ollama.baseUrl, discovery.ollama.models.length))
|
|
36
48
|
: warn(discovery.ollama.commandAvailable
|
|
37
|
-
?
|
|
38
|
-
:
|
|
39
|
-
inspectDetectedMissingAgents(config, discovery, lines);
|
|
40
|
-
inspectAgents(config, discovery, lines);
|
|
41
|
-
return render(lines, plain);
|
|
49
|
+
? t.doctor.ollamaInstalledNoApi(discovery.ollama.baseUrl, formatErrorSuffix(discovery.ollama.error))
|
|
50
|
+
: t.doctor.ollamaMissingNoApi(discovery.ollama.baseUrl, formatErrorSuffix(discovery.ollama.error))));
|
|
51
|
+
inspectDetectedMissingAgents(config, discovery, lines, t);
|
|
52
|
+
inspectAgents(config, discovery, lines, t);
|
|
53
|
+
return render(lines, plain, t);
|
|
42
54
|
}
|
|
43
|
-
async function loadConfigSafely(configPath
|
|
55
|
+
async function loadConfigSafely(configPath) {
|
|
44
56
|
try {
|
|
45
|
-
|
|
46
|
-
lines.push(ok("Config JSON lisible."));
|
|
47
|
-
return config;
|
|
57
|
+
return await loadConfig(configPath);
|
|
48
58
|
}
|
|
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."));
|
|
59
|
+
catch {
|
|
53
60
|
return undefined;
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
|
-
async function
|
|
63
|
+
async function getConfigLoadError(configPath) {
|
|
64
|
+
try {
|
|
65
|
+
await loadConfig(configPath);
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
catch (loadError) {
|
|
69
|
+
return loadError instanceof Error ? loadError.message : String(loadError);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function inspectConfig(config, lines, t) {
|
|
57
73
|
const agentNames = Object.keys(config.agents ?? {});
|
|
58
74
|
if (agentNames.length === 0) {
|
|
59
|
-
lines.push(error(
|
|
75
|
+
lines.push(error(t.doctor.noAgents));
|
|
60
76
|
}
|
|
61
77
|
else if (agentNames.length === 1) {
|
|
62
|
-
lines.push(warn(
|
|
78
|
+
lines.push(warn(t.doctor.oneAgent(agentNames[0])));
|
|
63
79
|
}
|
|
64
80
|
else {
|
|
65
|
-
lines.push(ok(
|
|
81
|
+
lines.push(ok(t.doctor.agentCount(agentNames.length, agentNames.join(", "))));
|
|
66
82
|
}
|
|
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);
|
|
83
|
+
inspectDefaultAgent("defaults.agentA", config.defaults?.agentA, config, lines, t);
|
|
84
|
+
inspectDefaultAgent("defaults.agentB", config.defaults?.agentB, config, lines, t);
|
|
85
|
+
inspectDefaultPair(config, lines, t);
|
|
86
|
+
inspectDefaultTurns(config.defaults?.turns, lines, t);
|
|
71
87
|
if (config.defaults?.summaryAgent) {
|
|
72
|
-
inspectDefaultAgent("defaults.summaryAgent", config.defaults.summaryAgent, config, lines);
|
|
88
|
+
inspectDefaultAgent("defaults.summaryAgent", config.defaults.summaryAgent, config, lines, t);
|
|
73
89
|
}
|
|
74
90
|
else {
|
|
75
|
-
lines.push(warn(
|
|
91
|
+
lines.push(warn(t.doctor.summaryAgentMissing));
|
|
76
92
|
}
|
|
77
|
-
await inspectOutputDir(config.outputDir, lines);
|
|
93
|
+
await inspectOutputDir(config.outputDir, lines, t);
|
|
78
94
|
}
|
|
79
|
-
function inspectDefaultAgent(label, agentName, config, lines) {
|
|
95
|
+
function inspectDefaultAgent(label, agentName, config, lines, t) {
|
|
80
96
|
if (!agentName) {
|
|
81
|
-
lines.push(warn(
|
|
97
|
+
lines.push(warn(t.doctor.defaultAgentMissing(label)));
|
|
82
98
|
return;
|
|
83
99
|
}
|
|
84
100
|
if (!config.agents[agentName]) {
|
|
85
|
-
lines.push(error(
|
|
101
|
+
lines.push(error(t.doctor.defaultAgentUnknown(label, agentName)));
|
|
86
102
|
return;
|
|
87
103
|
}
|
|
88
|
-
lines.push(ok(
|
|
104
|
+
lines.push(ok(t.doctor.defaultAgentOk(label, agentName)));
|
|
89
105
|
}
|
|
90
|
-
function inspectDefaultPair(config, lines) {
|
|
106
|
+
function inspectDefaultPair(config, lines, t) {
|
|
91
107
|
const { agentA, agentB } = config.defaults ?? {};
|
|
92
108
|
if (!agentA || !agentB) {
|
|
93
|
-
lines.push(warn(
|
|
109
|
+
lines.push(warn(t.doctor.defaultPairIncomplete));
|
|
94
110
|
return;
|
|
95
111
|
}
|
|
96
112
|
if (agentA === agentB) {
|
|
97
|
-
lines.push(warn(
|
|
113
|
+
lines.push(warn(t.doctor.sameDefaultAgent(agentA)));
|
|
98
114
|
}
|
|
99
115
|
}
|
|
100
|
-
function inspectDefaultTurns(turns, lines) {
|
|
116
|
+
function inspectDefaultTurns(turns, lines, t) {
|
|
101
117
|
const value = turns ?? DEFAULT_TURNS;
|
|
102
118
|
if (turns === undefined) {
|
|
103
|
-
lines.push(info(
|
|
119
|
+
lines.push(info(t.doctor.defaultTurnsMissing(DEFAULT_TURNS)));
|
|
104
120
|
return;
|
|
105
121
|
}
|
|
106
122
|
if (!Number.isInteger(value) || value < 1 || value > MAX_TURNS) {
|
|
107
|
-
lines.push(error(
|
|
123
|
+
lines.push(error(t.doctor.defaultTurnsInvalid(String(turns), MAX_TURNS)));
|
|
108
124
|
return;
|
|
109
125
|
}
|
|
110
|
-
lines.push(ok(
|
|
126
|
+
lines.push(ok(t.doctor.defaultTurnsOk(value)));
|
|
111
127
|
}
|
|
112
|
-
async function inspectOutputDir(outputDir, lines) {
|
|
113
|
-
const
|
|
128
|
+
async function inspectOutputDir(outputDir, lines, t) {
|
|
129
|
+
const effectiveOutputDir = resolveOutputDir(outputDir);
|
|
130
|
+
const resolved = path.resolve(effectiveOutputDir);
|
|
114
131
|
if (!outputDir) {
|
|
115
|
-
lines.push(info(
|
|
116
|
-
|
|
132
|
+
lines.push(info(t.doctor.outputDirMissing(resolved)));
|
|
133
|
+
}
|
|
134
|
+
else if (outputDir.trim() === ".") {
|
|
135
|
+
lines.push(info(t.doctor.outputDirLegacy(resolved)));
|
|
117
136
|
}
|
|
118
137
|
try {
|
|
119
138
|
const stats = await stat(resolved);
|
|
120
139
|
if (!stats.isDirectory()) {
|
|
121
|
-
lines.push(error(
|
|
140
|
+
lines.push(error(t.doctor.outputDirIsFile(resolved)));
|
|
122
141
|
return;
|
|
123
142
|
}
|
|
124
|
-
lines.push(ok(
|
|
143
|
+
lines.push(ok(t.doctor.outputDirConfigured(resolved)));
|
|
125
144
|
}
|
|
126
145
|
catch {
|
|
127
|
-
lines.push(warn(
|
|
146
|
+
lines.push(warn(t.doctor.outputDirWillCreate(resolved)));
|
|
128
147
|
}
|
|
129
148
|
}
|
|
130
|
-
function inspectDetectedMissingAgents(config, discovery, lines) {
|
|
149
|
+
function inspectDetectedMissingAgents(config, discovery, lines, t) {
|
|
131
150
|
const missing = detectedAgentNames(discovery).filter((name) => !config.agents[name]);
|
|
132
151
|
if (missing.length === 0) {
|
|
133
152
|
return;
|
|
134
153
|
}
|
|
135
|
-
lines.push(warn(
|
|
154
|
+
lines.push(warn(t.doctor.detectedMissing(missing.join(", "))));
|
|
136
155
|
}
|
|
137
|
-
function inspectAgents(config, discovery, lines) {
|
|
138
|
-
lines.push(info(
|
|
156
|
+
function inspectAgents(config, discovery, lines, t) {
|
|
157
|
+
lines.push(info(t.doctor.configuredAgents, "agents"));
|
|
139
158
|
for (const [name, agent] of Object.entries(config.agents)) {
|
|
140
|
-
inspectAgentShape(name, agent, lines);
|
|
141
|
-
if (agent.type === "cli") {
|
|
142
|
-
inspectCliAgent(name, agent, discovery, lines);
|
|
159
|
+
inspectAgentShape(name, agent, lines, t);
|
|
160
|
+
if (agent.type === "cli" || agent.type === "cli-pty") {
|
|
161
|
+
inspectCliAgent(name, agent, discovery, lines, t);
|
|
143
162
|
continue;
|
|
144
163
|
}
|
|
145
|
-
inspectOllamaAgent(name, agent, discovery, lines);
|
|
164
|
+
inspectOllamaAgent(name, agent, discovery, lines, t);
|
|
146
165
|
}
|
|
147
166
|
}
|
|
148
|
-
function inspectAgentShape(name, agent, lines) {
|
|
167
|
+
function inspectAgentShape(name, agent, lines, t) {
|
|
149
168
|
if (!agent.role) {
|
|
150
|
-
lines.push(error(
|
|
169
|
+
lines.push(error(t.doctor.roleMissing(name)));
|
|
151
170
|
}
|
|
152
|
-
if (agent.type === "cli") {
|
|
171
|
+
if (agent.type === "cli" || agent.type === "cli-pty") {
|
|
153
172
|
if (!agent.command || !agent.command.trim()) {
|
|
154
|
-
lines.push(error(
|
|
173
|
+
lines.push(error(t.doctor.cliCommandMissing(name)));
|
|
155
174
|
}
|
|
156
175
|
if (agent.promptMode && !["stdin", "argument"].includes(agent.promptMode)) {
|
|
157
|
-
lines.push(error(
|
|
176
|
+
lines.push(error(t.doctor.promptModeInvalid(name, agent.promptMode)));
|
|
158
177
|
}
|
|
159
178
|
if (agent.timeoutMs !== undefined && (!Number.isFinite(agent.timeoutMs) || agent.timeoutMs <= 0)) {
|
|
160
|
-
lines.push(error(
|
|
179
|
+
lines.push(error(t.doctor.positiveTimeout(name, "timeoutMs")));
|
|
161
180
|
}
|
|
162
181
|
if (agent.idleTimeoutMs !== undefined && (!Number.isFinite(agent.idleTimeoutMs) || agent.idleTimeoutMs <= 0)) {
|
|
163
|
-
lines.push(error(
|
|
182
|
+
lines.push(error(t.doctor.positiveTimeout(name, "idleTimeoutMs")));
|
|
164
183
|
}
|
|
165
184
|
return;
|
|
166
185
|
}
|
|
167
186
|
if (!agent.model || !agent.model.trim()) {
|
|
168
|
-
lines.push(error(
|
|
187
|
+
lines.push(error(t.doctor.ollamaModelMissing(name)));
|
|
169
188
|
}
|
|
170
189
|
if (agent.baseUrl && !/^https?:\/\//.test(agent.baseUrl)) {
|
|
171
|
-
lines.push(error(
|
|
190
|
+
lines.push(error(t.doctor.ollamaBaseUrlInvalid(name, agent.baseUrl)));
|
|
172
191
|
}
|
|
173
192
|
if (agent.timeoutMs !== undefined && (!Number.isFinite(agent.timeoutMs) || agent.timeoutMs <= 0)) {
|
|
174
|
-
lines.push(error(
|
|
193
|
+
lines.push(error(t.doctor.positiveTimeout(name, "timeoutMs")));
|
|
175
194
|
}
|
|
176
195
|
}
|
|
177
|
-
function inspectCliAgent(name, agent, discovery, lines) {
|
|
196
|
+
function inspectCliAgent(name, agent, discovery, lines, t) {
|
|
178
197
|
const known = knownCliDetection(agent.command, discovery);
|
|
179
198
|
const prefix = `${name} [cli:${agent.role}] command=${agent.command}`;
|
|
180
199
|
if (!known) {
|
|
181
|
-
lines.push(info(
|
|
200
|
+
lines.push(info(t.doctor.customCommand(prefix)));
|
|
182
201
|
return;
|
|
183
202
|
}
|
|
184
203
|
lines.push(known.available
|
|
185
|
-
? ok(
|
|
186
|
-
: warn(
|
|
204
|
+
? ok(t.doctor.cliDetected(prefix, known.path ?? known.command))
|
|
205
|
+
: warn(t.doctor.cliMissing(prefix)));
|
|
187
206
|
}
|
|
188
|
-
function inspectOllamaAgent(name, agent, discovery, lines) {
|
|
207
|
+
function inspectOllamaAgent(name, agent, discovery, lines, t) {
|
|
189
208
|
const prefix = `${name} [ollama:${agent.role}] model=${agent.model}`;
|
|
190
209
|
if (!discovery.ollama.available) {
|
|
191
|
-
lines.push(warn(
|
|
210
|
+
lines.push(warn(t.doctor.ollamaNotVerifiable(prefix)));
|
|
192
211
|
return;
|
|
193
212
|
}
|
|
194
213
|
if (agent.validateModel === false) {
|
|
195
|
-
lines.push(info(
|
|
214
|
+
lines.push(info(t.doctor.ollamaValidateFalse(prefix)));
|
|
196
215
|
return;
|
|
197
216
|
}
|
|
198
217
|
const installed = discovery.ollama.models.includes(agent.model);
|
|
199
218
|
lines.push(installed
|
|
200
|
-
? ok(
|
|
201
|
-
: warn(
|
|
219
|
+
? ok(t.doctor.ollamaInstalled(prefix))
|
|
220
|
+
: warn(t.doctor.ollamaMissing(prefix, agent.model)));
|
|
202
221
|
}
|
|
203
222
|
function detectedAgentNames(discovery) {
|
|
204
223
|
return [
|
|
205
224
|
discovery.codex.available ? "codex" : undefined,
|
|
206
225
|
discovery.claude.available ? "claude" : undefined,
|
|
207
226
|
discovery.gemini.available ? "gemini" : undefined,
|
|
227
|
+
discovery.antigravity.available ? "antigravity" : undefined,
|
|
208
228
|
discovery.opencode.available ? "opencode" : undefined,
|
|
209
229
|
discovery.ollama.available ? "ollama-local" : undefined
|
|
210
230
|
].filter((name) => Boolean(name));
|
|
211
231
|
}
|
|
212
|
-
function formatCommand(label, available, command, resolvedPath) {
|
|
232
|
+
function formatCommand(label, available, command, resolvedPath, t) {
|
|
213
233
|
return available
|
|
214
|
-
? ok(
|
|
215
|
-
: warn(
|
|
234
|
+
? ok(t.doctor.commandDetected(label, resolvedPath ?? command))
|
|
235
|
+
: warn(t.doctor.commandMissing(label));
|
|
216
236
|
}
|
|
217
237
|
function knownCliDetection(command, discovery) {
|
|
218
238
|
const normalized = path.basename(command).toLowerCase().replace(/\.(exe|cmd|bat)$/i, "");
|
|
@@ -222,21 +242,25 @@ function knownCliDetection(command, discovery) {
|
|
|
222
242
|
return discovery.claude;
|
|
223
243
|
if (normalized === "gemini")
|
|
224
244
|
return discovery.gemini;
|
|
245
|
+
if (normalized === "agy")
|
|
246
|
+
return discovery.antigravity;
|
|
247
|
+
if (normalized === "antigravity")
|
|
248
|
+
return discovery.antigravity;
|
|
225
249
|
if (normalized === "opencode")
|
|
226
250
|
return discovery.opencode;
|
|
227
251
|
return undefined;
|
|
228
252
|
}
|
|
229
|
-
function render(lines, plain) {
|
|
253
|
+
function render(lines, plain, t) {
|
|
230
254
|
const hasErrors = lines.some((line) => line.level === "error");
|
|
231
255
|
return {
|
|
232
256
|
ok: !hasErrors,
|
|
233
|
-
output: plain ? renderPlain(lines) : renderPretty(lines)
|
|
257
|
+
output: plain ? renderPlain(lines, t) : renderPretty(lines, t)
|
|
234
258
|
};
|
|
235
259
|
}
|
|
236
|
-
function renderPlain(lines) {
|
|
237
|
-
return lines.map(formatLine).join("\n");
|
|
260
|
+
function renderPlain(lines, t) {
|
|
261
|
+
return lines.map((line) => formatLine(line, t)).join("\n");
|
|
238
262
|
}
|
|
239
|
-
function renderPretty(lines) {
|
|
263
|
+
function renderPretty(lines, t) {
|
|
240
264
|
const configLines = [];
|
|
241
265
|
const toolLines = [];
|
|
242
266
|
const agentLines = [];
|
|
@@ -244,17 +268,17 @@ function renderPretty(lines) {
|
|
|
244
268
|
let current = "config";
|
|
245
269
|
let cwd = process.cwd();
|
|
246
270
|
for (const line of lines) {
|
|
247
|
-
if (line.
|
|
271
|
+
if (line.marker === "title")
|
|
248
272
|
continue;
|
|
249
|
-
if (line.
|
|
250
|
-
cwd = line.text.replace(
|
|
273
|
+
if (line.marker === "cwd") {
|
|
274
|
+
cwd = line.text.replace(/^.*?: /, "");
|
|
251
275
|
continue;
|
|
252
276
|
}
|
|
253
|
-
if (line.
|
|
277
|
+
if (line.marker === "tools") {
|
|
254
278
|
current = "tools";
|
|
255
279
|
continue;
|
|
256
280
|
}
|
|
257
|
-
if (line.
|
|
281
|
+
if (line.marker === "agents") {
|
|
258
282
|
current = "agents";
|
|
259
283
|
continue;
|
|
260
284
|
}
|
|
@@ -273,68 +297,54 @@ function renderPretty(lines) {
|
|
|
273
297
|
}
|
|
274
298
|
const errorCount = lines.filter((line) => line.level === "error").length;
|
|
275
299
|
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";
|
|
300
|
+
const status = t.doctor.status(errorCount, warnCount);
|
|
279
301
|
return [
|
|
280
|
-
...renderDoctorHeader(status),
|
|
302
|
+
...renderDoctorHeader(status, t),
|
|
281
303
|
"",
|
|
282
|
-
...renderSection(
|
|
304
|
+
...renderSection(t.doctor.sections.configuration, [info(t.doctor.currentDirectory(cwd)), ...configLines], t),
|
|
283
305
|
"",
|
|
284
|
-
...renderSection(
|
|
306
|
+
...renderSection(t.doctor.sections.tools, toolLines, t),
|
|
285
307
|
"",
|
|
286
|
-
...renderSection(
|
|
287
|
-
...(actionLines.length > 0 ? ["", ...renderSection(
|
|
308
|
+
...renderSection(t.doctor.sections.agents, agentLines, t),
|
|
309
|
+
...(actionLines.length > 0 ? ["", ...renderSection(t.doctor.sections.check, actionLines, t)] : []),
|
|
288
310
|
""
|
|
289
311
|
].join("\n");
|
|
290
312
|
}
|
|
291
|
-
function renderDoctorHeader(status) {
|
|
292
|
-
const title =
|
|
313
|
+
function renderDoctorHeader(status, t) {
|
|
314
|
+
const title = t.doctor.title;
|
|
293
315
|
return [
|
|
294
316
|
`┌─ ${title} ${"─".repeat(Math.max(1, 58 - title.length))}`,
|
|
295
|
-
`│
|
|
317
|
+
`│ ${t.doctor.statusLabel}: ${status}`,
|
|
296
318
|
`└${"─".repeat(73)}`
|
|
297
319
|
];
|
|
298
320
|
}
|
|
299
|
-
function renderSection(title, lines) {
|
|
321
|
+
function renderSection(title, lines, t) {
|
|
300
322
|
if (lines.length === 0) {
|
|
301
|
-
return [title,
|
|
323
|
+
return [title, ` ${t.doctor.prettyLevelLabels.info} ${t.doctor.nothingToDisplay}`];
|
|
302
324
|
}
|
|
303
325
|
return [
|
|
304
326
|
title,
|
|
305
327
|
"─".repeat(Math.max(16, title.length + 8)),
|
|
306
|
-
...lines.map((line) => ` ${formatPrettyLine(line)}`)
|
|
328
|
+
...lines.map((line) => ` ${formatPrettyLine(line, t)}`)
|
|
307
329
|
];
|
|
308
330
|
}
|
|
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}`;
|
|
331
|
+
function formatPrettyLine(line, t) {
|
|
332
|
+
return `${t.doctor.prettyLevelLabels[line.level]} ${line.text}`;
|
|
317
333
|
}
|
|
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}`;
|
|
334
|
+
function formatLine(line, t) {
|
|
335
|
+
return `[${t.doctor.levelLabels[line.level]}] ${line.text}`;
|
|
326
336
|
}
|
|
327
|
-
function ok(text) {
|
|
328
|
-
return { level: "ok", text };
|
|
337
|
+
function ok(text, marker) {
|
|
338
|
+
return { level: "ok", text, marker };
|
|
329
339
|
}
|
|
330
|
-
function warn(text) {
|
|
331
|
-
return { level: "warn", text };
|
|
340
|
+
function warn(text, marker) {
|
|
341
|
+
return { level: "warn", text, marker };
|
|
332
342
|
}
|
|
333
|
-
function error(text) {
|
|
334
|
-
return { level: "error", text };
|
|
343
|
+
function error(text, marker) {
|
|
344
|
+
return { level: "error", text, marker };
|
|
335
345
|
}
|
|
336
|
-
function info(text) {
|
|
337
|
-
return { level: "info", text };
|
|
346
|
+
function info(text, marker) {
|
|
347
|
+
return { level: "info", text, marker };
|
|
338
348
|
}
|
|
339
349
|
function formatErrorSuffix(errorMessage) {
|
|
340
350
|
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
|
}
|