@hasna/statusline 0.0.1 → 0.0.2

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
@@ -8,7 +8,7 @@ and more — rendered as a single line for your agent's status bar.
8
8
  apple03 · statusline (main) · 12h · 1.2k · fable 5 [1m] · 90% left · $0.04
9
9
  ```
10
10
 
11
- Zero runtime dependencies. Built with [Bun](https://bun.sh) + TypeScript. Apache-2.0.
11
+ Built with [Bun](https://bun.sh) + TypeScript. The CLI renderer is dependency-light; the optional MCP server uses the Model Context Protocol SDK. Apache-2.0.
12
12
 
13
13
  ## Install
14
14
 
@@ -21,32 +21,92 @@ That's it — Claude Code picks it up on the next status refresh (no restart nee
21
21
 
22
22
  ## Segments
23
23
 
24
- `statusline list` shows everything available:
24
+ `statusline list` shows a compact, enabled-first summary of your segments (use
25
+ `statusline list --all` for every row). The table below is the full catalog — all 18
26
+ segment ids from `src/segments/index.ts`.
27
+
28
+ `renderLine` walks your configured segment order, renders each one, and **drops**
29
+ anything that returns `null` or throws. A failed segment never breaks the host UI.
30
+
31
+ | Segment | Default | Description | Data source | Omit when | Example |
32
+ |---------|---------|-------------|-------------|-----------|---------|
33
+ | `machine` | on | Machine hostname (short) | `os.hostname()` — first label before `.` | never (always renders) | `apple03` |
34
+ | `project` | on | Project name with current Git branch | `gitProjectName(cwd)` + `gitBranch(cwd)`; falls back to cwd basename | no project name resolvable | `statusline (main)` |
35
+ | `project-name` | off | Project name only | `gitProjectName(cwd)` or cwd basename | outside a git repo with no name | `statusline` |
36
+ | `git-branch` | off | Current Git branch | `git branch --show-current` in `cwd` | outside git or detached | `main` |
37
+ | `commit-age` | on | Time since last commit (compact) | `git log -1` epoch vs now | no commits / not a repo | `12h` |
38
+ | `loc` | on | Lines of code tracked by Git (compact) | `git ls-files` + `wc -l` in repo root | outside git or zero lines | `1.2k` |
39
+ | `current-dir` | off | Current working directory (basename) | `StatusContext.cwd` | never (always renders basename) | `open-statusline` |
40
+ | `model` | off | Current model name (no context tag) | `StatusContext.model.id` via friendly formatter | no model id | `fable 5` |
41
+ | `model-context` | on | Model name with context-size tag | same formatter, keeps `[tag]` suffix | no model id | `fable 5 [1m]` |
42
+ | `context-used` | off | Percentage of context window used | session transcript JSONL (`contextUsage`) | transcript missing/unreadable | `10%` |
43
+ | `context-remaining` | on | Percentage of context window remaining | transcript via `contextUsage` | transcript missing/unreadable | `90% left` |
44
+ | `used-tokens` | off | Total tokens in the context window | transcript usage block (input + output) | transcript missing/unreadable | `102k tok` |
45
+ | `cost` | on | Session cost in USD | `StatusContext.cost.totalCostUsd` | cost is zero or missing | `$1,234.50` |
46
+ | `duration` | off | Session wall-clock duration | `StatusContext.cost.totalDurationMs` | duration missing | `1h30m` |
47
+ | `lines-changed` | off | Lines added/removed this session | `cost.totalLinesAdded/Removed` | both zero | `+142/-18` |
48
+ | `output-style` | off | Active output style | `StatusContext.outputStyle` | style is `default` or missing | `concise` |
49
+ | `agent-version` | off | Host agent version | `StatusContext.version` | version missing | `v2.1.39` |
50
+ | `session-id` | off | Session identifier (short) | first UUID segment of `sessionId` | no session id | `abc12345` |
51
+
52
+ ### Context segments and transcripts
53
+
54
+ Segments that report context usage (`context-used`, `context-remaining`, `used-tokens`)
55
+ read the session transcript path from the provider payload. `contextUsage` scans the
56
+ JSONL for the last assistant entry with a `usage` block and sums
57
+ `input_tokens + cache_creation_input_tokens + cache_read_input_tokens`. Window size is
58
+ **1,000,000** when the model id contains `[1m]`, otherwise **200,000**.
59
+
60
+ With the test fixture transcript (100k input-side tokens on a 1m window), expect
61
+ `context-used` → `10%`, `context-remaining` → `90% left`, `used-tokens` → `102k tok`.
62
+
63
+ ### Quick reference from `statusline list`
64
+
65
+ `statusline list` is compact by default so it stays friendly in agent terminals —
66
+ enabled segments first, capped rows, and hints for the detail paths:
25
67
 
26
68
  ```
