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 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
- ## prérequis
144
+ ## prerequis
145
145
 
146
146
  - **node.js** >= 18
147
- - **bun** (recommandé) ou npm/pnpm
148
- - **macos** pour les notifications audio et l'auth keychain
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
- ## prochaines étapes après installation
231
+ ## compatibilite
231
232
 
232
- 1. **ajouter le marketplace**:
233
- ```bash
234
- /plugin marketplace add matthieucousin/vibe-academy-marketplace
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
- 2. **installer les plugins**:
238
- ```bash
239
- /plugin install vibe-academy@vibe-academy-marketplace
240
- ```
242
+ ## prochaines etapes apres installation
241
243
 
242
- 3. **commencer à coder!**
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
- " 1. Ajouter le marketplace: /plugin marketplace add matthieucousin/vibe-academy-marketplace"
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 as mkdir2, cp, writeFile as writeFile2, chmod, readFile as readFile2 } from "fs/promises";
73
- import { join as join3, dirname } from "path";
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 as existsSync2 } from "fs";
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 = join3(__dirname2, "../templates");
82
- if (existsSync2(templatesPath)) {
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 = join3(claudeDir, "scripts/statusline");
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
- return `powershell -c "(New-Object Media.SoundPlayer '${audioFile}').PlaySync()"`;
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 `aplay -q ${audioFile} 2>/dev/null || paplay ${audioFile} 2>/dev/null || true`;
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 = join3(
206
+ const templatePath = join2(
211
207
  getTemplatesDir(),
212
208
  ".claude/settings.json.template"
213
209
  );
214
- const template = await readFile2(templatePath, "utf-8");
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 writeFile2(
228
- join3(claudeDir, "settings.json"),
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 = join3(templatesDir, ".claude");
230
+ const sourceDir = join2(templatesDir, ".claude");
235
231
  const runtime = detectRuntime();
236
232
  logger.info(`Runtime d\xE9tect\xE9: ${runtime}`);
237
- await mkdir2(targetPath, { recursive: true });
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(join3(sourceDir, "scripts"), join3(targetPath, "scripts"), {
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(join3(targetPath, "scripts/validate-command.js"), 493);
246
- await chmod(join3(targetPath, "scripts/hook-post-file.ts"), 493);
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(join3(sourceDir, "song"), join3(targetPath, "song"), {
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 writeFile2(join3(targetPath, "README.md"), readme);
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
- 1. Ajouter le marketplace :
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 existsSync3 } from "fs";
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 (!existsSync3(installPath)) {
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 existsSync4 } from "fs";
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 (!existsSync4(installPath)) {
653
+ if (!existsSync3(installPath)) {
834
654
  logger.error(`Aucune configuration trouv\xE9e dans ${installPath}`);
835
655
  process.exit(1);
836
656
  }