claude-friends 0.4.9 → 0.4.11

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/statusline.js +37 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-friends",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "description": "See who's online in Claude Code. Add friends, share status, nudge each other.",
5
5
  "type": "module",
6
6
  "bin": {
package/statusline.js CHANGED
@@ -7,6 +7,20 @@ import { join, basename } from "path";
7
7
  import { homedir } from "os";
8
8
  import { execSync } from "child_process";
9
9
 
10
+ // ANSI colors
11
+ const RESET = "\x1b[0m";
12
+ const BOLD = "\x1b[1m";
13
+ const DIM = "\x1b[2m";
14
+ const GRAY = "\x1b[90m";
15
+ const WHITE = "\x1b[97m";
16
+ const CYAN = "\x1b[36m";
17
+ const GREEN = "\x1b[32m";
18
+ const YELLOW = "\x1b[33m";
19
+ const MAGENTA = "\x1b[35m";
20
+ const BLUE = "\x1b[34m";
21
+
22
+ const SEP = ` ${GRAY}|${RESET} `;
23
+
10
24
  // Read JSON from stdin
11
25
  let input = "";
12
26
  try {
@@ -23,7 +37,7 @@ const segments = [];
23
37
  // 1. Project name
24
38
  const projectDir = data.workspace?.project_dir || data.cwd || "";
25
39
  if (projectDir) {
26
- segments.push(basename(projectDir));
40
+ segments.push(`${WHITE}${basename(projectDir)}${RESET}`);
27
41
  }
28
42
 
29
43
  // 2. Git branch
@@ -33,7 +47,7 @@ try {
33
47
  stdio: ["pipe", "pipe", "pipe"],
34
48
  }).toString().trim();
35
49
  if (branch) {
36
- segments.push(`\u2387 ${branch}`);
50
+ segments.push(`${MAGENTA}\u2387 ${branch}${RESET}`);
37
51
  }
38
52
  } catch {}
39
53
 
@@ -47,7 +61,7 @@ if (data.model) {
47
61
  .replace("opus-4-6", "Opus 4.6")
48
62
  .replace("sonnet-4-6", "Sonnet 4.6")
49
63
  .replace("haiku-4-5-20251001", "Haiku 4.5");
50
- segments.push(`\u{1F916} ${short}`);
64
+ segments.push(`${CYAN}\u{1F916} ${short}${RESET}`);
51
65
  }
52
66
 
53
67
  // 4. Tokens
@@ -55,7 +69,7 @@ const totalIn = data.context_window?.total_input_tokens;
55
69
  const totalOut = data.context_window?.total_output_tokens;
56
70
  if (totalIn != null || totalOut != null) {
57
71
  const total = (totalIn || 0) + (totalOut || 0);
58
- segments.push(`${formatNum(total)} tokens`);
72
+ segments.push(`${BLUE}${formatNum(total)} tokens${RESET}`);
59
73
  }
60
74
 
61
75
  // 5. Tool calls (grep count only — never reads conversation content)
@@ -66,30 +80,41 @@ if (data.transcript_path) {
66
80
  { stdio: ["pipe", "pipe", "pipe"] }
67
81
  ).toString().trim();
68
82
  const n = parseInt(count, 10);
69
- if (n > 0) segments.push(`\u{1F527} ${n}`);
83
+ if (n > 0) segments.push(`${YELLOW}\u{1F527} ${n}${RESET}`);
70
84
  } catch {}
71
85
  }
72
86
 
73
87
  // 6. Cost
74
88
  if (data.cost?.total_cost_usd != null) {
75
- segments.push(`$${data.cost.total_cost_usd.toFixed(2)}`);
89
+ const cost = data.cost.total_cost_usd;
90
+ const color = cost > 10 ? YELLOW : GREEN;
91
+ segments.push(`${color}$${cost.toFixed(2)}${RESET}`);
76
92
  }
77
93
 
78
- // 6. Streak (based on session file dates, not contents)
79
- segments.push(`\u{1F525} ${getStreak()}d`);
94
+ // 7. Streak
95
+ const streak = getStreak();
96
+ segments.push(`\u{1F525} ${streak}d`);
80
97
 
81
98
  // 8. Friends online
82
99
  const friends = getFriendsOnline();
100
+ const onlineColor = friends.count > 0 ? GREEN : GRAY;
83
101
  const dot = friends.count > 0 ? "\u{1F7E2}" : "\u25CB";
84
102
  const names = friends.names.length > 0
85
103
  ? ` (${friends.names.slice(0, 3).join(", ")}${friends.names.length > 3 ? "\u2026" : ""})`
86
104
  : "";
87
- segments.push(`${dot} ${friends.count} online${names}`);
105
+ segments.push(`${onlineColor}${dot} ${friends.count} online${names}${RESET}`);
88
106
 
89
- process.stdout.write(segments.join(" | "));
107
+ process.stdout.write(segments.join(SEP));
90
108
 
91
109
  // --- Helpers ---
92
110
 
111
+ function localDateStr(d) {
112
+ const y = d.getFullYear();
113
+ const m = String(d.getMonth() + 1).padStart(2, "0");
114
+ const day = String(d.getDate()).padStart(2, "0");
115
+ return `${y}-${m}-${day}`;
116
+ }
117
+
93
118
  function formatNum(n) {
94
119
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
95
120
  if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
@@ -107,7 +132,6 @@ function getFriendsOnline() {
107
132
  }
108
133
 
109
134
  function getStreak() {
110
- // Collect dates of all session file modifications
111
135
  const sessionsDir = join(homedir(), ".claude", "projects");
112
136
  try {
113
137
  if (!existsSync(sessionsDir)) return 0;
@@ -119,7 +143,7 @@ function getStreak() {
119
143
  for (let i = 0; i < 365; i++) {
120
144
  const d = new Date(today);
121
145
  d.setDate(d.getDate() - i);
122
- const dateStr = d.toISOString().slice(0, 10);
146
+ const dateStr = localDateStr(d);
123
147
  if (activeDates.has(dateStr)) {
124
148
  streak++;
125
149
  } else if (i > 0) {
@@ -142,7 +166,7 @@ function scanForDates(dir, dates, depth) {
142
166
  } else if (entry.name.endsWith(".jsonl")) {
143
167
  try {
144
168
  const mtime = statSync(full).mtime;
145
- dates.add(mtime.toISOString().slice(0, 10));
169
+ dates.add(localDateStr(mtime));
146
170
  } catch {}
147
171
  }
148
172
  }