@vibest/claude-code-statusline 0.0.1

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 ADDED
@@ -0,0 +1,64 @@
1
+ # @vibest/claude-code-statusline
2
+
3
+ A customizable statusline for Claude Code CLI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add -g @vibest/claude-code-statusline
9
+ ```
10
+
11
+ Or with npm:
12
+
13
+ ```bash
14
+ npm install -g @vibest/claude-code-statusline
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ Add to `~/.claude/settings.json`:
20
+
21
+ ```json
22
+ {
23
+ "statusLine": {
24
+ "type": "command",
25
+ "command": "claude-code-statusline"
26
+ }
27
+ }
28
+ ```
29
+
30
+ ## Output
31
+
32
+ ```
33
+ [Opus 4.5] █████░░░░░ 45% ∣ $0.52
34
+ ~/Code/my-project (main +123 -45)
35
+ ```
36
+
37
+ ## Programmatic Usage
38
+
39
+ ```typescript
40
+ import {
41
+ readStdin,
42
+ modelWidget,
43
+ costWidget,
44
+ contextWidget,
45
+ projectGitWidget,
46
+ } from "@vibest/claude-code-statusline";
47
+
48
+ const data = await readStdin();
49
+ console.log(modelWidget(data));
50
+ console.log(projectGitWidget(data));
51
+ ```
52
+
53
+ ## Available Widgets
54
+
55
+ - `modelWidget` - Model name with brackets (e.g., `[Opus 4.5]`)
56
+ - `contextWidget` - Context window progress bar and percentage
57
+ - `projectGitWidget` - Project path with git branch and changes
58
+ - `costWidget` - Total cost in USD
59
+ - `directoryWidget` - Current directory
60
+ - `gitWidget` - Git branch name
61
+
62
+ ## License
63
+
64
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Claude Code Statusline CLI
4
+ */
5
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+ var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __toESM = (mod, isNodeMode, target) => {
9
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
10
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
+ for (let key of __getOwnPropNames(mod))
12
+ if (!__hasOwnProp.call(to, key))
13
+ __defProp(to, key, {
14
+ get: () => mod[key],
15
+ enumerable: true
16
+ });
17
+ return to;
18
+ };
19
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
20
+ var __require = import.meta.require;
21
+
22
+ // node_modules/picocolors/picocolors.js
23
+ var require_picocolors = __commonJS((exports, module) => {
24
+ var p = process || {};
25
+ var argv = p.argv || [];
26
+ var env = p.env || {};
27
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
28
+ var formatter = (open, close, replace = open) => (input) => {
29
+ let string = "" + input, index = string.indexOf(close, open.length);
30
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
31
+ };
32
+ var replaceClose = (string, close, replace, index) => {
33
+ let result = "", cursor = 0;
34
+ do {
35
+ result += string.substring(cursor, index) + replace;
36
+ cursor = index + close.length;
37
+ index = string.indexOf(close, cursor);
38
+ } while (~index);
39
+ return result + string.substring(cursor);
40
+ };
41
+ var createColors = (enabled = isColorSupported) => {
42
+ let f = enabled ? formatter : () => String;
43
+ return {
44
+ isColorSupported: enabled,
45
+ reset: f("\x1B[0m", "\x1B[0m"),
46
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
47
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
48
+ italic: f("\x1B[3m", "\x1B[23m"),
49
+ underline: f("\x1B[4m", "\x1B[24m"),
50
+ inverse: f("\x1B[7m", "\x1B[27m"),
51
+ hidden: f("\x1B[8m", "\x1B[28m"),
52
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
53
+ black: f("\x1B[30m", "\x1B[39m"),
54
+ red: f("\x1B[31m", "\x1B[39m"),
55
+ green: f("\x1B[32m", "\x1B[39m"),
56
+ yellow: f("\x1B[33m", "\x1B[39m"),
57
+ blue: f("\x1B[34m", "\x1B[39m"),
58
+ magenta: f("\x1B[35m", "\x1B[39m"),
59
+ cyan: f("\x1B[36m", "\x1B[39m"),
60
+ white: f("\x1B[37m", "\x1B[39m"),
61
+ gray: f("\x1B[90m", "\x1B[39m"),
62
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
63
+ bgRed: f("\x1B[41m", "\x1B[49m"),
64
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
65
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
66
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
67
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
68
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
69
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
70
+ blackBright: f("\x1B[90m", "\x1B[39m"),
71
+ redBright: f("\x1B[91m", "\x1B[39m"),
72
+ greenBright: f("\x1B[92m", "\x1B[39m"),
73
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
74
+ blueBright: f("\x1B[94m", "\x1B[39m"),
75
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
76
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
77
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
78
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
79
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
80
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
81
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
82
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
83
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
84
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
85
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
86
+ };
87
+ };
88
+ module.exports = createColors();
89
+ module.exports.createColors = createColors;
90
+ });
91
+
92
+ // src/utils.ts
93
+ async function readStdin() {
94
+ const text = await Bun.stdin.text();
95
+ return JSON.parse(text);
96
+ }
97
+ function formatCost(cost) {
98
+ if (cost === 0) {
99
+ return "$0";
100
+ }
101
+ if (cost < 0.01) {
102
+ return `$${cost.toFixed(4)}`;
103
+ }
104
+ if (cost < 1) {
105
+ return `$${cost.toFixed(2)}`;
106
+ }
107
+ return `$${cost.toFixed(2)}`;
108
+ }
109
+ function getGitBranch(cwd) {
110
+ try {
111
+ const headFile = Bun.file(`${cwd}/.git/HEAD`);
112
+ const content = __require("fs").readFileSync(`${cwd}/.git/HEAD`, "utf8").trim();
113
+ if (content.startsWith("ref: refs/heads/")) {
114
+ return content.replace("ref: refs/heads/", "");
115
+ }
116
+ return content.slice(0, 7);
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+ function truncate(str, maxLength) {
122
+ if (str.length <= maxLength) {
123
+ return str;
124
+ }
125
+ return str.slice(0, maxLength - 1) + "\u2026";
126
+ }
127
+ function shortPath(path) {
128
+ const home = process.env.HOME || process.env.USERPROFILE || "";
129
+ if (home && path.startsWith(home)) {
130
+ return "~" + path.slice(home.length);
131
+ }
132
+ return path;
133
+ }
134
+
135
+ // src/colors.ts
136
+ var import_picocolors = __toESM(require_picocolors(), 1);
137
+ var pc = import_picocolors.createColors(true);
138
+ var {
139
+ reset,
140
+ bold,
141
+ dim,
142
+ italic,
143
+ underline,
144
+ inverse,
145
+ hidden,
146
+ strikethrough,
147
+ black,
148
+ red,
149
+ green,
150
+ yellow,
151
+ blue,
152
+ magenta,
153
+ cyan,
154
+ white,
155
+ gray,
156
+ bgBlack,
157
+ bgRed,
158
+ bgGreen,
159
+ bgYellow,
160
+ bgBlue,
161
+ bgMagenta,
162
+ bgCyan,
163
+ bgWhite
164
+ } = pc;
165
+
166
+ // src/widgets.ts
167
+ function modelWidget(data) {
168
+ const name = data.model.display_name;
169
+ return "[" + bold(cyan(name)) + "]";
170
+ }
171
+ function projectGitWidget(data) {
172
+ const dir = shortPath(data.workspace.current_dir);
173
+ const branch = getGitBranch(data.cwd);
174
+ let result = blue(dir);
175
+ if (branch) {
176
+ result += " (" + magenta(truncate(branch, 20)) + ")";
177
+ }
178
+ return result;
179
+ }
180
+ function codeChangesWidget(data) {
181
+ const added = data.cost.total_lines_added;
182
+ const removed = data.cost.total_lines_removed;
183
+ if (added === 0 && removed === 0) {
184
+ return "";
185
+ }
186
+ const parts = [];
187
+ if (added > 0) {
188
+ parts.push(green(`+${added}`));
189
+ }
190
+ if (removed > 0) {
191
+ parts.push(red(`-${removed}`));
192
+ }
193
+ return parts.join(" ");
194
+ }
195
+ function costWidget(data) {
196
+ const cost = data.cost.total_cost_usd;
197
+ const formatted = formatCost(cost);
198
+ if (cost < 0.1) {
199
+ return green(formatted);
200
+ }
201
+ if (cost < 0.5) {
202
+ return yellow(formatted);
203
+ }
204
+ return red(formatted);
205
+ }
206
+ function contextWidget(data) {
207
+ const percent = data.context_window.used_percentage;
208
+ const percentInt = Math.round(percent);
209
+ const filled = Math.round(percent / 10);
210
+ const empty = 10 - filled;
211
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
212
+ if (percent < 50) {
213
+ return green(bar) + " " + green(`${percentInt}%`);
214
+ }
215
+ if (percent < 80) {
216
+ return yellow(bar) + " " + yellow(`${percentInt}%`);
217
+ }
218
+ return red(bar) + " " + red(`${percentInt}%`);
219
+ }
220
+ function separator() {
221
+ return gray(" \u2223 ");
222
+ }
223
+
224
+ // src/cli.ts
225
+ var args = process.argv.slice(2);
226
+ if (args.includes("--help") || args.includes("-h")) {
227
+ console.log(`
228
+ Claude Code Statusline
229
+
230
+ Usage:
231
+ claude-code-statusline
232
+
233
+ Options:
234
+ --help, -h Show this help message
235
+ --version, -v Show version
236
+
237
+ Configuration:
238
+ Add to ~/.claude/settings.json:
239
+
240
+ {
241
+ "statusLine": {
242
+ "type": "command",
243
+ "command": "claude-code-statusline"
244
+ }
245
+ }
246
+ `);
247
+ process.exit(0);
248
+ }
249
+ if (args.includes("--version") || args.includes("-v")) {
250
+ const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json();
251
+ console.log(pkg.version);
252
+ process.exit(0);
253
+ }
254
+ try {
255
+ const data = await readStdin();
256
+ const changes = codeChangesWidget(data);
257
+ const line1Parts = [
258
+ modelWidget(data) + " " + contextWidget(data),
259
+ costWidget(data)
260
+ ];
261
+ if (changes) {
262
+ line1Parts.push(changes);
263
+ }
264
+ const line1 = line1Parts.join(separator());
265
+ const line2 = projectGitWidget(data);
266
+ console.log(line1 + `
267
+ ` + line2);
268
+ } catch {
269
+ process.exit(1);
270
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Color Utilities - using picocolors with forced color support
3
+ */
4
+ declare const pc: import("picocolors/types").Colors;
5
+ export { pc as colors };
6
+ export declare const reset: import("picocolors/types").Formatter, bold: import("picocolors/types").Formatter, dim: import("picocolors/types").Formatter, italic: import("picocolors/types").Formatter, underline: import("picocolors/types").Formatter, inverse: import("picocolors/types").Formatter, hidden: import("picocolors/types").Formatter, strikethrough: import("picocolors/types").Formatter, black: import("picocolors/types").Formatter, red: import("picocolors/types").Formatter, green: import("picocolors/types").Formatter, yellow: import("picocolors/types").Formatter, blue: import("picocolors/types").Formatter, magenta: import("picocolors/types").Formatter, cyan: import("picocolors/types").Formatter, white: import("picocolors/types").Formatter, gray: import("picocolors/types").Formatter, bgBlack: import("picocolors/types").Formatter, bgRed: import("picocolors/types").Formatter, bgGreen: import("picocolors/types").Formatter, bgYellow: import("picocolors/types").Formatter, bgBlue: import("picocolors/types").Formatter, bgMagenta: import("picocolors/types").Formatter, bgCyan: import("picocolors/types").Formatter, bgWhite: import("picocolors/types").Formatter;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Claude Code Statusline
3
+ *
4
+ * A customizable statusline for Claude Code CLI
5
+ */
6
+ export type { StatusLineInput, Model, Workspace, Cost, ContextWindow, CurrentUsage } from "./types.ts";
7
+ export { colors } from "./colors.ts";
8
+ export { bold, dim, italic, underline, red, green, yellow, blue, magenta, cyan, white, gray, } from "./colors.ts";
9
+ export { readStdin, basename, formatCost, formatPercent, formatTokens, formatDuration, getGitBranch, truncate, } from "./utils.ts";
10
+ export { type Widget, modelWidget, directoryWidget, projectWidget, gitWidget, costWidget, contextWidget, tokensWidget, linesWidget, durationWidget, versionWidget, separator, powerlineSeparator, } from "./widgets.ts";
package/dist/index.js ADDED
@@ -0,0 +1,299 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
+ var __require = import.meta.require;
20
+
21
+ // node_modules/picocolors/picocolors.js
22
+ var require_picocolors = __commonJS((exports, module) => {
23
+ var p = process || {};
24
+ var argv = p.argv || [];
25
+ var env = p.env || {};
26
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
27
+ var formatter = (open, close, replace = open) => (input) => {
28
+ let string = "" + input, index = string.indexOf(close, open.length);
29
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
30
+ };
31
+ var replaceClose = (string, close, replace, index) => {
32
+ let result = "", cursor = 0;
33
+ do {
34
+ result += string.substring(cursor, index) + replace;
35
+ cursor = index + close.length;
36
+ index = string.indexOf(close, cursor);
37
+ } while (~index);
38
+ return result + string.substring(cursor);
39
+ };
40
+ var createColors = (enabled = isColorSupported) => {
41
+ let f = enabled ? formatter : () => String;
42
+ return {
43
+ isColorSupported: enabled,
44
+ reset: f("\x1B[0m", "\x1B[0m"),
45
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
46
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
47
+ italic: f("\x1B[3m", "\x1B[23m"),
48
+ underline: f("\x1B[4m", "\x1B[24m"),
49
+ inverse: f("\x1B[7m", "\x1B[27m"),
50
+ hidden: f("\x1B[8m", "\x1B[28m"),
51
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
52
+ black: f("\x1B[30m", "\x1B[39m"),
53
+ red: f("\x1B[31m", "\x1B[39m"),
54
+ green: f("\x1B[32m", "\x1B[39m"),
55
+ yellow: f("\x1B[33m", "\x1B[39m"),
56
+ blue: f("\x1B[34m", "\x1B[39m"),
57
+ magenta: f("\x1B[35m", "\x1B[39m"),
58
+ cyan: f("\x1B[36m", "\x1B[39m"),
59
+ white: f("\x1B[37m", "\x1B[39m"),
60
+ gray: f("\x1B[90m", "\x1B[39m"),
61
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
62
+ bgRed: f("\x1B[41m", "\x1B[49m"),
63
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
64
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
65
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
66
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
67
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
68
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
69
+ blackBright: f("\x1B[90m", "\x1B[39m"),
70
+ redBright: f("\x1B[91m", "\x1B[39m"),
71
+ greenBright: f("\x1B[92m", "\x1B[39m"),
72
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
73
+ blueBright: f("\x1B[94m", "\x1B[39m"),
74
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
75
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
76
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
77
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
78
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
79
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
80
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
81
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
82
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
83
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
84
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
85
+ };
86
+ };
87
+ module.exports = createColors();
88
+ module.exports.createColors = createColors;
89
+ });
90
+
91
+ // src/colors.ts
92
+ var import_picocolors = __toESM(require_picocolors(), 1);
93
+ var pc = import_picocolors.createColors(true);
94
+ var {
95
+ reset,
96
+ bold,
97
+ dim,
98
+ italic,
99
+ underline,
100
+ inverse,
101
+ hidden,
102
+ strikethrough,
103
+ black,
104
+ red,
105
+ green,
106
+ yellow,
107
+ blue,
108
+ magenta,
109
+ cyan,
110
+ white,
111
+ gray,
112
+ bgBlack,
113
+ bgRed,
114
+ bgGreen,
115
+ bgYellow,
116
+ bgBlue,
117
+ bgMagenta,
118
+ bgCyan,
119
+ bgWhite
120
+ } = pc;
121
+ // src/utils.ts
122
+ async function readStdin() {
123
+ const text = await Bun.stdin.text();
124
+ return JSON.parse(text);
125
+ }
126
+ function basename(path) {
127
+ return path.split("/").pop() ?? path;
128
+ }
129
+ function formatCost(cost) {
130
+ if (cost === 0) {
131
+ return "$0";
132
+ }
133
+ if (cost < 0.01) {
134
+ return `$${cost.toFixed(4)}`;
135
+ }
136
+ if (cost < 1) {
137
+ return `$${cost.toFixed(2)}`;
138
+ }
139
+ return `$${cost.toFixed(2)}`;
140
+ }
141
+ function formatPercent(value) {
142
+ return `${value.toFixed(1)}%`;
143
+ }
144
+ function formatDuration(ms) {
145
+ if (ms < 1000) {
146
+ return `${ms}ms`;
147
+ }
148
+ const seconds = ms / 1000;
149
+ if (seconds < 60) {
150
+ return `${seconds.toFixed(1)}s`;
151
+ }
152
+ const minutes = Math.floor(seconds / 60);
153
+ const remainingSeconds = Math.floor(seconds % 60);
154
+ return `${minutes}m${remainingSeconds}s`;
155
+ }
156
+ function formatTokens(tokens) {
157
+ if (tokens < 1000) {
158
+ return tokens.toString();
159
+ }
160
+ if (tokens < 1e6) {
161
+ return `${(tokens / 1000).toFixed(1)}K`;
162
+ }
163
+ return `${(tokens / 1e6).toFixed(2)}M`;
164
+ }
165
+ function getGitBranch(cwd) {
166
+ try {
167
+ const headFile = Bun.file(`${cwd}/.git/HEAD`);
168
+ const content = __require("fs").readFileSync(`${cwd}/.git/HEAD`, "utf8").trim();
169
+ if (content.startsWith("ref: refs/heads/")) {
170
+ return content.replace("ref: refs/heads/", "");
171
+ }
172
+ return content.slice(0, 7);
173
+ } catch {
174
+ return null;
175
+ }
176
+ }
177
+ function truncate(str, maxLength) {
178
+ if (str.length <= maxLength) {
179
+ return str;
180
+ }
181
+ return str.slice(0, maxLength - 1) + "\u2026";
182
+ }
183
+ // src/widgets.ts
184
+ function modelWidget(data) {
185
+ const name = data.model.display_name;
186
+ return "[" + bold(cyan(name)) + "]";
187
+ }
188
+ function directoryWidget(data) {
189
+ const dir = basename(data.workspace.current_dir);
190
+ return blue(dir);
191
+ }
192
+ function projectWidget(data) {
193
+ const project = basename(data.workspace.project_dir);
194
+ const current = basename(data.workspace.current_dir);
195
+ if (project === current) {
196
+ return blue(project);
197
+ }
198
+ return blue(`${project}/${current}`);
199
+ }
200
+ function gitWidget(data) {
201
+ const branch = getGitBranch(data.cwd);
202
+ if (!branch) {
203
+ return "";
204
+ }
205
+ return magenta(` ${truncate(branch, 20)}`);
206
+ }
207
+ function costWidget(data) {
208
+ const cost = data.cost.total_cost_usd;
209
+ const formatted = formatCost(cost);
210
+ if (cost < 0.1) {
211
+ return green(formatted);
212
+ }
213
+ if (cost < 0.5) {
214
+ return yellow(formatted);
215
+ }
216
+ return red(formatted);
217
+ }
218
+ function contextWidget(data) {
219
+ const percent = data.context_window.used_percentage;
220
+ const percentInt = Math.round(percent);
221
+ const filled = Math.round(percent / 10);
222
+ const empty = 10 - filled;
223
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
224
+ if (percent < 50) {
225
+ return green(bar) + " " + green(`${percentInt}%`);
226
+ }
227
+ if (percent < 80) {
228
+ return yellow(bar) + " " + yellow(`${percentInt}%`);
229
+ }
230
+ return red(bar) + " " + red(`${percentInt}%`);
231
+ }
232
+ function tokensWidget(data) {
233
+ const input = formatTokens(data.context_window.total_input_tokens);
234
+ const output = formatTokens(data.context_window.total_output_tokens);
235
+ return gray(`\u2193${input}`) + " " + gray(`\u2191${output}`);
236
+ }
237
+ function linesWidget(data) {
238
+ const added = data.cost.total_lines_added;
239
+ const removed = data.cost.total_lines_removed;
240
+ if (added === 0 && removed === 0) {
241
+ return "";
242
+ }
243
+ const parts = [];
244
+ if (added > 0) {
245
+ parts.push(green(`+${added}`));
246
+ }
247
+ if (removed > 0) {
248
+ parts.push(red(`-${removed}`));
249
+ }
250
+ return parts.join(" ");
251
+ }
252
+ function durationWidget(data) {
253
+ const duration = formatDuration(data.cost.total_duration_ms);
254
+ return gray(duration);
255
+ }
256
+ function versionWidget(data) {
257
+ return dim(`v${data.version}`);
258
+ }
259
+ function separator() {
260
+ return gray(" \u2223 ");
261
+ }
262
+ function powerlineSeparator() {
263
+ return gray("");
264
+ }
265
+ export {
266
+ yellow,
267
+ white,
268
+ versionWidget,
269
+ underline,
270
+ truncate,
271
+ tokensWidget,
272
+ separator,
273
+ red,
274
+ readStdin,
275
+ projectWidget,
276
+ powerlineSeparator,
277
+ modelWidget,
278
+ magenta,
279
+ linesWidget,
280
+ italic,
281
+ green,
282
+ gray,
283
+ gitWidget,
284
+ getGitBranch,
285
+ formatTokens,
286
+ formatPercent,
287
+ formatDuration,
288
+ formatCost,
289
+ durationWidget,
290
+ directoryWidget,
291
+ dim,
292
+ cyan,
293
+ costWidget,
294
+ contextWidget,
295
+ pc as colors,
296
+ bold,
297
+ blue,
298
+ basename
299
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Statusline Themes
3
+ */
4
+ import type { StatusLineInput } from "./types.ts";
5
+ export interface Theme {
6
+ name: string;
7
+ render: (data: StatusLineInput) => string;
8
+ }
9
+ /**
10
+ * Default theme - balanced information display
11
+ */
12
+ export declare const defaultTheme: Theme;
13
+ /**
14
+ * Minimal theme - just the essentials
15
+ */
16
+ export declare const minimalTheme: Theme;
17
+ /**
18
+ * Full theme - all information
19
+ */
20
+ export declare const fullTheme: Theme;
21
+ /**
22
+ * Developer theme - focus on code changes
23
+ */
24
+ export declare const devTheme: Theme;
25
+ /**
26
+ * Cost-focused theme
27
+ */
28
+ export declare const costTheme: Theme;
29
+ /**
30
+ * All available themes
31
+ */
32
+ export declare const themes: Record<string, Theme>;
33
+ /**
34
+ * Get theme by name
35
+ */
36
+ export declare function getTheme(name: string): Theme;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Claude Code Statusline Input Types
3
+ */
4
+ export interface Model {
5
+ id: string;
6
+ display_name: string;
7
+ }
8
+ export interface Workspace {
9
+ current_dir: string;
10
+ project_dir: string;
11
+ }
12
+ export interface Cost {
13
+ total_cost_usd: number;
14
+ total_duration_ms: number;
15
+ total_api_duration_ms: number;
16
+ total_lines_added: number;
17
+ total_lines_removed: number;
18
+ }
19
+ export interface CurrentUsage {
20
+ input_tokens: number;
21
+ output_tokens: number;
22
+ cache_creation_input_tokens: number;
23
+ cache_read_input_tokens: number;
24
+ }
25
+ export interface ContextWindow {
26
+ total_input_tokens: number;
27
+ total_output_tokens: number;
28
+ context_window_size: number;
29
+ used_percentage: number;
30
+ remaining_percentage: number;
31
+ current_usage: CurrentUsage | null;
32
+ }
33
+ export interface StatusLineInput {
34
+ hook_event_name: "Status";
35
+ session_id: string;
36
+ transcript_path: string;
37
+ cwd: string;
38
+ model: Model;
39
+ workspace: Workspace;
40
+ version: string;
41
+ output_style?: {
42
+ name: string;
43
+ };
44
+ cost: Cost;
45
+ context_window: ContextWindow;
46
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Utility functions
3
+ */
4
+ import type { StatusLineInput } from "./types.ts";
5
+ /**
6
+ * Read and parse JSON from stdin
7
+ */
8
+ export declare function readStdin(): Promise<StatusLineInput>;
9
+ /**
10
+ * Get basename of a path
11
+ */
12
+ export declare function basename(path: string): string;
13
+ /**
14
+ * Format cost in USD
15
+ */
16
+ export declare function formatCost(cost: number): string;
17
+ /**
18
+ * Format percentage
19
+ */
20
+ export declare function formatPercent(value: number): string;
21
+ /**
22
+ * Format duration in ms to human readable
23
+ */
24
+ export declare function formatDuration(ms: number): string;
25
+ /**
26
+ * Format token count with K/M suffix
27
+ */
28
+ export declare function formatTokens(tokens: number): string;
29
+ /**
30
+ * Get git branch name (sync, for speed)
31
+ */
32
+ export declare function getGitBranch(cwd: string): string | null;
33
+ /**
34
+ * Truncate string with ellipsis
35
+ */
36
+ export declare function truncate(str: string, maxLength: number): string;
37
+ /**
38
+ * Convert path to ~/xxx format
39
+ */
40
+ export declare function shortPath(path: string): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Statusline Widgets
3
+ */
4
+ import type { StatusLineInput } from "./types.ts";
5
+ export type Widget = (data: StatusLineInput) => string;
6
+ /**
7
+ * Model name widget
8
+ */
9
+ export declare function modelWidget(data: StatusLineInput): string;
10
+ /**
11
+ * Directory widget
12
+ */
13
+ export declare function directoryWidget(data: StatusLineInput): string;
14
+ /**
15
+ * Project directory widget
16
+ */
17
+ export declare function projectWidget(data: StatusLineInput): string;
18
+ /**
19
+ * Git branch widget (branch only)
20
+ */
21
+ export declare function gitWidget(data: StatusLineInput): string;
22
+ /**
23
+ * Project widget with git branch
24
+ */
25
+ export declare function projectGitWidget(data: StatusLineInput): string;
26
+ /**
27
+ * Code changes widget (lines added/removed by Claude)
28
+ */
29
+ export declare function codeChangesWidget(data: StatusLineInput): string;
30
+ /**
31
+ * Cost widget
32
+ */
33
+ export declare function costWidget(data: StatusLineInput): string;
34
+ /**
35
+ * Context usage widget with progress bar
36
+ */
37
+ export declare function contextWidget(data: StatusLineInput): string;
38
+ /**
39
+ * Token count widget
40
+ */
41
+ export declare function tokensWidget(data: StatusLineInput): string;
42
+ /**
43
+ * Lines changed widget (standalone, without git)
44
+ */
45
+ export declare function linesWidget(data: StatusLineInput): string;
46
+ /**
47
+ * Duration widget
48
+ */
49
+ export declare function durationWidget(data: StatusLineInput): string;
50
+ /**
51
+ * Version widget
52
+ */
53
+ export declare function versionWidget(data: StatusLineInput): string;
54
+ /**
55
+ * Separator
56
+ */
57
+ export declare function separator(): string;
58
+ /**
59
+ * Powerline separator (right arrow)
60
+ */
61
+ export declare function powerlineSeparator(): string;
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@vibest/claude-code-statusline",
3
+ "version": "0.0.1",
4
+ "description": "A customizable statusline for Claude Code CLI",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "claude-code-statusline": "./dist/cli.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "bun run build:js && bun run build:types",
23
+ "build:js": "bun build ./src/cli.ts --outdir ./dist --target bun && bun build ./src/index.ts --outdir ./dist --target bun",
24
+ "build:types": "tsc --emitDeclarationOnly --declaration --outDir dist",
25
+ "dev": "bun --watch src/cli.ts",
26
+ "test": "bun test",
27
+ "prepublishOnly": "bun run build",
28
+ "release": "bumpp"
29
+ },
30
+ "keywords": [
31
+ "claude",
32
+ "claude-code",
33
+ "statusline",
34
+ "cli",
35
+ "terminal",
36
+ "anthropic"
37
+ ],
38
+ "author": "",
39
+ "license": "MIT",
40
+ "devDependencies": {
41
+ "@types/bun": "latest",
42
+ "bumpp": "^10.4.0"
43
+ },
44
+ "peerDependencies": {
45
+ "typescript": "^5"
46
+ },
47
+ "engines": {
48
+ "bun": ">=1.0.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/vibest-dev/claude-code-statusline"
53
+ },
54
+ "dependencies": {
55
+ "picocolors": "^1.1.1"
56
+ }
57
+ }