bibest-code 2.0.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/bibest-code.js +437 -0
- package/package.json +17 -0
- package/test.js +1 -0
- package/todo-api/.env.example +3 -0
- package/todo-api/README.md +136 -0
- package/todo-api/package-lock.json +1191 -0
- package/todo-api/package.json +22 -0
- package/todo-api/src/controllers/todoController.js +285 -0
- package/todo-api/src/data/todos.js +39 -0
- package/todo-api/src/middlewares/errorHandler.js +27 -0
- package/todo-api/src/middlewares/validate.js +92 -0
- package/todo-api/src/routes/todoRoutes.js +53 -0
- package/todo-api/src/server.js +86 -0
package/bibest-code.js
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ============================================================
|
|
3
|
+
// BIBEST CODE v2.0.0 — Agent CLI propulsé par Claude
|
|
4
|
+
// © BIBI ONDOUA — Tous droits réservés
|
|
5
|
+
// ============================================================
|
|
6
|
+
|
|
7
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
8
|
+
import * as readline from "readline";
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
import { exec } from "child_process";
|
|
12
|
+
import { promisify } from "util";
|
|
13
|
+
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
16
|
+
|
|
17
|
+
// ── Couleurs & styles ──────────────────────────────────────
|
|
18
|
+
const C = {
|
|
19
|
+
reset: "\x1b[0m",
|
|
20
|
+
bold: "\x1b[1m",
|
|
21
|
+
dim: "\x1b[2m",
|
|
22
|
+
italic: "\x1b[3m",
|
|
23
|
+
underline: "\x1b[4m",
|
|
24
|
+
cyan: "\x1b[36m",
|
|
25
|
+
cyanBright: "\x1b[96m",
|
|
26
|
+
green: "\x1b[32m",
|
|
27
|
+
greenBright: "\x1b[92m",
|
|
28
|
+
yellow: "\x1b[33m",
|
|
29
|
+
yellowBright: "\x1b[93m",
|
|
30
|
+
red: "\x1b[31m",
|
|
31
|
+
redBright: "\x1b[91m",
|
|
32
|
+
magenta: "\x1b[35m",
|
|
33
|
+
magentaBright: "\x1b[95m",
|
|
34
|
+
blue: "\x1b[34m",
|
|
35
|
+
blueBright: "\x1b[94m",
|
|
36
|
+
white: "\x1b[37m",
|
|
37
|
+
whiteBright: "\x1b[97m",
|
|
38
|
+
gray: "\x1b[90m",
|
|
39
|
+
bgCyan: "\x1b[46m",
|
|
40
|
+
bgBlue: "\x1b[44m",
|
|
41
|
+
bgGreen: "\x1b[42m",
|
|
42
|
+
bgRed: "\x1b[41m",
|
|
43
|
+
bgMagenta: "\x1b[45m",
|
|
44
|
+
bgYellow: "\x1b[43m",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const W = process.stdout.columns || 80;
|
|
48
|
+
const line = (char = "─", color = C.gray) => color + char.repeat(W) + C.reset;
|
|
49
|
+
const pad = (text, width) => text + " ".repeat(Math.max(0, width - stripAnsi(text).length));
|
|
50
|
+
const stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
51
|
+
const center = (text, color = "") => {
|
|
52
|
+
const len = stripAnsi(text).length;
|
|
53
|
+
const spaces = Math.max(0, Math.floor((W - len) / 2));
|
|
54
|
+
return " ".repeat(spaces) + color + text + C.reset;
|
|
55
|
+
};
|
|
56
|
+
const box = (lines, borderColor = C.cyanBright) => {
|
|
57
|
+
const maxLen = Math.max(...lines.map(l => stripAnsi(l).length));
|
|
58
|
+
const w = maxLen + 4;
|
|
59
|
+
console.log(borderColor + "╔" + "═".repeat(w) + "╗" + C.reset);
|
|
60
|
+
lines.forEach(l => {
|
|
61
|
+
const padded = pad(l, maxLen);
|
|
62
|
+
console.log(borderColor + "║ " + C.reset + padded + " " + borderColor + "║" + C.reset);
|
|
63
|
+
});
|
|
64
|
+
console.log(borderColor + "╚" + "═".repeat(w) + "╝" + C.reset);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ── État global ────────────────────────────────────────────
|
|
68
|
+
let sessionStats = { messages: 0, filesCreated: 0, filesRead: 0, commandsRun: 0, tokens: 0 };
|
|
69
|
+
let currentModel = "claude-sonnet-4-6";
|
|
70
|
+
let sessionStart = new Date();
|
|
71
|
+
const HISTORY_FILE = path.join(process.env.USERPROFILE || process.env.HOME || ".", ".bibest-code-history.json");
|
|
72
|
+
|
|
73
|
+
// ── Header principal ───────────────────────────────────────
|
|
74
|
+
function showHeader() {
|
|
75
|
+
console.clear();
|
|
76
|
+
console.log();
|
|
77
|
+
console.log(center("██████╗ ██╗██████╗ ███████╗███████╗████████╗", C.cyanBright + C.bold));
|
|
78
|
+
console.log(center("██╔══██╗██║██╔══██╗██╔════╝██╔════╝╚══██╔══╝", C.cyanBright + C.bold));
|
|
79
|
+
console.log(center("██████╔╝██║██████╔╝█████╗ ███████╗ ██║ ", C.cyan + C.bold));
|
|
80
|
+
console.log(center("██╔══██╗██║██╔══██╗██╔══╝ ╚════██║ ██║ ", C.blue + C.bold));
|
|
81
|
+
console.log(center("██████╔╝██║██████╔╝███████╗███████║ ██║ ", C.blueBright + C.bold));
|
|
82
|
+
console.log(center("╚═════╝ ╚═╝╚═════╝ ╚══════╝╚══════╝ ╚═╝ ", C.gray + C.bold));
|
|
83
|
+
console.log(center("██████╗ ██████╗ ██████╗ ███████╗", C.magentaBright + C.bold));
|
|
84
|
+
console.log(center("██╔════╝██╔═══██╗██╔══██╗██╔════╝", C.magenta + C.bold));
|
|
85
|
+
console.log(center("██║ ██║ ██║██║ ██║█████╗ ", C.magentaBright + C.bold));
|
|
86
|
+
console.log(center("██║ ██║ ██║██║ ██║██╔══╝ ", C.magenta + C.bold));
|
|
87
|
+
console.log(center("╚██████╗╚██████╔╝██████╔╝███████╗", C.magentaBright + C.bold));
|
|
88
|
+
console.log(center(" ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝", C.gray + C.bold));
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(line("═", C.cyanBright));
|
|
91
|
+
console.log(center("🤖 Agent CLI de développement propulsé par Claude AI 🚀", C.whiteBright + C.bold));
|
|
92
|
+
console.log(center("© BIBI ONDOUA — Tous droits réservés — v2.0.0", C.gray));
|
|
93
|
+
console.log(line("═", C.cyanBright));
|
|
94
|
+
console.log();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Barre de statut ────────────────────────────────────────
|
|
98
|
+
function showStatus() {
|
|
99
|
+
const now = new Date();
|
|
100
|
+
const uptime = Math.floor((now - sessionStart) / 1000);
|
|
101
|
+
const mins = Math.floor(uptime / 60).toString().padStart(2, "0");
|
|
102
|
+
const secs = (uptime % 60).toString().padStart(2, "0");
|
|
103
|
+
const cwd = process.cwd().replace(process.env.USERPROFILE || "", "~");
|
|
104
|
+
|
|
105
|
+
const items = [
|
|
106
|
+
`${C.greenBright}●${C.reset} ${C.gray}Connecté${C.reset}`,
|
|
107
|
+
`${C.cyan}⚡${C.reset} ${C.whiteBright}${currentModel}${C.reset}`,
|
|
108
|
+
`${C.yellow}📁${C.reset} ${C.gray}${cwd}${C.reset}`,
|
|
109
|
+
`${C.magenta}⏱${C.reset} ${C.gray}${mins}:${secs}${C.reset}`,
|
|
110
|
+
`${C.blue}💬${C.reset} ${C.gray}${sessionStats.messages} msgs${C.reset}`,
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
console.log(line("─", C.gray));
|
|
114
|
+
console.log(" " + items.join(` ${C.gray}│${C.reset} `));
|
|
115
|
+
console.log(line("─", C.gray));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Menu d'aide ────────────────────────────────────────────
|
|
119
|
+
function showHelp() {
|
|
120
|
+
console.log();
|
|
121
|
+
box([
|
|
122
|
+
`${C.yellowBright + C.bold}BIBEST CODE — Commandes disponibles${C.reset}`,
|
|
123
|
+
"",
|
|
124
|
+
`${C.cyanBright}/help${C.reset} ${C.gray}Afficher cette aide${C.reset}`,
|
|
125
|
+
`${C.cyanBright}/clear${C.reset} ${C.gray}Effacer l'écran${C.reset}`,
|
|
126
|
+
`${C.cyanBright}/history${C.reset} ${C.gray}Voir l'historique des sessions${C.reset}`,
|
|
127
|
+
`${C.cyanBright}/stats${C.reset} ${C.gray}Statistiques de la session${C.reset}`,
|
|
128
|
+
`${C.cyanBright}/model${C.reset} ${C.gray}Changer de modèle Claude${C.reset}`,
|
|
129
|
+
`${C.cyanBright}/cd <dossier>${C.reset} ${C.gray}Changer de répertoire${C.reset}`,
|
|
130
|
+
`${C.cyanBright}/ls${C.reset} ${C.gray}Lister le répertoire courant${C.reset}`,
|
|
131
|
+
`${C.cyanBright}/reset${C.reset} ${C.gray}Réinitialiser la conversation${C.reset}`,
|
|
132
|
+
`${C.cyanBright}/exit${C.reset} ${C.gray}Quitter BIBEST CODE${C.reset}`,
|
|
133
|
+
"",
|
|
134
|
+
`${C.gray}Exemples de tâches :${C.reset}`,
|
|
135
|
+
`${C.green}▶${C.reset} ${C.italic}crée une API REST en Express${C.reset}`,
|
|
136
|
+
`${C.green}▶${C.reset} ${C.italic}analyse et corrige les bugs de mon projet${C.reset}`,
|
|
137
|
+
`${C.green}▶${C.reset} ${C.italic}écris des tests unitaires pour app.js${C.reset}`,
|
|
138
|
+
`${C.green}▶${C.reset} ${C.italic}optimise les performances de ce code${C.reset}`,
|
|
139
|
+
], C.cyanBright);
|
|
140
|
+
console.log();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── Statistiques ───────────────────────────────────────────
|
|
144
|
+
function showStats() {
|
|
145
|
+
console.log();
|
|
146
|
+
box([
|
|
147
|
+
`${C.yellowBright + C.bold}📊 Statistiques de session${C.reset}`,
|
|
148
|
+
"",
|
|
149
|
+
`${C.cyan}Messages envoyés :${C.reset} ${C.whiteBright}${sessionStats.messages}${C.reset}`,
|
|
150
|
+
`${C.cyan}Fichiers créés :${C.reset} ${C.greenBright}${sessionStats.filesCreated}${C.reset}`,
|
|
151
|
+
`${C.cyan}Fichiers lus :${C.reset} ${C.blueBright}${sessionStats.filesRead}${C.reset}`,
|
|
152
|
+
`${C.cyan}Commandes exécutées:${C.reset} ${C.yellowBright}${sessionStats.commandsRun}${C.reset}`,
|
|
153
|
+
`${C.cyan}Modèle actuel :${C.reset} ${C.magentaBright}${currentModel}${C.reset}`,
|
|
154
|
+
], C.yellowBright);
|
|
155
|
+
console.log();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── Sélecteur de modèle ────────────────────────────────────
|
|
159
|
+
async function selectModel(rl) {
|
|
160
|
+
const models = [
|
|
161
|
+
{ id: "claude-opus-4-6", label: "Claude Opus 4.6", desc: "Le plus puissant — tâches complexes" },
|
|
162
|
+
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", desc: "Équilibre puissance/vitesse (recommandé)" },
|
|
163
|
+
{ id: "claude-haiku-4-5", label: "Claude Haiku 4.5", desc: "Ultra rapide — tâches simples" },
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
console.log();
|
|
167
|
+
box([
|
|
168
|
+
`${C.yellowBright + C.bold}🤖 Choisir un modèle Claude${C.reset}`,
|
|
169
|
+
"",
|
|
170
|
+
...models.map((m, i) => `${C.cyanBright}${i + 1}.${C.reset} ${C.whiteBright}${m.label}${C.reset} ${C.gray}${m.desc}${C.reset}${m.id === currentModel ? ` ${C.greenBright}← actuel${C.reset}` : ""}`),
|
|
171
|
+
], C.yellowBright);
|
|
172
|
+
|
|
173
|
+
return new Promise((resolve) => {
|
|
174
|
+
process.stdout.write(`\n${C.yellow}Choix (1-3) : ${C.reset}`);
|
|
175
|
+
rl.once("line", (input) => {
|
|
176
|
+
const idx = parseInt(input.trim()) - 1;
|
|
177
|
+
if (idx >= 0 && idx < models.length) {
|
|
178
|
+
currentModel = models[idx].id;
|
|
179
|
+
console.log(`\n${C.greenBright}✅ Modèle changé → ${C.bold}${models[idx].label}${C.reset}\n`);
|
|
180
|
+
} else {
|
|
181
|
+
console.log(`\n${C.gray}Annulé.${C.reset}\n`);
|
|
182
|
+
}
|
|
183
|
+
resolve();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ── Historique sessions ────────────────────────────────────
|
|
189
|
+
function saveHistory(history) {
|
|
190
|
+
try {
|
|
191
|
+
const data = { date: new Date().toISOString(), model: currentModel, stats: sessionStats, messages: history.slice(-20) };
|
|
192
|
+
let all = [];
|
|
193
|
+
if (fs.existsSync(HISTORY_FILE)) all = JSON.parse(fs.readFileSync(HISTORY_FILE, "utf8"));
|
|
194
|
+
all.unshift(data);
|
|
195
|
+
if (all.length > 10) all = all.slice(0, 10);
|
|
196
|
+
fs.writeFileSync(HISTORY_FILE, JSON.stringify(all, null, 2));
|
|
197
|
+
} catch {}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function showHistory() {
|
|
201
|
+
console.log();
|
|
202
|
+
try {
|
|
203
|
+
if (!fs.existsSync(HISTORY_FILE)) { console.log(`${C.gray} Aucun historique disponible.${C.reset}\n`); return; }
|
|
204
|
+
const all = JSON.parse(fs.readFileSync(HISTORY_FILE, "utf8"));
|
|
205
|
+
box([
|
|
206
|
+
`${C.yellowBright + C.bold}📜 Historique des sessions${C.reset}`,
|
|
207
|
+
"",
|
|
208
|
+
...all.map((s, i) => {
|
|
209
|
+
const d = new Date(s.date).toLocaleString("fr-FR");
|
|
210
|
+
return `${C.cyanBright}${i + 1}.${C.reset} ${C.whiteBright}${d}${C.reset} ${C.gray}${s.model} — ${s.stats.messages} msgs — ${s.stats.filesCreated} fichiers${C.reset}`;
|
|
211
|
+
}),
|
|
212
|
+
], C.blueBright);
|
|
213
|
+
} catch { console.log(`${C.gray} Erreur lecture historique.${C.reset}`); }
|
|
214
|
+
console.log();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ── Outils ─────────────────────────────────────────────────
|
|
218
|
+
const tools = [
|
|
219
|
+
{ name: "read_file", description: "Lire le contenu d'un fichier", input_schema: { type: "object", properties: { path: { type: "string", description: "Chemin du fichier" } }, required: ["path"] } },
|
|
220
|
+
{ name: "write_file", description: "Créer ou écrire dans un fichier", input_schema: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] } },
|
|
221
|
+
{ name: "list_files", description: "Lister les fichiers d'un répertoire", input_schema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } },
|
|
222
|
+
{ name: "run_command", description: "Exécuter une commande shell", input_schema: { type: "object", properties: { command: { type: "string" } }, required: ["command"] } },
|
|
223
|
+
{ name: "delete_file", description: "Supprimer un fichier", input_schema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } },
|
|
224
|
+
{ name: "create_directory", description: "Créer un dossier", input_schema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } },
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
const toolIcons = { read_file: "📄", write_file: "✏️ ", list_files: "📁", run_command: "⚡", delete_file: "🗑️ ", create_directory: "📂" };
|
|
228
|
+
|
|
229
|
+
async function executeTool(name, input) {
|
|
230
|
+
try {
|
|
231
|
+
switch (name) {
|
|
232
|
+
case "read_file": {
|
|
233
|
+
const content = fs.readFileSync(input.path, "utf8");
|
|
234
|
+
sessionStats.filesRead++;
|
|
235
|
+
console.log(` ${C.blueBright}└─${C.reset} ${C.gray}${input.path} (${content.length} chars)${C.reset}`);
|
|
236
|
+
return content;
|
|
237
|
+
}
|
|
238
|
+
case "write_file": {
|
|
239
|
+
const dir = path.dirname(input.path);
|
|
240
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
241
|
+
fs.writeFileSync(input.path, input.content, "utf8");
|
|
242
|
+
sessionStats.filesCreated++;
|
|
243
|
+
console.log(` ${C.greenBright}└─${C.reset} ${C.gray}${input.path}${C.reset}`);
|
|
244
|
+
return `Fichier écrit: ${input.path}`;
|
|
245
|
+
}
|
|
246
|
+
case "list_files": {
|
|
247
|
+
const files = fs.readdirSync(input.path);
|
|
248
|
+
console.log(` ${C.blueBright}└─${C.reset} ${C.gray}${files.length} entrées${C.reset}`);
|
|
249
|
+
return files.map(f => {
|
|
250
|
+
const full = path.join(input.path, f);
|
|
251
|
+
const stat = fs.statSync(full);
|
|
252
|
+
return `${stat.isDirectory() ? "[DIR] " : " "}${f}`;
|
|
253
|
+
}).join("\n");
|
|
254
|
+
}
|
|
255
|
+
case "run_command": {
|
|
256
|
+
sessionStats.commandsRun++;
|
|
257
|
+
console.log(` ${C.yellowBright}└─${C.reset} ${C.gray}${input.command}${C.reset}`);
|
|
258
|
+
const { stdout, stderr } = await execAsync(input.command, { timeout: 30000, cwd: process.cwd() });
|
|
259
|
+
return (stdout + (stderr ? "\nSTDERR: " + stderr : "")).trim() || "(aucune sortie)";
|
|
260
|
+
}
|
|
261
|
+
case "delete_file": {
|
|
262
|
+
fs.unlinkSync(input.path);
|
|
263
|
+
console.log(` ${C.redBright}└─${C.reset} ${C.gray}${input.path}${C.reset}`);
|
|
264
|
+
return `Supprimé: ${input.path}`;
|
|
265
|
+
}
|
|
266
|
+
case "create_directory": {
|
|
267
|
+
fs.mkdirSync(input.path, { recursive: true });
|
|
268
|
+
console.log(` ${C.greenBright}└─${C.reset} ${C.gray}${input.path}${C.reset}`);
|
|
269
|
+
return `Dossier créé: ${input.path}`;
|
|
270
|
+
}
|
|
271
|
+
default: return `Outil inconnu: ${name}`;
|
|
272
|
+
}
|
|
273
|
+
} catch (e) { return `ERREUR: ${e.message}`; }
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ── Animation de chargement ────────────────────────────────
|
|
277
|
+
function startSpinner(text) {
|
|
278
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
279
|
+
let i = 0;
|
|
280
|
+
const timer = setInterval(() => {
|
|
281
|
+
process.stdout.write(`\r ${C.cyanBright}${frames[i % frames.length]}${C.reset} ${C.gray}${text}${C.reset} `);
|
|
282
|
+
i++;
|
|
283
|
+
}, 80);
|
|
284
|
+
return () => { clearInterval(timer); process.stdout.write("\r" + " ".repeat(60) + "\r"); };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ── Agent principal ────────────────────────────────────────
|
|
288
|
+
async function runAgent(userMessage, history) {
|
|
289
|
+
sessionStats.messages++;
|
|
290
|
+
history.push({ role: "user", content: userMessage });
|
|
291
|
+
|
|
292
|
+
const stopSpinner = startSpinner("BIBEST CODE réfléchit...");
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
while (true) {
|
|
296
|
+
const response = await client.messages.create({
|
|
297
|
+
model: currentModel,
|
|
298
|
+
max_tokens: 8096,
|
|
299
|
+
system: `Tu es BIBEST CODE, un agent de développement expert créé par BIBI ONDOUA.
|
|
300
|
+
Tu peux lire/écrire des fichiers, exécuter des commandes shell, créer des projets complets.
|
|
301
|
+
Répertoire de travail actuel: ${process.cwd()}
|
|
302
|
+
Réponds toujours en français sauf demande contraire.
|
|
303
|
+
Sois efficace, précis et professionnel. Utilise les outils pour accomplir les tâches concrètement.
|
|
304
|
+
Après chaque action, donne un résumé clair de ce qui a été fait.`,
|
|
305
|
+
tools,
|
|
306
|
+
messages: history,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
stopSpinner();
|
|
310
|
+
history.push({ role: "assistant", content: response.content });
|
|
311
|
+
|
|
312
|
+
let hasToolUse = false;
|
|
313
|
+
const toolResults = [];
|
|
314
|
+
|
|
315
|
+
for (const block of response.content) {
|
|
316
|
+
if (block.type === "text" && block.text) {
|
|
317
|
+
console.log();
|
|
318
|
+
console.log(line("─", C.gray));
|
|
319
|
+
const lines = block.text.split("\n");
|
|
320
|
+
lines.forEach(l => {
|
|
321
|
+
if (l.startsWith("# ")) console.log(` ${C.yellowBright + C.bold}${l.substring(2)}${C.reset}`);
|
|
322
|
+
else if (l.startsWith("## ")) console.log(` ${C.cyanBright + C.bold}${l.substring(3)}${C.reset}`);
|
|
323
|
+
else if (l.startsWith("- ") || l.startsWith("• ")) console.log(` ${C.cyan}▸${C.reset} ${C.white}${l.substring(2)}${C.reset}`);
|
|
324
|
+
else if (l.startsWith("```")) console.log(` ${C.gray}${l}${C.reset}`);
|
|
325
|
+
else if (l.trim() === "") console.log();
|
|
326
|
+
else console.log(` ${C.whiteBright}${l}${C.reset}`);
|
|
327
|
+
});
|
|
328
|
+
console.log(line("─", C.gray));
|
|
329
|
+
}
|
|
330
|
+
if (block.type === "tool_use") {
|
|
331
|
+
hasToolUse = true;
|
|
332
|
+
const icon = toolIcons[block.name] || "🔧";
|
|
333
|
+
console.log(`\n ${icon} ${C.magentaBright + C.bold}${block.name}${C.reset} ${C.gray}en cours...${C.reset}`);
|
|
334
|
+
const result = await executeTool(block.name, block.input);
|
|
335
|
+
toolResults.push({ type: "tool_result", tool_use_id: block.id, content: result });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (hasToolUse) {
|
|
340
|
+
history.push({ role: "user", content: toolResults });
|
|
341
|
+
const stop2 = startSpinner("Traitement des résultats...");
|
|
342
|
+
setTimeout(stop2, 100);
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
} catch (e) {
|
|
348
|
+
stopSpinner();
|
|
349
|
+
throw e;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ── Prompt décoré ──────────────────────────────────────────
|
|
354
|
+
function showPrompt() {
|
|
355
|
+
const cwd = path.basename(process.cwd());
|
|
356
|
+
process.stdout.write(
|
|
357
|
+
`\n ${C.gray}[${C.cyanBright}${cwd}${C.gray}]${C.reset} ${C.yellowBright + C.bold}❯${C.reset} `
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ── Main ───────────────────────────────────────────────────
|
|
362
|
+
async function main() {
|
|
363
|
+
showHeader();
|
|
364
|
+
|
|
365
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
366
|
+
box([
|
|
367
|
+
`${C.redBright + C.bold}❌ Clé API manquante${C.reset}`,
|
|
368
|
+
"",
|
|
369
|
+
`Lance cette commande d'abord :`,
|
|
370
|
+
`${C.yellowBright}$env:ANTHROPIC_API_KEY="sk-ant-..."${C.reset}`,
|
|
371
|
+
], C.redBright);
|
|
372
|
+
process.exit(1);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
showStatus();
|
|
376
|
+
showHelp();
|
|
377
|
+
|
|
378
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
379
|
+
const history = [];
|
|
380
|
+
|
|
381
|
+
const prompt = () => {
|
|
382
|
+
showPrompt();
|
|
383
|
+
rl.question("", async (input) => {
|
|
384
|
+
const t = input.trim();
|
|
385
|
+
if (!t) return prompt();
|
|
386
|
+
|
|
387
|
+
// Commandes internes
|
|
388
|
+
if (t === "/exit" || t === "exit") {
|
|
389
|
+
saveHistory(history);
|
|
390
|
+
console.log();
|
|
391
|
+
box([
|
|
392
|
+
`${C.cyanBright + C.bold}👋 Merci d'avoir utilisé BIBEST CODE${C.reset}`,
|
|
393
|
+
"",
|
|
394
|
+
`${C.gray}Session sauvegardée — À bientôt !${C.reset}`,
|
|
395
|
+
`${C.gray}© BIBI ONDOUA — Tous droits réservés${C.reset}`,
|
|
396
|
+
], C.cyanBright);
|
|
397
|
+
console.log();
|
|
398
|
+
process.exit(0);
|
|
399
|
+
}
|
|
400
|
+
if (t === "/help") { showHelp(); return prompt(); }
|
|
401
|
+
if (t === "/clear") { showHeader(); showStatus(); return prompt(); }
|
|
402
|
+
if (t === "/stats") { showStats(); return prompt(); }
|
|
403
|
+
if (t === "/history") { showHistory(); return prompt(); }
|
|
404
|
+
if (t === "/reset") { history.length = 0; console.log(`\n ${C.greenBright}✅ Conversation réinitialisée${C.reset}\n`); return prompt(); }
|
|
405
|
+
if (t === "/ls") {
|
|
406
|
+
const files = fs.readdirSync(process.cwd());
|
|
407
|
+
console.log();
|
|
408
|
+
files.forEach(f => {
|
|
409
|
+
const stat = fs.statSync(f);
|
|
410
|
+
console.log(` ${stat.isDirectory() ? C.blueBright + "📁" : C.gray + "📄"} ${C.whiteBright}${f}${C.reset}`);
|
|
411
|
+
});
|
|
412
|
+
console.log();
|
|
413
|
+
return prompt();
|
|
414
|
+
}
|
|
415
|
+
if (t.startsWith("/cd ")) {
|
|
416
|
+
const dir = t.substring(4).trim();
|
|
417
|
+
try { process.chdir(dir); console.log(`\n ${C.greenBright}✅ Répertoire: ${process.cwd()}${C.reset}\n`); }
|
|
418
|
+
catch { console.log(`\n ${C.redBright}❌ Dossier introuvable: ${dir}${C.reset}\n`); }
|
|
419
|
+
return prompt();
|
|
420
|
+
}
|
|
421
|
+
if (t.startsWith("/model")) { await selectModel(rl); return prompt(); }
|
|
422
|
+
|
|
423
|
+
// Message pour l'agent
|
|
424
|
+
try {
|
|
425
|
+
await runAgent(t, history);
|
|
426
|
+
} catch (e) {
|
|
427
|
+
console.log();
|
|
428
|
+
box([`${C.redBright}❌ Erreur: ${e.message}${C.reset}`], C.redBright);
|
|
429
|
+
}
|
|
430
|
+
prompt();
|
|
431
|
+
});
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
prompt();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bibest-code",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Agent CLI de developpement propulse par Claude AI",
|
|
5
|
+
"main": "bibest-code.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"bibest-code": "./bibest-code.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["ai", "cli", "claude", "agent", "developer-tools"],
|
|
11
|
+
"author": "BIBI ONDOUA <b.ondoua00@gmail.com>",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
15
|
+
"readline-sync": "^1.4.10"
|
|
16
|
+
}
|
|
17
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("bibest-code fonctionne !");
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# 📝 Todo List — REST API
|
|
2
|
+
|
|
3
|
+
> API REST complète construite avec **Express.js** | par **BIBEST CODE — BIBI ONDOUA**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🚀 Installation & démarrage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 1. Installer les dépendances
|
|
11
|
+
npm install
|
|
12
|
+
|
|
13
|
+
# 2. Démarrer en développement (rechargement auto)
|
|
14
|
+
npm run dev
|
|
15
|
+
|
|
16
|
+
# 3. Démarrer en production
|
|
17
|
+
npm start
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Le serveur écoute sur **http://localhost:3000**
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📂 Structure du projet
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
todo-api/
|
|
28
|
+
├── src/
|
|
29
|
+
│ ├── server.js # Point d'entrée
|
|
30
|
+
│ ├── routes/
|
|
31
|
+
│ │ └── todoRoutes.js # Définition des routes
|
|
32
|
+
│ ├── controllers/
|
|
33
|
+
│ │ └── todoController.js # Logique métier CRUD
|
|
34
|
+
│ ├── middlewares/
|
|
35
|
+
│ │ ├── validate.js # Validation des données
|
|
36
|
+
│ │ └── errorHandler.js # Gestion des erreurs
|
|
37
|
+
│ └── data/
|
|
38
|
+
│ └── todos.js # Stockage en mémoire
|
|
39
|
+
├── package.json
|
|
40
|
+
├── .env.example
|
|
41
|
+
└── README.md
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🔗 Endpoints
|
|
47
|
+
|
|
48
|
+
| Méthode | Route | Description |
|
|
49
|
+
|----------|----------------------------|----------------------------------------|
|
|
50
|
+
| `GET` | `/api/todos` | Lister tous les todos |
|
|
51
|
+
| `GET` | `/api/todos/:id` | Récupérer un todo par ID |
|
|
52
|
+
| `POST` | `/api/todos` | Créer un nouveau todo |
|
|
53
|
+
| `PUT` | `/api/todos/:id` | Remplacer complètement un todo |
|
|
54
|
+
| `PATCH` | `/api/todos/:id` | Mettre à jour partiellement un todo |
|
|
55
|
+
| `PATCH` | `/api/todos/:id/toggle` | Basculer le statut `completed` |
|
|
56
|
+
| `DELETE` | `/api/todos/:id` | Supprimer un todo |
|
|
57
|
+
| `DELETE` | `/api/todos` | Supprimer tous les todos complétés |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🔍 Paramètres de requête (GET /api/todos)
|
|
62
|
+
|
|
63
|
+
| Paramètre | Type | Valeurs possibles | Exemple |
|
|
64
|
+
|-------------|---------|---------------------------------------|-----------------------------|
|
|
65
|
+
| `completed` | boolean | `true` \| `false` | `?completed=false` |
|
|
66
|
+
| `priority` | string | `low` \| `medium` \| `high` | `?priority=high` |
|
|
67
|
+
| `search` | string | texte libre | `?search=express` |
|
|
68
|
+
| `sort` | string | `createdAt` \| `updatedAt` \| `title` | `?sort=title` |
|
|
69
|
+
| `order` | string | `asc` \| `desc` | `?order=asc` |
|
|
70
|
+
| `page` | number | entier positif (défaut: 1) | `?page=2` |
|
|
71
|
+
| `limit` | number | entier positif (défaut: 10) | `?limit=5` |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 📋 Modèle de données
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"id": "uuid-v4",
|
|
80
|
+
"title": "string (requis, max 100 chars)",
|
|
81
|
+
"description": "string (optionnel, max 500 chars)",
|
|
82
|
+
"completed": "boolean (défaut: false)",
|
|
83
|
+
"priority": "low | medium | high (défaut: medium)",
|
|
84
|
+
"createdAt": "ISO date",
|
|
85
|
+
"updatedAt": "ISO date"
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 💡 Exemples de requêtes
|
|
92
|
+
|
|
93
|
+
### Créer un todo
|
|
94
|
+
```bash
|
|
95
|
+
curl -X POST http://localhost:3000/api/todos \
|
|
96
|
+
-H "Content-Type: application/json" \
|
|
97
|
+
-d '{"title": "Ma tâche", "description": "Description ici", "priority": "high"}'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Lister avec filtres
|
|
101
|
+
```bash
|
|
102
|
+
curl "http://localhost:3000/api/todos?completed=false&priority=high&sort=createdAt&order=desc&page=1&limit=5"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Mettre à jour partiellement
|
|
106
|
+
```bash
|
|
107
|
+
curl -X PATCH http://localhost:3000/api/todos/:id \
|
|
108
|
+
-H "Content-Type: application/json" \
|
|
109
|
+
-d '{"completed": true}'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Basculer le statut
|
|
113
|
+
```bash
|
|
114
|
+
curl -X PATCH http://localhost:3000/api/todos/:id/toggle
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Supprimer tous les complétés
|
|
118
|
+
```bash
|
|
119
|
+
curl -X DELETE http://localhost:3000/api/todos
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 📦 Dépendances
|
|
125
|
+
|
|
126
|
+
| Package | Rôle |
|
|
127
|
+
|------------|-------------------------------------------|
|
|
128
|
+
| `express` | Framework HTTP |
|
|
129
|
+
| `uuid` | Génération d'identifiants uniques |
|
|
130
|
+
| `cors` | Gestion des requêtes cross-origin |
|
|
131
|
+
| `morgan` | Logger HTTP |
|
|
132
|
+
| `nodemon` | Rechargement automatique (dev) |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
*BIBEST CODE — by BIBI ONDOUA*
|