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 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,3 @@
1
+ # Variables d'environnement — Todo API
2
+ PORT=3000
3
+ NODE_ENV=development
@@ -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*