27
- [x] machine Machine hostname (short)
28
- [x] project Project name with current Git branch
29
- [ ] project-name Project name (omitted when unavailable)
30
- [ ] git-branch Current Git branch (omitted when unavailable)
31
- [x] commit-age Time since last commit (compact)
32
- [x] loc Lines of code tracked by Git (compact)
33
- [ ] current-dir Current working directory (basename)
34
- [ ] model Current model name
35
- [x] model-context Model name with context-size tag (e.g. [1m])
36
- [ ] context-used Percentage of context window used (omitted when unknown)
37
- [x] context-remaining Percentage of context window remaining (omitted when unknown)
38
- [ ] used-tokens Total tokens in the context window (omitted when unknown)
39
- [x] cost Session cost in USD (omitted when zero)
40
- [ ] duration Session wall-clock duration
41
- [ ] lines-changed Lines added/removed this session (omitted when zero)
42
- [ ] output-style Active output style (omitted when default)
43
- [ ] agent-version Host agent version (e.g. Claude Code version)
44
- [ ] session-id Session identifier (short)
69
+ Segments: 7 enabled / 18 total (showing 12 of 18)
70
+ state default id
71
+ on yes machine
72
+ on yes project
73
+ on yes commit-age
74
+ on yes loc
75
+ on yes model-context
76
+ on yes context-remaining
77
+ on yes cost
78
+ off no project-name
79
+ off no git-branch
80
+ off no current-dir
81
+ off no model
82
+ off no context-used
83
+ Hint: use `--all` for all matching rows, `statusline show <id>` for details, `--verbose` for descriptions, `--json` for machines.
45
84
  ```
46
85
 
86
+ Use gradual disclosure when you need more:
87
+
88
+ ```bash
89
+ statusline list --verbose # include descriptions, still capped
90
+ statusline list --all --verbose # include every row with descriptions
91
+ statusline list --all # show every segment row
92
+ statusline list --limit 5 # cap rows explicitly
93
+ statusline list --enabled # only enabled segments
94
+ statusline list --disabled # only disabled segments
95
+ statusline list --search token # filter by id or description
96
+ statusline list --json # structured output for tooling
97
+ statusline show model-context # full details for one segment
98
+ statusline inspect used-tokens --json # JSON detail path
99
+ ```
100
+
101
+ `statusline list --json` returns `{ total, matching, showing, limited, segments, next }`.
102
+ Each segment has `{ id, description, enabled, defaultEnabled }`; `next` is `null`
103
+ unless the result is limited. Column padding is display-only — it does not affect
104
+ statusbar width.
105
+
47
106
  ## Usage
48
107
 
49
108
  ```bash
109
+ statusline list # compact segment summary
50
110
  statusline enable used-tokens duration # turn segments on (appended at the end)
51
111
  statusline disable loc # turn segments off
52
112
  statusline order machine project model-context cost # exact order = enabled set
