vibe-academy-cli 1.2.0 → 1.3.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 +15 -14
- package/dist/index.js +31 -211
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/.claude/.gitattributes +2 -0
- package/templates/.claude/scripts/statusline/fixtures/test-input.json +36 -23
- package/templates/.claude/scripts/statusline/src/index.ts +74 -60
- package/templates/.claude/scripts/statusline/src/lib/context.ts +31 -77
- package/templates/.claude/scripts/statusline/src/lib/formatters.ts +36 -32
- package/templates/.claude/scripts/statusline/src/lib/types.ts +40 -23
- package/templates/.claude/scripts/statusline/src/lib/usage-limits.ts +51 -41
- package/templates/.claude/scripts/statusline/statusline.config.ts +20 -20
package/README.md
CHANGED
|
@@ -141,11 +141,12 @@ affiche en temps réel:
|
|
|
141
141
|
|
|
142
142
|
**note importante**: l'utilisation % et le temps restant viennent directement de l'api anthropic (`/api/oauth/usage`), pas d'une projection. c'est le vrai temps avant que ta fenêtre de 5h se réinitialise.
|
|
143
143
|
|
|
144
|
-
##
|
|
144
|
+
## prerequis
|
|
145
145
|
|
|
146
146
|
- **node.js** >= 18
|
|
147
|
-
- **bun** (
|
|
148
|
-
- **macos**
|
|
147
|
+
- **bun** (recommande) ou npm/pnpm
|
|
148
|
+
- **macos** recommande (notifications audio et auth keychain fonctionnent nativement)
|
|
149
|
+
- **windows/linux** : support partiel (voir section compatibilite)
|
|
149
150
|
|
|
150
151
|
## développement
|
|
151
152
|
|
|
@@ -227,24 +228,24 @@ avantages:
|
|
|
227
228
|
|
|
228
229
|
au setup, on vérifie que le token oauth a le scope `user:profile`. si non, on propose de ré-authentifier automatiquement.
|
|
229
230
|
|
|
230
|
-
##
|
|
231
|
+
## compatibilite
|
|
231
232
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
| fonctionnalite | macOS | Windows | Linux |
|
|
234
|
+
|----------------|-------|---------|-------|
|
|
235
|
+
| installation | OK | OK | OK |
|
|
236
|
+
| hooks securite | OK | partiel | OK |
|
|
237
|
+
| audio | OK | non teste | partiel |
|
|
238
|
+
| statusline | OK | KO | partiel |
|
|
239
|
+
| auth OAuth | OK | KO | KO |
|
|
240
|
+
**note** : le support Windows est en cours d'amelioration. voir `docs/memory-bank/features/windows-compat.md`.
|
|
236
241
|
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
/plugin install vibe-academy@vibe-academy-marketplace
|
|
240
|
-
```
|
|
242
|
+
## prochaines etapes apres installation
|
|
241
243
|
|
|
242
|
-
|
|
244
|
+
lancez `claude` et commencez a coder!
|
|
243
245
|
|
|
244
246
|
## inspirations
|
|
245
247
|
|
|
246
248
|
- [aiblueprint-cli](https://github.com/Melvynx/aiblueprint/tree/main/claude-code-config/)
|
|
247
|
-
- [superpowers](https://github.com/obra/superpowers)
|
|
248
249
|
|
|
249
250
|
## licence
|
|
250
251
|
|
package/dist/index.js
CHANGED
|
@@ -36,13 +36,8 @@ var init_logger = __esm({
|
|
|
36
36
|
console.log(`Configuration install\xE9e dans: ${chalk.cyan(path2)}
|
|
37
37
|
`);
|
|
38
38
|
console.log("Prochaines \xE9tapes:");
|
|
39
|
-
console.log(
|
|
40
|
-
|
|
41
|
-
);
|
|
42
|
-
console.log(
|
|
43
|
-
" 2. Installer les plugins: /plugin install vibe-academy@vibe-academy-marketplace"
|
|
44
|
-
);
|
|
45
|
-
console.log(" 3. Commencer \xE0 coder! \u{1F389}\n");
|
|
39
|
+
console.log(" 1. Lancez 'claude' pour commencer \xE0 coder!");
|
|
40
|
+
console.log("");
|
|
46
41
|
},
|
|
47
42
|
error(message) {
|
|
48
43
|
console.error(chalk.red(`
|
|
@@ -69,17 +64,17 @@ var files_exports = {};
|
|
|
69
64
|
__export(files_exports, {
|
|
70
65
|
installConfig: () => installConfig
|
|
71
66
|
});
|
|
72
|
-
import { mkdir
|
|
73
|
-
import { join as
|
|
67
|
+
import { mkdir, cp, writeFile, chmod, readFile } from "fs/promises";
|
|
68
|
+
import { join as join2, dirname } from "path";
|
|
74
69
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
75
|
-
import { existsSync
|
|
70
|
+
import { existsSync } from "fs";
|
|
76
71
|
import { execSync as execSync3 } from "child_process";
|
|
77
72
|
function getWhichCommand() {
|
|
78
73
|
return isWindows ? "where" : "which";
|
|
79
74
|
}
|
|
80
75
|
function getTemplatesDir() {
|
|
81
|
-
const templatesPath =
|
|
82
|
-
if (
|
|
76
|
+
const templatesPath = join2(__dirname2, "../templates");
|
|
77
|
+
if (existsSync(templatesPath)) {
|
|
83
78
|
return templatesPath;
|
|
84
79
|
}
|
|
85
80
|
throw new Error(`Templates directory not found at: ${templatesPath}`);
|
|
@@ -127,7 +122,7 @@ async function installCcusage(runtime) {
|
|
|
127
122
|
}
|
|
128
123
|
}
|
|
129
124
|
async function installStatuslineDependencies(claudeDir, runtime) {
|
|
130
|
-
const statuslineDir =
|
|
125
|
+
const statuslineDir = join2(claudeDir, "scripts/statusline");
|
|
131
126
|
const spinner = logger.spinner(
|
|
132
127
|
`Installation des d\xE9pendances statusline avec ${runtime}...`
|
|
133
128
|
);
|
|
@@ -168,9 +163,10 @@ function getAudioCommand(audioFile) {
|
|
|
168
163
|
if (process.platform === "darwin") {
|
|
169
164
|
return `afplay -v 0.1 ${audioFile}`;
|
|
170
165
|
} else if (isWindows) {
|
|
171
|
-
|
|
166
|
+
const escapedPath = audioFile.replace(/\\/g, "\\\\");
|
|
167
|
+
return `powershell -ExecutionPolicy Bypass -c "Add-Type -AssemblyName presentationCore; $p = New-Object System.Windows.Media.MediaPlayer; $p.Open('${escapedPath}'); $p.Volume = 0.1; $p.Play(); Start-Sleep -Seconds 3"`;
|
|
172
168
|
} else {
|
|
173
|
-
return `
|
|
169
|
+
return `paplay ${audioFile} 2>/dev/null || aplay -q ${audioFile} 2>/dev/null || true`;
|
|
174
170
|
}
|
|
175
171
|
}
|
|
176
172
|
function replacePathsInObject(obj, claudeDir, _runtime) {
|
|
@@ -207,11 +203,11 @@ function replacePathsInObject(obj, claudeDir, _runtime) {
|
|
|
207
203
|
return obj;
|
|
208
204
|
}
|
|
209
205
|
async function generateSettings(claudeDir, runtime, config) {
|
|
210
|
-
const templatePath =
|
|
206
|
+
const templatePath = join2(
|
|
211
207
|
getTemplatesDir(),
|
|
212
208
|
".claude/settings.json.template"
|
|
213
209
|
);
|
|
214
|
-
const template = await
|
|
210
|
+
const template = await readFile(templatePath, "utf-8");
|
|
215
211
|
const settingsObj = JSON.parse(template);
|
|
216
212
|
const settings = replacePathsInObject(
|
|
217
213
|
settingsObj,
|
|
@@ -224,30 +220,34 @@ async function generateSettings(claudeDir, runtime, config) {
|
|
|
224
220
|
if (!config.statusline) {
|
|
225
221
|
delete settings.statusLine;
|
|
226
222
|
}
|
|
227
|
-
await
|
|
228
|
-
|
|
223
|
+
await writeFile(
|
|
224
|
+
join2(claudeDir, "settings.json"),
|
|
229
225
|
JSON.stringify(settings, null, 2)
|
|
230
226
|
);
|
|
231
227
|
}
|
|
232
228
|
async function installConfig(targetPath, config) {
|
|
233
229
|
const templatesDir = getTemplatesDir();
|
|
234
|
-
const sourceDir =
|
|
230
|
+
const sourceDir = join2(templatesDir, ".claude");
|
|
235
231
|
const runtime = detectRuntime();
|
|
236
232
|
logger.info(`Runtime d\xE9tect\xE9: ${runtime}`);
|
|
237
|
-
await
|
|
233
|
+
await mkdir(targetPath, { recursive: true });
|
|
238
234
|
if (config.hooks || config.statusline) {
|
|
239
235
|
const scriptsSpinner = logger.spinner("Copie des scripts...");
|
|
240
|
-
await cp(
|
|
236
|
+
await cp(join2(sourceDir, "scripts"), join2(targetPath, "scripts"), {
|
|
241
237
|
recursive: true
|
|
242
238
|
});
|
|
243
239
|
scriptsSpinner.succeed("Scripts copi\xE9s");
|
|
244
240
|
if (config.hooks) {
|
|
245
|
-
await chmod(
|
|
246
|
-
await chmod(
|
|
241
|
+
await chmod(join2(targetPath, "scripts/validate-command.js"), 493);
|
|
242
|
+
await chmod(join2(targetPath, "scripts/hook-post-file.ts"), 493);
|
|
247
243
|
}
|
|
248
244
|
}
|
|
245
|
+
const gitattribsSrc = join2(sourceDir, ".gitattributes");
|
|
246
|
+
if (existsSync(gitattribsSrc)) {
|
|
247
|
+
await cp(gitattribsSrc, join2(targetPath, ".gitattributes"));
|
|
248
|
+
}
|
|
249
249
|
const songSpinner = logger.spinner("Copie des fichiers audio...");
|
|
250
|
-
await cp(
|
|
250
|
+
await cp(join2(sourceDir, "song"), join2(targetPath, "song"), {
|
|
251
251
|
recursive: true
|
|
252
252
|
});
|
|
253
253
|
songSpinner.succeed("Fichiers audio copi\xE9s");
|
|
@@ -265,7 +265,7 @@ async function installConfig(targetPath, config) {
|
|
|
265
265
|
if (config.docs) {
|
|
266
266
|
const readmeSpinner = logger.spinner("Cr\xE9ation du README...");
|
|
267
267
|
const readme = generateReadme(targetPath);
|
|
268
|
-
await
|
|
268
|
+
await writeFile(join2(targetPath, "README.md"), readme);
|
|
269
269
|
readmeSpinner.succeed("README cr\xE9\xE9");
|
|
270
270
|
}
|
|
271
271
|
}
|
|
@@ -309,17 +309,7 @@ La statusline affiche :
|
|
|
309
309
|
|
|
310
310
|
## Prochaines \xE9tapes
|
|
311
311
|
|
|
312
|
-
|
|
313
|
-
\`\`\`bash
|
|
314
|
-
/plugin marketplace add ton-username/vibe-coding-marketplace
|
|
315
|
-
\`\`\`
|
|
316
|
-
|
|
317
|
-
2. Installer les plugins :
|
|
318
|
-
\`\`\`bash
|
|
319
|
-
/plugin install vibe-coding@vibe-coding-marketplace
|
|
320
|
-
\`\`\`
|
|
321
|
-
|
|
322
|
-
3. Commencer \xE0 coder ! \u{1F680}
|
|
312
|
+
Lancez 'claude' et commencez \xE0 coder !
|
|
323
313
|
|
|
324
314
|
## Mise \xE0 jour
|
|
325
315
|
|
|
@@ -477,169 +467,6 @@ async function checkAndPromptAuth() {
|
|
|
477
467
|
process.exit(0);
|
|
478
468
|
}
|
|
479
469
|
|
|
480
|
-
// src/utils/vibe-auth.ts
|
|
481
|
-
init_esm_shims();
|
|
482
|
-
init_logger();
|
|
483
|
-
import { homedir as homedir2 } from "os";
|
|
484
|
-
import { join as join2 } from "path";
|
|
485
|
-
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
486
|
-
import { existsSync } from "fs";
|
|
487
|
-
var VIBE_API_URL = "https://app.vibeacademy.eu/api/user";
|
|
488
|
-
var CONFIG_DIR = join2(homedir2(), ".vibe-academy");
|
|
489
|
-
var CONFIG_FILE = join2(CONFIG_DIR, "config.json");
|
|
490
|
-
async function validateApiKey(apiKey) {
|
|
491
|
-
if (!apiKey || apiKey.trim() === "") {
|
|
492
|
-
return { valid: false, error: "Cl\xE9 API vide" };
|
|
493
|
-
}
|
|
494
|
-
try {
|
|
495
|
-
const response = await fetch(VIBE_API_URL, {
|
|
496
|
-
method: "GET",
|
|
497
|
-
headers: {
|
|
498
|
-
Authorization: `Bearer ${apiKey.trim()}`,
|
|
499
|
-
"Content-Type": "application/json"
|
|
500
|
-
}
|
|
501
|
-
});
|
|
502
|
-
if (!response.ok) {
|
|
503
|
-
if (response.status === 401) {
|
|
504
|
-
return { valid: false, error: "Cl\xE9 API invalide ou expir\xE9e" };
|
|
505
|
-
}
|
|
506
|
-
return {
|
|
507
|
-
valid: false,
|
|
508
|
-
error: `Erreur serveur (${response.status})`
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
const data = await response.json();
|
|
512
|
-
if (data?.data) {
|
|
513
|
-
return {
|
|
514
|
-
valid: true,
|
|
515
|
-
user: data.data
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
return { valid: false, error: "R\xE9ponse API invalide" };
|
|
519
|
-
} catch (error) {
|
|
520
|
-
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
521
|
-
return {
|
|
522
|
-
valid: false,
|
|
523
|
-
error: "Pas de connexion internet. V\xE9rifiez votre connexion."
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
return {
|
|
527
|
-
valid: false,
|
|
528
|
-
error: `Erreur r\xE9seau: ${error instanceof Error ? error.message : "inconnue"}`
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
async function getStoredApiKey() {
|
|
533
|
-
try {
|
|
534
|
-
if (!existsSync(CONFIG_FILE)) {
|
|
535
|
-
return null;
|
|
536
|
-
}
|
|
537
|
-
const content = await readFile(CONFIG_FILE, "utf-8");
|
|
538
|
-
const config = JSON.parse(content);
|
|
539
|
-
return config.apiKey || null;
|
|
540
|
-
} catch {
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
async function storeApiKey(apiKey) {
|
|
545
|
-
try {
|
|
546
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
547
|
-
await mkdir(CONFIG_DIR, { recursive: true });
|
|
548
|
-
}
|
|
549
|
-
const config = { apiKey };
|
|
550
|
-
await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
551
|
-
} catch (error) {
|
|
552
|
-
throw new Error(
|
|
553
|
-
`Impossible de sauvegarder la cl\xE9 API: ${error instanceof Error ? error.message : "erreur inconnue"}`
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
async function clearApiKey() {
|
|
558
|
-
try {
|
|
559
|
-
if (existsSync(CONFIG_FILE)) {
|
|
560
|
-
const config = {};
|
|
561
|
-
await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
562
|
-
}
|
|
563
|
-
} catch {
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
async function checkAndPromptVibeAuth() {
|
|
567
|
-
const storedKey = await getStoredApiKey();
|
|
568
|
-
if (storedKey) {
|
|
569
|
-
const spinner = logger.spinner("V\xE9rification de votre cl\xE9 Vibe Academy...");
|
|
570
|
-
const result = await validateApiKey(storedKey);
|
|
571
|
-
spinner.stop();
|
|
572
|
-
if (result.valid && result.user) {
|
|
573
|
-
const name = result.user.name || result.user.email || "Apprenant";
|
|
574
|
-
const credits = result.user.credits ?? 0;
|
|
575
|
-
console.log(
|
|
576
|
-
`
|
|
577
|
-
\u2705 Connect\xE9 en tant que ${name} (${credits} cr\xE9dit${credits > 1 ? "s" : ""})
|
|
578
|
-
`
|
|
579
|
-
);
|
|
580
|
-
return true;
|
|
581
|
-
}
|
|
582
|
-
logger.warn("Votre cl\xE9 API a expir\xE9 ou est invalide.");
|
|
583
|
-
await clearApiKey();
|
|
584
|
-
}
|
|
585
|
-
return await promptForApiKey();
|
|
586
|
-
}
|
|
587
|
-
async function promptForApiKey() {
|
|
588
|
-
const inquirer2 = (await import("inquirer")).default;
|
|
589
|
-
console.log("\n\u{1F511} Authentification Vibe Academy requise\n");
|
|
590
|
-
console.log(
|
|
591
|
-
" Pour utiliser cette CLI, vous devez \xEAtre inscrit \xE0 la formation."
|
|
592
|
-
);
|
|
593
|
-
console.log(
|
|
594
|
-
" Votre cl\xE9 API est disponible sur: https://app.vibeacademy.eu/outils\n"
|
|
595
|
-
);
|
|
596
|
-
const { apiKey } = await inquirer2.prompt([
|
|
597
|
-
{
|
|
598
|
-
type: "password",
|
|
599
|
-
name: "apiKey",
|
|
600
|
-
message: "Entrez votre cl\xE9 API Vibe Academy:",
|
|
601
|
-
mask: "*",
|
|
602
|
-
validate: (input) => {
|
|
603
|
-
if (!input || input.trim() === "") {
|
|
604
|
-
return "La cl\xE9 API est requise";
|
|
605
|
-
}
|
|
606
|
-
return true;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
]);
|
|
610
|
-
const spinner = logger.spinner("Validation de votre cl\xE9...");
|
|
611
|
-
const result = await validateApiKey(apiKey);
|
|
612
|
-
spinner.stop();
|
|
613
|
-
if (!result.valid) {
|
|
614
|
-
logger.error(result.error || "Cl\xE9 invalide");
|
|
615
|
-
console.log(
|
|
616
|
-
"\n V\xE9rifiez votre cl\xE9 sur https://app.vibeacademy.eu/outils\n"
|
|
617
|
-
);
|
|
618
|
-
const { retry } = await inquirer2.prompt([
|
|
619
|
-
{
|
|
620
|
-
type: "confirm",
|
|
621
|
-
name: "retry",
|
|
622
|
-
message: "Voulez-vous r\xE9essayer ?",
|
|
623
|
-
default: true
|
|
624
|
-
}
|
|
625
|
-
]);
|
|
626
|
-
if (retry) {
|
|
627
|
-
return promptForApiKey();
|
|
628
|
-
}
|
|
629
|
-
return false;
|
|
630
|
-
}
|
|
631
|
-
await storeApiKey(apiKey);
|
|
632
|
-
const name = result.user?.name || result.user?.email || "Apprenant";
|
|
633
|
-
const credits = result.user?.credits ?? 0;
|
|
634
|
-
console.log(`
|
|
635
|
-
\u2705 Bienvenue ${name}!`);
|
|
636
|
-
console.log(
|
|
637
|
-
` Vous avez ${credits} cr\xE9dit${credits > 1 ? "s" : ""} disponible${credits > 1 ? "s" : ""}.
|
|
638
|
-
`
|
|
639
|
-
);
|
|
640
|
-
return true;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
470
|
// src/commands/setup.ts
|
|
644
471
|
function getDefaultConfig() {
|
|
645
472
|
return {
|
|
@@ -651,13 +478,6 @@ function getDefaultConfig() {
|
|
|
651
478
|
async function setupCommand(options) {
|
|
652
479
|
try {
|
|
653
480
|
logger.welcome();
|
|
654
|
-
const vibeAuthOk = await checkAndPromptVibeAuth();
|
|
655
|
-
if (!vibeAuthOk) {
|
|
656
|
-
logger.error(
|
|
657
|
-
"Authentification Vibe Academy requise pour utiliser cette CLI."
|
|
658
|
-
);
|
|
659
|
-
process.exit(1);
|
|
660
|
-
}
|
|
661
481
|
await checkAndPromptAuth();
|
|
662
482
|
let installPath;
|
|
663
483
|
if (options.folder) {
|
|
@@ -776,7 +596,7 @@ async function promptConfig(options) {
|
|
|
776
596
|
init_esm_shims();
|
|
777
597
|
init_logger();
|
|
778
598
|
init_files();
|
|
779
|
-
import { existsSync as
|
|
599
|
+
import { existsSync as existsSync2 } from "fs";
|
|
780
600
|
import { cp as cp2 } from "fs/promises";
|
|
781
601
|
async function backupConfig(installPath) {
|
|
782
602
|
const backupPath = `${installPath}.backup.${Date.now()}`;
|
|
@@ -793,7 +613,7 @@ async function updateCommand(options) {
|
|
|
793
613
|
try {
|
|
794
614
|
logger.info("\u{1F504} Mise \xE0 jour de la configuration Vibe Academy...\n");
|
|
795
615
|
const installPath = await detectInstallPath(options.folder);
|
|
796
|
-
if (!
|
|
616
|
+
if (!existsSync2(installPath)) {
|
|
797
617
|
logger.error(
|
|
798
618
|
`Aucune configuration trouv\xE9e dans ${installPath}
|
|
799
619
|
Utilisez 'vibe-academy setup' pour installer.`
|
|
@@ -823,14 +643,14 @@ Utilisez 'vibe-academy setup' pour installer.`
|
|
|
823
643
|
// src/commands/uninstall.ts
|
|
824
644
|
init_esm_shims();
|
|
825
645
|
init_logger();
|
|
826
|
-
import { existsSync as
|
|
646
|
+
import { existsSync as existsSync3 } from "fs";
|
|
827
647
|
import { rm } from "fs/promises";
|
|
828
648
|
import inquirer from "inquirer";
|
|
829
649
|
async function uninstallCommand(options) {
|
|
830
650
|
try {
|
|
831
651
|
logger.warn("\u26A0\uFE0F D\xE9sinstallation de la configuration Vibe Academy\n");
|
|
832
652
|
const installPath = await detectInstallPath(options.folder);
|
|
833
|
-
if (!
|
|
653
|
+
if (!existsSync3(installPath)) {
|
|
834
654
|
logger.error(`Aucune configuration trouv\xE9e dans ${installPath}`);
|
|
835
655
|
process.exit(1);
|
|
836
656
|
}
|