claude-blip 1.0.3 → 1.0.5
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 +15 -55
- package/bin/setup.js +31 -69
- package/package.json +2 -2
- package/statusline.js +3 -3
package/README.md
CHANGED
|
@@ -2,86 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
# claude-blip
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
*Zero dependencies. One command. Just a blip.*
|
|
5
|
+
*green when fresh, yellow when warm, red when you're cooked*
|
|
7
6
|
|
|
8
7
|
[](https://www.npmjs.com/package/claude-blip)
|
|
9
8
|
[](https://packagephobia.com/result?p=claude-blip)
|
|
10
|
-
[](LICENSE)
|
|
11
9
|
|
|
12
10
|

|
|
13
11
|
|
|
14
|
-
*dim when chill, yellow when warm, red when you should probably wrap up*
|
|
15
|
-
|
|
16
12
|
</div>
|
|
17
13
|
|
|
18
|
-
Four segments. No config files. No themes. No plugins.
|
|
19
|
-
It just shows you what matters and gets out of the way.
|
|
20
|
-
|
|
21
14
|
## Install
|
|
22
15
|
|
|
23
16
|
```sh
|
|
24
17
|
npx claude-blip
|
|
25
18
|
```
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
Restart Claude Code.
|
|
28
21
|
|
|
29
|
-
##
|
|
22
|
+
## Segments
|
|
30
23
|
|
|
31
|
-
| Segment |
|
|
32
|
-
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
24
|
+
| Segment | Shows | Style |
|
|
25
|
+
|---------|-------|-------|
|
|
26
|
+
| Project | Directory name | dim |
|
|
27
|
+
| Branch | Current git branch | dim |
|
|
28
|
+
| Model | opus, sonnet, haiku | dim |
|
|
29
|
+
| Context | Usage bar + token count | green / yellow / red |
|
|
37
30
|
|
|
38
|
-
|
|
31
|
+
The context bar scales to 80% - roughly where Claude starts compressing history.
|
|
39
32
|
|
|
40
|
-
Terminal too narrow? Segments drop
|
|
33
|
+
Terminal too narrow? Segments drop from the left. Context bar stays.
|
|
41
34
|
|
|
42
|
-
##
|
|
35
|
+
## Uninstall
|
|
43
36
|
|
|
44
37
|
```sh
|
|
45
|
-
npx claude-blip
|
|
46
|
-
npx claude-blip --project # .claude/settings.json (shareable with team)
|
|
47
|
-
npx claude-blip --local # .claude/settings.local.json (just you)
|
|
48
|
-
npx claude-blip --uninstall # clean removal from all scopes
|
|
38
|
+
npx claude-blip --uninstall
|
|
49
39
|
```
|
|
50
40
|
|
|
51
41
|
## How it works
|
|
52
42
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Claude Code pipes session JSON to your statusline script via stdin. This script reads it, picks out the useful bits, formats them, writes one line to stdout. That's the whole thing.
|
|
56
|
-
|
|
57
|
-
The context bar scales to 80% capacity — that's roughly where Claude starts compressing context, so 100% on the bar means "you're about to lose history."
|
|
58
|
-
|
|
59
|
-
<details>
|
|
60
|
-
<summary><strong>Debug mode</strong></summary>
|
|
61
|
-
|
|
62
|
-
Set `debug: true` in the CONFIG object at top of `statusline.js` to dump the full JSON payload to stderr:
|
|
63
|
-
|
|
64
|
-
```js
|
|
65
|
-
const CONFIG = {
|
|
66
|
-
debug: true, // logs to stderr
|
|
67
|
-
// ...
|
|
68
|
-
};
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
</details>
|
|
72
|
-
|
|
73
|
-
## Contributing
|
|
74
|
-
|
|
75
|
-
Found a bug? Want a feature? PRs welcome. Keep it simple — the whole point is one file with zero dependencies.
|
|
43
|
+
Claude Code pipes session JSON via stdin. This script reads it, formats one line, writes it to stdout. ~140 lines of Node.js.
|
|
76
44
|
|
|
77
45
|
## License
|
|
78
46
|
|
|
79
|
-
MIT
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
<div align="center">
|
|
84
|
-
|
|
85
|
-
*Built for developers with mass context window anxiety.*
|
|
86
|
-
|
|
87
|
-
</div>
|
|
47
|
+
MIT
|
package/bin/setup.js
CHANGED
|
@@ -1,68 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// claude-blip setup
|
|
2
|
+
// claude-blip setup - one command, done.
|
|
3
3
|
//
|
|
4
4
|
// Usage:
|
|
5
|
-
// npx claude-blip (
|
|
6
|
-
// npx claude-blip --
|
|
7
|
-
// npx claude-blip --local (this project, gitignored)
|
|
8
|
-
// npx claude-blip --uninstall (remove from all scopes)
|
|
5
|
+
// npx claude-blip (install)
|
|
6
|
+
// npx claude-blip --uninstall (remove)
|
|
9
7
|
|
|
10
8
|
const fs = require("fs");
|
|
11
9
|
const path = require("path");
|
|
12
10
|
const os = require("os");
|
|
13
11
|
|
|
14
12
|
const HOOK_SOURCE = path.resolve(__dirname, "..", "statusline.js");
|
|
13
|
+
const HOOKS_DIR = path.join(os.homedir(), ".claude", "hooks");
|
|
14
|
+
const SETTINGS_PATH = path.join(os.homedir(), ".claude", "settings.json");
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
global: () => path.join(os.homedir(), ".claude", "settings.json"),
|
|
18
|
-
project: () => path.join(process.cwd(), ".claude", "settings.json"),
|
|
19
|
-
local: () => path.join(process.cwd(), ".claude", "settings.local.json"),
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const args = process.argv.slice(2);
|
|
23
|
-
const uninstall = args.includes("--uninstall");
|
|
24
|
-
const scope = args.includes("--project")
|
|
25
|
-
? "project"
|
|
26
|
-
: args.includes("--local")
|
|
27
|
-
? "local"
|
|
28
|
-
: "global";
|
|
16
|
+
const uninstall = process.argv.includes("--uninstall");
|
|
29
17
|
|
|
30
18
|
const DIM = "\x1b[2m";
|
|
31
19
|
const BOLD = "\x1b[1m";
|
|
32
20
|
const GREEN = "\x1b[32m";
|
|
33
|
-
const RED = "\x1b[31m";
|
|
34
21
|
const RESET = "\x1b[0m";
|
|
35
22
|
|
|
36
23
|
function log(msg) {
|
|
37
24
|
console.log(` ${msg}`);
|
|
38
25
|
}
|
|
39
26
|
|
|
40
|
-
function getInstallDir() {
|
|
41
|
-
if (scope === "global") {
|
|
42
|
-
return path.join(os.homedir(), ".claude", "hooks");
|
|
43
|
-
}
|
|
44
|
-
return path.join(process.cwd(), ".claude", "hooks");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
27
|
function install() {
|
|
48
|
-
const
|
|
49
|
-
const dest = path.join(installDir, "statusline.js");
|
|
50
|
-
const settingsPath = SCOPES[scope]();
|
|
51
|
-
const settingsDir = path.dirname(settingsPath);
|
|
28
|
+
const dest = path.join(HOOKS_DIR, "statusline.js");
|
|
52
29
|
|
|
53
30
|
// 1. Copy the statusline script
|
|
54
|
-
fs.mkdirSync(
|
|
31
|
+
fs.mkdirSync(HOOKS_DIR, { recursive: true });
|
|
55
32
|
fs.copyFileSync(HOOK_SOURCE, dest);
|
|
56
33
|
fs.chmodSync(dest, 0o755);
|
|
57
34
|
|
|
58
35
|
// 2. Update settings.json
|
|
59
|
-
fs.mkdirSync(settingsDir, { recursive: true });
|
|
60
36
|
let settings = {};
|
|
61
|
-
if (fs.existsSync(
|
|
37
|
+
if (fs.existsSync(SETTINGS_PATH)) {
|
|
62
38
|
try {
|
|
63
|
-
settings = JSON.parse(fs.readFileSync(
|
|
39
|
+
settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, "utf8"));
|
|
64
40
|
} catch {
|
|
65
|
-
// Corrupted settings
|
|
41
|
+
// Corrupted settings - start fresh
|
|
66
42
|
}
|
|
67
43
|
}
|
|
68
44
|
|
|
@@ -71,65 +47,51 @@ function install() {
|
|
|
71
47
|
command: dest,
|
|
72
48
|
};
|
|
73
49
|
|
|
74
|
-
fs.writeFileSync(
|
|
50
|
+
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
|
|
75
51
|
|
|
76
52
|
console.log();
|
|
77
53
|
log(`${GREEN}${BOLD}blip${RESET} ${DIM}installed${RESET}`);
|
|
78
|
-
log(`${DIM}scope: ${RESET}${scope}`);
|
|
79
54
|
log(`${DIM}hook: ${RESET}${dest}`);
|
|
80
|
-
log(`${DIM}config: ${RESET}${
|
|
55
|
+
log(`${DIM}config: ${RESET}${SETTINGS_PATH}`);
|
|
81
56
|
console.log();
|
|
82
57
|
log(`${DIM}Restart Claude Code to see it.${RESET}`);
|
|
83
58
|
console.log();
|
|
84
59
|
}
|
|
85
60
|
|
|
86
|
-
function removeStatusLine(settingsPath) {
|
|
87
|
-
if (!fs.existsSync(settingsPath)) return false;
|
|
88
|
-
try {
|
|
89
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
90
|
-
if (!settings.statusLine) return false;
|
|
91
|
-
delete settings.statusLine;
|
|
92
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
93
|
-
return true;
|
|
94
|
-
} catch {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
61
|
function uninstallAll() {
|
|
100
62
|
let removed = false;
|
|
101
63
|
|
|
102
|
-
// Remove from
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
64
|
+
// Remove statusLine from global settings
|
|
65
|
+
if (fs.existsSync(SETTINGS_PATH)) {
|
|
66
|
+
try {
|
|
67
|
+
const settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, "utf8"));
|
|
68
|
+
if (settings.statusLine) {
|
|
69
|
+
delete settings.statusLine;
|
|
70
|
+
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
|
|
71
|
+
removed = true;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// ignore
|
|
107
75
|
}
|
|
108
76
|
}
|
|
109
77
|
|
|
110
|
-
// Remove hook
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
for (const loc of locations) {
|
|
116
|
-
if (fs.existsSync(loc)) {
|
|
117
|
-
fs.unlinkSync(loc);
|
|
118
|
-
log(`${DIM}Removed ${loc}${RESET}`);
|
|
119
|
-
removed = true;
|
|
120
|
-
}
|
|
78
|
+
// Remove hook file
|
|
79
|
+
const hookPath = path.join(HOOKS_DIR, "statusline.js");
|
|
80
|
+
if (fs.existsSync(hookPath)) {
|
|
81
|
+
fs.unlinkSync(hookPath);
|
|
82
|
+
removed = true;
|
|
121
83
|
}
|
|
122
84
|
|
|
123
85
|
console.log();
|
|
124
86
|
if (removed) {
|
|
125
87
|
log(`${GREEN}${BOLD}blip${RESET} ${DIM}uninstalled${RESET}`);
|
|
126
88
|
} else {
|
|
127
|
-
log(`${DIM}Nothing to remove
|
|
89
|
+
log(`${DIM}Nothing to remove - blip wasn't installed.${RESET}`);
|
|
128
90
|
}
|
|
129
91
|
console.log();
|
|
130
92
|
}
|
|
131
93
|
|
|
132
|
-
//
|
|
94
|
+
// -----------------------------------------------------------------
|
|
133
95
|
|
|
134
96
|
if (uninstall) {
|
|
135
97
|
uninstallAll();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-blip",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "A minimal statusline for Claude Code",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "davebream",
|
|
7
7
|
"repository": {
|
package/statusline.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// claude-blip
|
|
2
|
+
// claude-blip - a single-file statusline for Claude Code
|
|
3
3
|
// Shows: project │ branch │ model │ context usage
|
|
4
4
|
|
|
5
5
|
const { execSync } = require("child_process");
|
|
@@ -117,7 +117,7 @@ function buildStatusline(input) {
|
|
|
117
117
|
parts.push(ctx);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// Truncate if wider than terminal
|
|
120
|
+
// Truncate if wider than terminal - drops segments from the left
|
|
121
121
|
const sep = dim(" \u00B7 ");
|
|
122
122
|
const cols = process.stdout.columns || 80;
|
|
123
123
|
while (parts.length > 1 && stripAnsi(parts.join(sep)).length > cols) {
|
|
@@ -138,6 +138,6 @@ process.stdin.on("end", () => {
|
|
|
138
138
|
const output = buildStatusline(input);
|
|
139
139
|
process.stdout.write(output);
|
|
140
140
|
} catch {
|
|
141
|
-
// Silent fail
|
|
141
|
+
// Silent fail - a broken statusline should never interrupt your flow
|
|
142
142
|
}
|
|
143
143
|
});
|