@@ -64,6 +124,23 @@ Config lives at `~/.config/statusline/config.json` (override with `$STATUSLINE_C
64
124
  }
65
125
  ```
66
126
 
127
+ ## Theming and layout
128
+
129
+ There is no fixed width knob — the rendered line grows with enabled segments. Control
130
+ length practically:
131
+
132
+ 1. **Fewer segments** — `statusline disable` segments you do not need, or `statusline order` to pick a compact set.
133
+ 2. **Shorter separator** — `statusline separator "·"` or `" | "` (default is `" · "`).
134
+ 3. **Split project info** — use `project-name` + `git-branch` instead of combined `project` if you want to omit the branch when unknown.
135
+ 4. **Rely on omit-when-null** — segments like `cost`, `context-remaining`, and `lines-changed` disappear when data is unavailable instead of showing placeholders.
136
+
137
+ **Segment order matters.** `statusline order a b c` sets both the display order and the
138
+ exact enabled set — segments not listed are disabled. `statusline enable` appends new
139
+ ids to the end; `statusline disable` removes them.
140
+
141
+ **Config path:** `~/.config/statusline/config.json`, overridable with `$STATUSLINE_CONFIG`.
142
+ `statusline reset` restores `defaultConfig()` (separator + default segment list above).
143
+
67
144
  ## How it works
68
145
 
69
146
  Claude Code pipes a JSON payload (cwd, model, session cost, transcript path, …) to the
@@ -72,11 +149,103 @@ renders each enabled segment, drops anything unavailable, and prints one line. S
72
149
  that need more than the payload offers (e.g. `context-remaining`) read the session
73
150
  transcript to compute it. A segment failure is never fatal — it's simply omitted.
74
151
 
152
+ ## SDK
153
+
154
+ The package root is a side-effect-free SDK surface:
155
+
156
+ ```ts
157
+ import {
158
+ renderStatusline,
159
+ listSegments,
160
+ enableSegments,
161
+ saveConfig,
162
+ loadConfig,
163
+ } from "@hasna/statusline";
164
+
165
+ const config = loadConfig();
166
+ const line = await renderStatusline({ cwd: process.cwd() }, config);
167
+ const next = enableSegments(["duration"], config).config;
168
+ saveConfig(next);
169
+ ```
170
+
171
+ Useful exports include `parseClaudeInput`, `renderLine`, `segments`, `getSegment`,
172
+ `defaultConfig`, `loadConfig`, `saveConfig`, `configPath`, `previewStatusline`,
173
+ `orderSegments`, `disableSegments`, `setSeparator`, and `installClaude`.
174
+
175
+ ## MCP
176
+
177
+ `statusline-mcp` starts a stdio MCP server:
178
+
179
+ ```bash
180
+ statusline-mcp
181
+ ```
182
+
183
+ It exposes safe tools for `render_statusline`, `preview_statusline`,
184
+ `list_segments`, `get_config`, `update_config`, `enable_segments`,
185
+ `disable_segments`, `order_segments`, and `reset_config`. Config mutation tools
186
+ require `confirm_write: true`; every mutation tool supports `dry_run: true`.
187
+
188
+ The MCP server intentionally does not mutate Claude settings. Use
189
+ `statusline install claude` or the `installClaude()` SDK helper when you
190
+ explicitly want to wire the CLI into Claude Code.
191
+
75
192
  ## Providers
76
193
 
194
+ ### Claude Code (implemented)
195
+
196
+ ```bash
197
+ bun install -g @hasna/statusline
198
+ statusline install claude
199
+ ```
200
+
201
+ `installClaude()` writes to `~/.claude/settings.json`:
202
+
203
+ ```json
204
+ {
205
+ "statusLine": { "type": "command", "command": "statusline render" }
206
+ }
207
+ ```
208
+
209
+ Existing settings are preserved; the previous file is backed up as
210
+ `settings.json.bak-statusline`. Claude Code pipes JSON on stdin to `statusline render`
211
+ on every status refresh — **no restart needed**.
212
+
213
+ **stdin fields** parsed by `parseClaudeInput` (`src/providers/claude.ts`):
214
+
215
+ | Payload field | Maps to `StatusContext` |
216
+ |---------------|-------------------------|
217
+ | `cwd` / `workspace.current_dir` | `cwd` |
218
+ | `workspace.project_dir` | `projectDir` |
219
+ | `model.id`, `model.display_name` | `model.id`, `model.displayName` |
220
+ | `cost.total_cost_usd` | `cost.totalCostUsd` |
221
+ | `cost.total_duration_ms` | `cost.totalDurationMs` |
222
+ | `cost.total_lines_added/removed` | `cost.totalLinesAdded/Removed` |
223
+ | `transcript_path` | `transcriptPath` |
224
+ | `session_id` | `sessionId` |
225
+ | `version` | `version` |
226
+ | `output_style.name` | `outputStyle` |
227
+
228
+ Every field is optional — partial or empty payloads still produce a usable context.
229
+
230
+ ### Codewith (not implemented)
231
+
232
+ `statusline install` only accepts `claude` today. Running
233
+ `statusline install codewith` errors with *unsupported target* (`src/cli.ts`).
234
+
235
+ The renderer is provider-agnostic: a future `parseCodewithInput` would map Codewith's
236
+ status payload into `StatusContext` (`src/providers/types.ts`) and reuse the same
237
+ segment registry. No install wiring exists yet (tracked in STA-00003).
238
+
239
+ ### Cursor (not implemented)
240
+
241
+ Same gap as Codewith — no `statusline install cursor` subcommand and no Cursor-specific
242
+ parser. A future adapter would implement `parseCursorInput` → `StatusContext` and hook
243
+ into Cursor's status bar the same way Claude does (tracked in STA-00004).
244
+
245
+ ### Other agents
246
+
77
247
  | Agent | Status |
78
248
  |-------|--------|
79
- | Claude Code | ✅ `statusline install claude` |
80
249
  | OpenCode | ⏳ no statusline/footer hook in its config as of v1.3.x — the renderer is provider-agnostic (`src/providers/`), so an adapter slots in the moment one exists |
81
250
  | Codex CLI | Codex ships its own built-in segment picker; no external command hook |
82
251
 
@@ -87,8 +256,16 @@ Adding a provider means one small parser: agent payload → `StatusContext`
87
256
 
88
257
  ```bash
89
258
  bun install
259
+ bun run typecheck
90
260
  bun test
91
- echo '{"cwd":"'$PWD'","model":{"id":"claude-fable-5[1m]"}}' | bun src/index.ts render
261
+ bun run build
262
+ echo '{"cwd":"'$PWD'","model":{"id":"claude-fable-5[1m]"}}' | bun src/cli.ts render
263
+ ```
264
+
265
+ Spot-check fixture output:
266
+
267
+ ```bash
268
+ cat test/fixtures/claude-input.json | bun src/cli.ts render
92
269
  ```
93
270
 
94
271
  ## License
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export declare function runCli(argv?: string[]): Promise<void>;