lumira 1.13.0 → 1.14.0
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +24 -0
- package/dist/commands/subagent.js +129 -0
- package/dist/index.js +15 -0
- package/dist/installer.js +58 -31
- package/package.json +1 -1
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "Real-time statusline HUD for Claude Code and Qwen Code — analytics, quota projection, themes, powerline",
|
|
8
|
-
"version": "1.
|
|
8
|
+
"version": "1.14.0"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "lumira",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Real-time statusline HUD for Claude Code and Qwen Code. Session analytics, API latency widget, 7-day quota projection, auto-compact warnings, 7 themes, powerline support. Zero runtime dependencies. Run /lumira:setup after install to activate.",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.14.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "Carlos Cativo"
|
|
18
18
|
},
|
|
@@ -32,5 +32,5 @@
|
|
|
32
32
|
]
|
|
33
33
|
}
|
|
34
34
|
],
|
|
35
|
-
"version": "1.
|
|
35
|
+
"version": "1.14.0"
|
|
36
36
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lumira",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Real-time statusline HUD for Claude Code and Qwen Code — session analytics, API latency, 7-day quota projection, auto-compact warnings, 7 themes, powerline. Zero runtime deps.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Carlos Cativo"
|
package/README.md
CHANGED
|
@@ -111,6 +111,7 @@ See [`docs/competitive-comparison.md`](docs/competitive-comparison.md) for the f
|
|
|
111
111
|
- **Pace delta** — `usedPct − elapsedPct` of the 5h window. Turtle when behind pace (healthy), car with time-to-exhaustion when ahead. Color escalates green → yellow → orange → blinkRed. Toggle independently via `display.paceDelta`.
|
|
112
112
|
- **7d quota projection** — when the current burn rate would exhaust the 7d quota before the window resets, the 7d segment grows a warning: `⚠ ~24h`, `⚠ ~2d`, `⚠ Tue`, or `🔥 ~8h` (critical icon under 12h). Default on. Toggle via `display.quotaProjection`; off in the `minimal` preset. Different from pace delta — pace looks backwards at the 5h window's actual vs proportional burn, projection looks forwards at the 7d window's exhaustion ETA.
|
|
113
113
|
- **Active agents** — live count of running subagents (`⚡N agents`) plus types parsed from the transcript. Toggle via `display.agents`.
|
|
114
|
+
- **Subagent panel rows** — custom rendering for each row in Claude Code's agent panel via [`lumira subagent`](#subagent-rows): a state glyph (running / done / error), what each agent is doing, and its token count, in your configured theme and icon set. Opt-in at install.
|
|
114
115
|
- **Cache hit rate** — prompt cache efficiency for the current turn (`87%⚡`). Alarm-mode: hidden while ≥90% (Anthropic's prompt cache pins this near 99% in healthy steady state, so an always-on number is wallpaper, not signal). Surfaces as yellow/orange/blinkRed only when the cache is actually degrading. Same hide-when-healthy pattern as rate-limits and agent count.
|
|
115
116
|
- **GSD integration** — current task and update notifications (opt-in).
|
|
116
117
|
- **Config health widget** — surfaces silent fallbacks (theme/powerline degrading in named-ANSI, missing GSD STATE.md). Opt-in.
|
|
@@ -259,6 +260,29 @@ lumira stats
|
|
|
259
260
|
|
|
260
261
|
**Qwen Code sessions** are parsed the same way, but cost and burn-rate lines are suppressed when the transcript lacks usage blocks (`hasCostData: false` in the JSON output) — no misleading `$0.00`.
|
|
261
262
|
|
|
263
|
+
## Subagent rows
|
|
264
|
+
|
|
265
|
+
Claude Code (≥ 2.1.x) exposes a [`subagentStatusLine`](https://code.claude.com/docs/en/statusline#subagent-status-lines) hook that lets a command customize how each subagent renders in the agent panel — replacing the default `name · description · token count` row. `lumira subagent` reads the visible tasks from stdin and renders each row in your configured theme and icon set:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
reviewing auth module · 15k tok · running
|
|
269
|
+
✓ exploring the codebase · 3k tok · done
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The **glyph and colour are driven by state** — running (cyan clock), done (green check), error (red warning) — so a glance tells you what's live, what finished, what failed.
|
|
273
|
+
|
|
274
|
+
The **row label** identifies each agent. In practice Claude Code reports every Task subagent with the generic `type: "local_agent"` and no `name`, so the per-agent `description` is the only field that distinguishes one row from another — lumira uses it. The full fallback is `name → a meaningful type → description → id`: an explicit `name` or a real agent type wins when Claude Code provides one, otherwise the `description` carries the row. Rows respect your `icons` (`nerd`/`emoji`/`none`) and `colors.mode` config, and are truncated to the width Claude Code budgets.
|
|
275
|
+
|
|
276
|
+
**Enable it:** the installer offers to register `subagentStatusLine` alongside the main statusline (opt-in — it never writes the second key without asking). To wire it manually, add to `~/.claude/settings.json`:
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"subagentStatusLine": { "type": "command", "command": "lumira subagent", "padding": 0 }
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
It's robust by design: on any unreadable or malformed payload it emits nothing and exits 0, so Claude Code falls back to its default rows — a bad render never breaks the panel. Set `LUMIRA_DEBUG=1` to log the raw payload Claude Code sends to stderr.
|
|
285
|
+
|
|
262
286
|
## Powerline
|
|
263
287
|
|
|
264
288
|
`style: "powerline"` (or `--powerline`) renders the statusline with colored segment backgrounds and glyph separators inspired by powerline-go / oh-my-posh. Available separator presets via `powerline.style` (or `--powerline-style=<name>`):
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lumira subagent` subcommand (issue #176 — `subagentStatusLine` renderer).
|
|
3
|
+
*
|
|
4
|
+
* Claude Code (≥ 2.1.x) lets a command customize how each subagent row renders
|
|
5
|
+
* in the agent panel. CC pipes a JSON object on stdin describing every visible
|
|
6
|
+
* subagent and expects ONE JSON line back per row (`{ id, content }`). Omitting
|
|
7
|
+
* an id keeps CC's default rendering for that row; empty content hides it.
|
|
8
|
+
*
|
|
9
|
+
* Input schema (distinct from the main statusline's `RawInput`):
|
|
10
|
+
* { columns: number, tasks: [ { id, name, type, status, description,
|
|
11
|
+
* label, startTime, tokenCount, ... } ] }
|
|
12
|
+
*
|
|
13
|
+
* Icon choice is by STATE, not agent type. CC's `type` field is open-ended and
|
|
14
|
+
* user-defined (dozens of agent types in a real setup), so there's no stable
|
|
15
|
+
* type→glyph map to maintain — but `running/completed/error` map cleanly onto
|
|
16
|
+
* glyphs lumira already ships across all three icon sets. The state also drives
|
|
17
|
+
* the colour, so a glance tells you what's live vs done vs failed.
|
|
18
|
+
*
|
|
19
|
+
* Robustness contract: this runs inside CC's render loop. On ANY bad input
|
|
20
|
+
* (unreadable stdin, malformed JSON, missing fields) it emits nothing and exits
|
|
21
|
+
* 0 — CC then falls back to its default rows. It must never crash the panel.
|
|
22
|
+
*/
|
|
23
|
+
import { formatTokens } from '../utils/format.js';
|
|
24
|
+
import { displayWidth, truncField } from '../render/text.js';
|
|
25
|
+
import { resolveIcons } from '../render/icons.js';
|
|
26
|
+
import { createColors, detectColorMode } from '../render/colors.js';
|
|
27
|
+
import { loadConfig } from '../config.js';
|
|
28
|
+
import { readStdin } from '../stdin.js';
|
|
29
|
+
import { debug } from '../utils/debug.js';
|
|
30
|
+
/** Per-state presentation: glyph + colour + human label. */
|
|
31
|
+
function styleFor(status, icons, colors) {
|
|
32
|
+
switch (status) {
|
|
33
|
+
case 'running':
|
|
34
|
+
return { icon: icons.clock, color: colors.cyan, label: 'running' };
|
|
35
|
+
case 'completed':
|
|
36
|
+
return { icon: icons.checkmark, color: colors.green, label: 'done' };
|
|
37
|
+
case 'error':
|
|
38
|
+
case 'failed':
|
|
39
|
+
return { icon: icons.warning, color: colors.red, label: 'error' };
|
|
40
|
+
default:
|
|
41
|
+
// Unknown/absent state: no glyph, dim, pass the raw status through as the
|
|
42
|
+
// label so a future CC state is still legible instead of being dropped.
|
|
43
|
+
return { icon: '', color: colors.dim, label: status ?? '' };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Render the `content` string for a single subagent row:
|
|
48
|
+
* `<glyph> <name> · <tokens> · <state-label>`
|
|
49
|
+
*
|
|
50
|
+
* The glyph and label are state-coloured; tokens are dim. When `columns` is
|
|
51
|
+
* given, the name is truncated (with an ellipsis) so the visible row fits,
|
|
52
|
+
* reserving width for the fixed glyph/meta first.
|
|
53
|
+
*/
|
|
54
|
+
export function renderSubagentContent(task, icons, colors, columns) {
|
|
55
|
+
const { icon, color, label } = styleFor(task.status, icons, colors);
|
|
56
|
+
// Identity fallback. CC reports every Task subagent as the generic
|
|
57
|
+
// `type: "local_agent"` with no `name`, so `description` (the dispatch arg) is
|
|
58
|
+
// the only field that distinguishes rows — it must win over a generic type.
|
|
59
|
+
// A *meaningful* type (a real agent_type, if CC ever exposes one) still wins
|
|
60
|
+
// over description, and an explicit `name` wins over everything.
|
|
61
|
+
const meaningfulType = task.type && task.type !== 'local_agent' ? task.type : '';
|
|
62
|
+
const name = (task.name || meaningfulType || task.description || task.id || 'agent').trim();
|
|
63
|
+
const tokens = Number.isFinite(task.tokenCount) ? `${formatTokens(task.tokenCount)} tok` : '';
|
|
64
|
+
const prefix = icon ? `${color(icon)} ` : '';
|
|
65
|
+
const prefixW = icon ? displayWidth(icon) + 1 : 0;
|
|
66
|
+
// Plain (uncoloured) meta used both for measuring and for assembling the
|
|
67
|
+
// coloured version, so width math and output never drift apart.
|
|
68
|
+
const metaPlain = [tokens, label].filter(Boolean);
|
|
69
|
+
const metaW = metaPlain.length ? displayWidth(` · ${metaPlain.join(' · ')}`) : 0;
|
|
70
|
+
// Fit the name to whatever width is left after the fixed glyph + meta.
|
|
71
|
+
let shownName = name;
|
|
72
|
+
if (columns !== undefined && columns > 0) {
|
|
73
|
+
const budget = columns - prefixW - metaW;
|
|
74
|
+
if (budget < displayWidth(name))
|
|
75
|
+
shownName = budget > 1 ? truncField(name, budget) : '';
|
|
76
|
+
}
|
|
77
|
+
const metaColored = [];
|
|
78
|
+
if (tokens)
|
|
79
|
+
metaColored.push(colors.dim(tokens));
|
|
80
|
+
if (label)
|
|
81
|
+
metaColored.push(color(label));
|
|
82
|
+
const meta = metaColored.join(' · ');
|
|
83
|
+
// Avoid a dangling " · " when the name was truncated away entirely.
|
|
84
|
+
if (!shownName)
|
|
85
|
+
return `${prefix}${meta}`.trimEnd();
|
|
86
|
+
return meta ? `${prefix}${shownName} · ${meta}` : `${prefix}${shownName}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Render the full CC response: one `{ id, content }` JSON line per addressable
|
|
90
|
+
* task. Tasks without an id are skipped (CC keys rows by id, so a row we can't
|
|
91
|
+
* address can't be overridden). Empty/missing task list → empty string.
|
|
92
|
+
*/
|
|
93
|
+
export function renderSubagentTasks(input, icons, colors) {
|
|
94
|
+
const tasks = Array.isArray(input?.tasks) ? input.tasks : [];
|
|
95
|
+
const columns = Number.isFinite(input?.columns) ? input.columns : undefined;
|
|
96
|
+
const lines = [];
|
|
97
|
+
for (const task of tasks) {
|
|
98
|
+
if (!task || typeof task.id !== 'string' || task.id === '')
|
|
99
|
+
continue;
|
|
100
|
+
lines.push(JSON.stringify({ id: task.id, content: renderSubagentContent(task, icons, colors, columns) }));
|
|
101
|
+
}
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Wire stdin → config → render. Reuses the main `readStdin` (its only schema
|
|
106
|
+
* assertion is "is a plain object", which our payload satisfies). Any failure
|
|
107
|
+
* degrades to empty stdout + exit 0 so CC's panel is never broken.
|
|
108
|
+
*/
|
|
109
|
+
export async function runSubagentCommand(opts = {}) {
|
|
110
|
+
const log = debug('subagent');
|
|
111
|
+
let raw;
|
|
112
|
+
try {
|
|
113
|
+
raw = await readStdin(opts.stream ?? process.stdin);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
log('stdin read failed:', e.message);
|
|
117
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
118
|
+
}
|
|
119
|
+
if (log.enabled)
|
|
120
|
+
log('payload:', raw);
|
|
121
|
+
const config = opts.config ?? loadConfig();
|
|
122
|
+
const icons = resolveIcons(config.icons);
|
|
123
|
+
const mode = opts.colorMode
|
|
124
|
+
?? (config.colors?.mode && config.colors.mode !== 'auto' ? config.colors.mode : detectColorMode());
|
|
125
|
+
const colors = createColors(mode);
|
|
126
|
+
const out = renderSubagentTasks(raw, icons, colors);
|
|
127
|
+
return { stdout: out ? `${out}\n` : '', stderr: '', exitCode: 0 };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=subagent.js.map
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { runThemesCommand } from './commands/themes.js';
|
|
|
20
20
|
import { runStatsCommand } from './commands/stats.js';
|
|
21
21
|
import { runCustomRefreshFromStdin } from './commands/custom-refresh.js';
|
|
22
22
|
import { runCustomCommand } from './commands/custom.js';
|
|
23
|
+
import { runSubagentCommand } from './commands/subagent.js';
|
|
23
24
|
import { EMPTY_TRANSCRIPT } from './types.js';
|
|
24
25
|
import { normalize } from './normalize.js';
|
|
25
26
|
const defaultDeps = {
|
|
@@ -121,6 +122,7 @@ Commands:
|
|
|
121
122
|
stats [path] Show session statistics
|
|
122
123
|
themes Browse and preview themes
|
|
123
124
|
custom Manage custom commands
|
|
125
|
+
subagent Render Claude Code agent-panel rows (reads tasks JSON from stdin)
|
|
124
126
|
|
|
125
127
|
Options:
|
|
126
128
|
--help, -h Show this help
|
|
@@ -181,6 +183,19 @@ if (isDirectRun()) {
|
|
|
181
183
|
process.exit(1);
|
|
182
184
|
});
|
|
183
185
|
}
|
|
186
|
+
else if (cmd === 'subagent') {
|
|
187
|
+
// CC's subagentStatusLine hook: reads the tasks JSON from its own stdin and
|
|
188
|
+
// emits one JSON line per row. Degrades to empty output on any error so a
|
|
189
|
+
// bad payload never breaks CC's agent panel.
|
|
190
|
+
runSubagentCommand().then(r => {
|
|
191
|
+
if (r.stdout)
|
|
192
|
+
process.stdout.write(r.stdout);
|
|
193
|
+
if (r.stderr)
|
|
194
|
+
process.stderr.write(r.stderr);
|
|
195
|
+
if (r.exitCode !== 0)
|
|
196
|
+
process.exit(r.exitCode);
|
|
197
|
+
}).catch(() => process.exit(0));
|
|
198
|
+
}
|
|
184
199
|
else if (cmd === '__custom-refresh') {
|
|
185
200
|
// Internal: invoked by the renderer as a detached child to refresh a
|
|
186
201
|
// single custom command's cache entry without keeping the renderer's
|
package/dist/installer.js
CHANGED
|
@@ -25,6 +25,25 @@ const header = () => `\n${CYAN} lumira installer${RST}\n`;
|
|
|
25
25
|
function makeStatusLine(command) {
|
|
26
26
|
return { type: 'command', command, padding: 0 };
|
|
27
27
|
}
|
|
28
|
+
/** Atomically write settings.json: temp file + fsync + rename, mode 0600. */
|
|
29
|
+
function writeSettingsAtomic(settings, settingsPath) {
|
|
30
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
31
|
+
const tmp = `${settingsPath}.${process.pid}.${Date.now()}.lumira.tmp`;
|
|
32
|
+
try {
|
|
33
|
+
const fd = openSync(tmp, 'wx', 0o600);
|
|
34
|
+
writeSync(fd, JSON.stringify(settings, null, 2) + '\n');
|
|
35
|
+
fsyncSync(fd);
|
|
36
|
+
closeSync(fd);
|
|
37
|
+
renameSync(tmp, settingsPath);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
try {
|
|
41
|
+
unlinkSync(tmp);
|
|
42
|
+
}
|
|
43
|
+
catch { }
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
28
47
|
// Rank a statusLine command by per-render speed (higher = faster).
|
|
29
48
|
// 3 = bare `lumira` binary — always resolves to the current installed version
|
|
30
49
|
// 2 = node /path/dist/index.js or plugin-cache path — fast but version-pinned
|
|
@@ -153,6 +172,31 @@ function emitFooter(lines, homeOverride) {
|
|
|
153
172
|
lines.push(`\n Restart Claude Code to see your statusline.\n`);
|
|
154
173
|
}
|
|
155
174
|
// ── Install ─────────────────────────────────────────────────────────
|
|
175
|
+
/**
|
|
176
|
+
* Optionally register Claude Code's `subagentStatusLine` hook (CC ≥ 2.1.x)
|
|
177
|
+
* alongside the main statusLine, pointing it at `<cmd> subagent`.
|
|
178
|
+
*
|
|
179
|
+
* Opt-in and interactive-only: we never add a second settings key without
|
|
180
|
+
* explicit consent, and skip silently in non-TTY runs (so CI/scripted installs
|
|
181
|
+
* stay predictable). No-op when it already points at lumira. Returns true when
|
|
182
|
+
* the key was added, so the caller knows it must flush settings to disk.
|
|
183
|
+
*/
|
|
184
|
+
async function maybeRegisterSubagent(args) {
|
|
185
|
+
const { settings, baseCmd, confirm, isTTY, lines } = args;
|
|
186
|
+
// Only register when the key is absent. If it's already lumira there's nothing
|
|
187
|
+
// to do; if it's a *foreign* command we leave it untouched rather than clobber
|
|
188
|
+
// a user's own subagent renderer without an explicit backup/replace flow.
|
|
189
|
+
if (settings.subagentStatusLine != null)
|
|
190
|
+
return false;
|
|
191
|
+
if (!isTTY)
|
|
192
|
+
return false;
|
|
193
|
+
const accepted = await confirm('Customize subagent panel rows too? (subagentStatusLine)');
|
|
194
|
+
if (!accepted)
|
|
195
|
+
return false;
|
|
196
|
+
settings.subagentStatusLine = makeStatusLine(`${baseCmd} subagent`);
|
|
197
|
+
lines.push(ok(`Configured subagentStatusLine → ${DIM}${baseCmd} subagent${RST}`));
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
156
200
|
export async function install(opts = {}) {
|
|
157
201
|
const settingsPath = opts.settingsPath ?? defaultSettingsPath();
|
|
158
202
|
const configPath = opts.configPath ?? defaultConfigPath();
|
|
@@ -243,6 +287,11 @@ export async function install(opts = {}) {
|
|
|
243
287
|
// here — it may point to a stale version and should be migrated to `lumira`.
|
|
244
288
|
if (existingIsLumira && commandSpeed(existingCmd) >= 3) {
|
|
245
289
|
lines.push(ok('lumira is already configured (optimal command)'));
|
|
290
|
+
// statusLine needs no rewrite, but a returning user may still want to opt
|
|
291
|
+
// into the subagent hook — offer it and flush only if they accept.
|
|
292
|
+
const added = await maybeRegisterSubagent({ settings, baseCmd: existingCmd, confirm, isTTY: !!stdin?.isTTY, lines });
|
|
293
|
+
if (added)
|
|
294
|
+
writeSettingsAtomic(settings, settingsPath);
|
|
246
295
|
return finalize();
|
|
247
296
|
}
|
|
248
297
|
// Resolve the fastest per-render command this environment can offer.
|
|
@@ -254,6 +303,9 @@ export async function install(opts = {}) {
|
|
|
254
303
|
// so we never downgrade a user's direct binary to npx.
|
|
255
304
|
if (commandSpeed(resolvedCmd) <= commandSpeed(existingCmd)) {
|
|
256
305
|
lines.push(ok('lumira is already configured'));
|
|
306
|
+
const added = await maybeRegisterSubagent({ settings, baseCmd: existingCmd, confirm, isTTY: !!stdin?.isTTY, lines });
|
|
307
|
+
if (added)
|
|
308
|
+
writeSettingsAtomic(settings, settingsPath);
|
|
257
309
|
return finalize();
|
|
258
310
|
}
|
|
259
311
|
}
|
|
@@ -263,22 +315,8 @@ export async function install(opts = {}) {
|
|
|
263
315
|
lines.push(ok(`Backed up existing settings → ${DIM}settings.json.lumira.bak${RST}`));
|
|
264
316
|
}
|
|
265
317
|
settings.statusLine = makeStatusLine(resolvedCmd);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
const fd = openSync(tmp, 'wx', 0o600);
|
|
270
|
-
writeSync(fd, JSON.stringify(settings, null, 2) + '\n');
|
|
271
|
-
fsyncSync(fd);
|
|
272
|
-
closeSync(fd);
|
|
273
|
-
renameSync(tmp, settingsPath);
|
|
274
|
-
}
|
|
275
|
-
catch (e) {
|
|
276
|
-
try {
|
|
277
|
-
unlinkSync(tmp);
|
|
278
|
-
}
|
|
279
|
-
catch { }
|
|
280
|
-
throw e;
|
|
281
|
-
}
|
|
318
|
+
await maybeRegisterSubagent({ settings, baseCmd: resolvedCmd, confirm, isTTY: !!stdin?.isTTY, lines });
|
|
319
|
+
writeSettingsAtomic(settings, settingsPath);
|
|
282
320
|
lines.push(ok(existingIsLumira
|
|
283
321
|
? `Upgraded statusline command → ${DIM}${resolvedCmd}${RST} (faster)`
|
|
284
322
|
: 'Configured lumira as statusline'));
|
|
@@ -339,21 +377,10 @@ export function uninstall(opts = {}) {
|
|
|
339
377
|
return lines.join('\n') + '\n';
|
|
340
378
|
}
|
|
341
379
|
delete uninstSettings.statusLine;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
fsyncSync(fd);
|
|
347
|
-
closeSync(fd);
|
|
348
|
-
renameSync(uninstTmp, settingsPath);
|
|
349
|
-
}
|
|
350
|
-
catch (e) {
|
|
351
|
-
try {
|
|
352
|
-
unlinkSync(uninstTmp);
|
|
353
|
-
}
|
|
354
|
-
catch { }
|
|
355
|
-
throw e;
|
|
356
|
-
}
|
|
380
|
+
// Only remove the subagent hook if it's ours — never wipe a foreign renderer.
|
|
381
|
+
if (isLumira(uninstSettings.subagentStatusLine))
|
|
382
|
+
delete uninstSettings.subagentStatusLine;
|
|
383
|
+
writeSettingsAtomic(uninstSettings, settingsPath);
|
|
357
384
|
lines.push(ok('Removed lumira statusline from settings'));
|
|
358
385
|
// Remove skill from both destinations (best effort)
|
|
359
386
|
for (const root of [join(home, '.claude'), join(home, '.qwen')]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lumira",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Real-time statusline HUD for Claude Code and Qwen Code. Includes session analytics CLI, API latency overhead widget, 7d quota projection, auto-compact proximity warnings, themes, and powerline. Zero deps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|