@jeremyy_prt/cc-config 1.1.4 → 1.1.9

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
@@ -32,8 +32,13 @@ npx @jeremyy_prt/cc-config setup
32
32
  **Sécurité:**
33
33
  - Hook de validation bash (bloque `rm -rf /`, `sudo`, etc.)
34
34
 
35
+ **Statusline:**
36
+ - Branche Git avec modifications (`main*`)
37
+ - Contexte utilisé (`23%`)
38
+ - Limites d'usage (`Session: 45%`)
39
+ - Compatible **Mac ET Windows** ✅
40
+
35
41
  **Bonus:**
36
- - Statusline avec Git et coûts
37
42
  - Sons de notification
38
43
  - Hooks configurés
39
44
 
package/cli.js CHANGED
@@ -76,14 +76,25 @@ function mergeSettings() {
76
76
 
77
77
  console.log('⚙️ Configuration des settings...');
78
78
 
79
+ // Lire et adapter le settings.json selon la plateforme
80
+ let settingsContent = fs.readFileSync(srcSettings, 'utf-8');
81
+
82
+ // Remplacer le wrapper selon la plateforme
83
+ const isWindows = process.platform === 'win32';
84
+ const wrapperFile = isWindows ? 'statusline-wrapper.cmd' : 'statusline-wrapper.sh';
85
+ settingsContent = settingsContent.replace(
86
+ /statusline-wrapper\.(cmd|sh)/g,
87
+ wrapperFile
88
+ );
89
+
79
90
  if (fs.existsSync(destSettings)) {
80
91
  console.log(' ⚠️ settings.json existe déjà');
81
92
  const examplePath = path.join(CLAUDE_DIR, 'settings.example.json');
82
- fs.copyFileSync(srcSettings, examplePath);
93
+ fs.writeFileSync(examplePath, settingsContent);
83
94
  console.log(` → Copié vers settings.example.json`);
84
95
  console.log(' → Merge manuel recommandé');
85
96
  } else {
86
- fs.copyFileSync(srcSettings, destSettings);
97
+ fs.writeFileSync(destSettings, settingsContent);
87
98
  console.log(' ✓ settings.json installé');
88
99
  }
89
100
  }
