@jeremyy_prt/cc-config 1.1.3 → 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.3",
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;
@@ -1,4 +1,4 @@
1
- import { $ } from "bun";
1
+ import { execSync } from "node:child_process";
2
2
 
3
3
  export interface GitStatus {
4
4
  branch: string;
@@ -15,9 +15,25 @@ export interface GitStatus {
15
15
  };
16
16
  }
17
17
 
18
- export async function getGitStatus(): Promise<GitStatus> {
18
+ function exec(command: string, cwd?: string): { stdout: string; exitCode: number } {
19
19
  try {
20
- const isGitRepo = await $`git rev-parse --git-dir`.quiet().nothrow();
20
+ const stdout = execSync(command, {
21
+ encoding: "utf-8",
22
+ stdio: ["pipe", "pipe", "pipe"],
23
+ cwd: cwd || process.cwd(),
24
+ });
25
+ return { stdout, exitCode: 0 };
26
+ } catch (error: any) {
27
+ return {
28
+ stdout: error.stdout?.toString() || "",
29
+ exitCode: error.status || 1,
30
+ };
31
+ }
32
+ }
33
+
34
+ export async function getGitStatus(cwd?: string): Promise<GitStatus> {
35
+ try {
36
+ const isGitRepo = exec("git rev-parse --git-dir", cwd);
21
37
  if (isGitRepo.exitCode !== 0) {
22
38
  return {
23
39
  branch: "no-git",
@@ -27,21 +43,17 @@ export async function getGitStatus(): Promise<GitStatus> {
27
43
  };
28
44
  }
29
45
 
30
- const branchResult = await $`git branch --show-current`.quiet().text();
31
- const branch = branchResult.trim() || "detached";
46
+ const branchResult = exec("git branch --show-current", cwd);
47
+ const branch = branchResult.stdout.trim() || "detached";
32
48
 
33
- const diffCheck = await $`git diff-index --quiet HEAD --`.quiet().nothrow();
34
- const cachedCheck = await $`git diff-index --quiet --cached HEAD --`
35
- .quiet()
36
- .nothrow();
49
+ const diffCheck = exec("git diff-index --quiet HEAD --", cwd);
50
+ const cachedCheck = exec("git diff-index --quiet --cached HEAD --", cwd);
37
51
 
38
52
  if (diffCheck.exitCode !== 0 || cachedCheck.exitCode !== 0) {
39
- const unstagedDiff = await $`git diff --numstat`.quiet().text();
40
- const stagedDiff = await $`git diff --cached --numstat`.quiet().text();
41
- const stagedFilesResult = await $`git diff --cached --name-only`
42
- .quiet()
43
- .text();
44
- const unstagedFilesResult = await $`git diff --name-only`.quiet().text();
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;
45
57
 
46
58
  const parseStats = (diff: string) => {
47
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";
4
- import { $ } from "bun";
3
+ import { join, dirname } from "node:path";
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,9 +22,25 @@ interface CachedUsageLimits {
21
22
 
22
23
  const CACHE_DURATION_MS = 60 * 1000; // 1 minute
23
24
 
24
- function getCacheFilePath(): string {
25
- const projectRoot = join(import.meta.dir, "..", "..");
26
- 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
+ }
27
44
  }
28
45
 
29
46
  interface Credentials {
@@ -38,11 +55,29 @@ interface Credentials {
38
55
 
39
56
  export async function getCredentials(): Promise<string | null> {
40
57
  try {
41
- const result =
42
- await $`security find-generic-password -s "Claude Code-credentials" -w`
43
- .quiet()
44
- .text();
45
- const creds: Credentials = JSON.parse(result.trim());
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;
66
+ }
67
+
68
+ // Sur Windows/Linux : lire ~/.claude/.credentials.json
69
+ const credentialsPath = join(
70
+ process.env.HOME || process.env.USERPROFILE || "",
71
+ ".claude",
72
+ ".credentials.json",
73
+ );
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);
46
81
  return creds.claudeAiOauth.accessToken;
47
82
  } catch {
48
83
  return null;
@@ -83,7 +118,7 @@ export async function fetchUsageLimits(
83
118
  async function loadCache(): Promise<CachedUsageLimits | null> {
84
119
  try {
85
120
  const cacheFile = getCacheFilePath();
86
- if (!existsSync(cacheFile)) {
121
+ if (!cacheFile || !existsSync(cacheFile)) {
87
122
  return null;
88
123
  }
89
124
 
@@ -105,6 +140,10 @@ async function loadCache(): Promise<CachedUsageLimits | null> {
105
140
  async function saveCache(data: UsageLimits): Promise<void> {
106
141
  try {
107
142
  const cacheFile = getCacheFilePath();
143
+ if (!cacheFile) {
144
+ return;
145
+ }
146
+
108
147
  const cached: CachedUsageLimits = {
109
148
  data,
110
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
  }