@@ -303,6 +314,18 @@ function setup() {
303
314
  // Installer dépendances statusline
304
315
  installStatuslineDeps();
305
316
 
317
+ // Rendre le wrapper statusline exécutable sur Mac/Linux
318
+ if (process.platform !== 'win32') {
319
+ try {
320
+ const wrapperPath = path.join(CLAUDE_DIR, 'scripts', 'statusline-wrapper.sh');
321
+ if (fs.existsSync(wrapperPath)) {
322
+ execSync(`chmod +x "${wrapperPath}"`, { stdio: 'ignore' });
323
+ }
324
+ } catch (err) {
325
+ // Ignore errors
326
+ }
327
+ }
328
+
306
329
  // Afficher résumé
307
330
  listInstalled();
308
331
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeremyy_prt/cc-config",
3
- "version": "1.1.4",
3
+ "version": "1.1.9",
4
4
  "description": "Configuration personnalisée pour Claude Code avec commandes et agents en français",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -142,7 +142,7 @@ async function main() {
142
142
 
143
143
  await saveSession(input);
144
144
 
145
- const git = await getGitStatus();
145
+ const git = await getGitStatus(input.workspace.current_dir);
146
146
  const branch = formatBranch(git, defaultConfig.git);
147
147
  const dirPath = formatPath(
148
148
  input.workspace.current_dir,
@@ -1,4 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
2
3
 
3
4
  export interface TokenUsage {
4
5
  input_tokens: number;
@@ -23,7 +24,7 @@ export async function getContextLength(
23
24
  transcriptPath: string,
24
25
  ): Promise<number> {
25
26
  try {
26
- const content = await Bun.file(transcriptPath).text();
27
+ const content = await readFile(transcriptPath, "utf-8");
27
28
  const lines = content.trim().split("\n");
28
29
 
29
30
  if (lines.length === 0) return 0;
@@ -15,11 +15,12 @@ export interface GitStatus {
15
15
  };
16
16
  }
17
17
 
18
- function exec(command: string): { stdout: string; exitCode: number } {
18
+ function exec(command: string, cwd?: string): { stdout: string; exitCode: number } {
19
19
  try {
20
20
  const stdout = execSync(command, {
21
21
  encoding: "utf-8",
22
22
  stdio: ["pipe", "pipe", "pipe"],
23
+ cwd: cwd || process.cwd(),
23
24
  });
24
25
  return { stdout, exitCode: 0 };
25
26
  } catch (error: any) {
@@ -30,9 +31,9 @@ function exec(command: string): { stdout: string; exitCode: number } {
30
31
  }
31
32
  }
32
33
 
33
- export async function getGitStatus(): Promise<GitStatus> {
34
+ export async function getGitStatus(cwd?: string): Promise<GitStatus> {
34
35
  try {
35
- const isGitRepo = exec("git rev-parse --git-dir");
36
+ const isGitRepo = exec("git rev-parse --git-dir", cwd);
36
37
  if (isGitRepo.exitCode !== 0) {
37
38
  return {
38
39
  branch: "no-git",
@@ -42,17 +43,17 @@ export async function getGitStatus(): Promise<GitStatus> {
42
43
  };
43
44
  }
44
45
 
45
- const branchResult = exec("git branch --show-current");
46
+ const branchResult = exec("git branch --show-current", cwd);
46
47
  const branch = branchResult.stdout.trim() || "detached";
47
48
 
48
- const diffCheck = exec("git diff-index --quiet HEAD --");
49
- const cachedCheck = exec("git diff-index --quiet --cached HEAD --");
49
+ const diffCheck = exec("git diff-index --quiet HEAD --", cwd);
50
+ const cachedCheck = exec("git diff-index --quiet --cached HEAD --", cwd);
50
51
 
51
52
  if (diffCheck.exitCode !== 0 || cachedCheck.exitCode !== 0) {
52
- const unstagedDiff = exec("git diff --numstat").stdout;
53
- const stagedDiff = exec("git diff --cached --numstat").stdout;
54
- const stagedFilesResult = exec("git diff --cached --name-only").stdout;
55
- const unstagedFilesResult = exec("git diff --name-only").stdout;
53
+ const unstagedDiff = exec("git diff --numstat", cwd).stdout;
54
+ const stagedDiff = exec("git diff --cached --numstat", cwd).stdout;
55
+ const stagedFilesResult = exec("git diff --cached --name-only", cwd).stdout;
56
+ const unstagedFilesResult = exec("git diff --name-only", cwd).stdout;
56
57
 
57
58
  const parseStats = (diff: string) => {
58
59
  let added = 0;
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync } from "node:fs";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
4
5
  import type { HookInput } from "./types";
5
6
 
6
7
  export interface SpendSession {
@@ -19,7 +20,9 @@ export interface SpendData {
19
20
 
20
21
  export function getSpendFilePath(): string {
21
22
  // Use the project's data folder instead of ~/.claude
22
- const projectRoot = join(import.meta.dir, "..", "..");
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = dirname(__filename);
25
+ const projectRoot = join(__dirname, "..", "..");
23
26
  return join(projectRoot, "data", "spend.json");
24
27
  }
25
28
 
@@ -40,7 +43,9 @@ export async function loadSpendData(): Promise<SpendData> {
40
43
 
41
44
  export async function saveSpendData(data: SpendData): Promise<void> {
42
45
  const spendFile = getSpendFilePath();
43
- const projectRoot = join(import.meta.dir, "..", "..");
46
+ const __filename = fileURLToPath(import.meta.url);
47
+ const __dirname = dirname(__filename);
48
+ const projectRoot = join(__dirname, "..", "..");
44
49
  const dataDir = join(projectRoot, "data");
45
50
 
46
51
  if (!existsSync(dataDir)) {
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
3
+ import { join, dirname } from "node:path";
4
4
  import { execSync } from "node:child_process";
5
+ import { fileURLToPath } from "node:url";
5
6
 
6
7
  export interface UsageLimits {
7
8
  five_hour: {
@@ -21,10 +22,25 @@ interface CachedUsageLimits {
21
22
 
22
23
  const CACHE_DURATION_MS = 60 * 1000; // 1 minute
23
24
 
24
- function getCacheFilePath(): string {
25
- // Utiliser __dirname pour Node.js au lieu de import.meta.dir (Bun)
26
- const projectRoot = join(__dirname, "..", "..");
27
- return join(projectRoot, "data", "usage-limits-cache.json");
25
+ function getCacheFilePath(): string | null {
26
+ try {
27
+ // Utiliser import.meta.url pour ESM (compatible Node.js et Bun)
28
+ const __filename = fileURLToPath(import.meta.url);
29
+ const __dirname = dirname(__filename);
30
+ const projectRoot = join(__dirname, "..", "..");
31
+ const cacheDir = join(projectRoot, "data");
32
+
33
+ // Créer le dossier data s'il n'existe pas
34
+ if (!existsSync(cacheDir)) {
35
+ const { mkdirSync } = require("node:fs");
36
+ mkdirSync(cacheDir, { recursive: true });
37
+ }
38
+
39
+ return join(cacheDir, "usage-limits-cache.json");
40
+ } catch {
41
+ // Si erreur, désactiver le cache
42
+ return null;
43
+ }
28
44
  }
29
45
 
30
46
  interface Credentials {
@@ -39,17 +55,29 @@ interface Credentials {
39
55
 
40
56
  export async function getCredentials(): Promise<string | null> {
41
57
  try {
42
- // Cette commande ne fonctionne que sur macOS
43
- // Sur Windows, retourner null (la statusline affichera sans les limites)
44
- if (process.platform !== "darwin") {
45
- return null;
58
+ // Sur macOS : utiliser le Keychain
59
+ if (process.platform === "darwin") {
60
+ const result = execSync(
61
+ 'security find-generic-password -s "Claude Code-credentials" -w',
62
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] },
63
+ );
64
+ const creds: Credentials = JSON.parse(result.trim());
65
+ return creds.claudeAiOauth.accessToken;
46
66
  }
47
67
 
48
- const result = execSync(
49
- 'security find-generic-password -s "Claude Code-credentials" -w',
50
- { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] },
68
+ // Sur Windows/Linux : lire ~/.claude/.credentials.json
69
+ const credentialsPath = join(
70
+ process.env.HOME || process.env.USERPROFILE || "",
71
+ ".claude",
72
+ ".credentials.json",
51
73
  );
52
- const creds: Credentials = JSON.parse(result.trim());
74
+
75
+ if (!existsSync(credentialsPath)) {
76
+ return null;
77
+ }
78
+
79
+ const credentialsContent = await readFile(credentialsPath, "utf-8");
80
+ const creds: Credentials = JSON.parse(credentialsContent);
53
81
  return creds.claudeAiOauth.accessToken;
54
82
  } catch {
55
83
  return null;
@@ -90,7 +118,7 @@ export async function fetchUsageLimits(
90
118
  async function loadCache(): Promise<CachedUsageLimits | null> {
91
119
  try {
92
120
  const cacheFile = getCacheFilePath();
93
- if (!existsSync(cacheFile)) {
121
+ if (!cacheFile || !existsSync(cacheFile)) {
94
122
  return null;
95
123
  }
96
124
 
@@ -112,6 +140,10 @@ async function loadCache(): Promise<CachedUsageLimits | null> {
112
140
  async function saveCache(data: UsageLimits): Promise<void> {
113
141
  try {
114
142
  const cacheFile = getCacheFilePath();
143
+ if (!cacheFile) {
144
+ return;
145
+ }
146
+
115
147
  const cached: CachedUsageLimits = {
116
148
  data,
117
149
  timestamp: Date.now(),
@@ -0,0 +1,3 @@
1
+ @echo off
2
+ cd /d "%USERPROFILE%\.claude\scripts\statusline"
3
+ "%USERPROFILE%\.claude\scripts\statusline\node_modules\.bin\tsx.cmd" "%USERPROFILE%\.claude\scripts\statusline\src\index.ts"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ cd "$HOME/.claude/scripts/statusline"
3
+ "$HOME/.claude/scripts/statusline/node_modules/.bin/tsx" "$HOME/.claude/scripts/statusline/src/index.ts"
package/settings.json CHANGED
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "statusLine": {
16
16
  "type": "command",
17
- "command": "npx tsx ${CLAUDE_CONFIG_DIR}/scripts/statusline/src/index.ts",
17
+ "command": "${CLAUDE_CONFIG_DIR}/scripts/statusline-wrapper.cmd",
18
18
  "padding": 0
19
19
  }
20
20
  }