lumira 0.1.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.
Files changed (71) hide show
  1. package/README.md +165 -0
  2. package/dist/config.d.ts +3 -0
  3. package/dist/config.js +42 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.js +56 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/installer.d.ts +7 -0
  9. package/dist/installer.js +105 -0
  10. package/dist/installer.js.map +1 -0
  11. package/dist/parsers/git.d.ts +5 -0
  12. package/dist/parsers/git.js +31 -0
  13. package/dist/parsers/git.js.map +1 -0
  14. package/dist/parsers/gsd.d.ts +2 -0
  15. package/dist/parsers/gsd.js +37 -0
  16. package/dist/parsers/gsd.js.map +1 -0
  17. package/dist/parsers/memory.d.ts +2 -0
  18. package/dist/parsers/memory.js +28 -0
  19. package/dist/parsers/memory.js.map +1 -0
  20. package/dist/parsers/token-speed.d.ts +9 -0
  21. package/dist/parsers/token-speed.js +21 -0
  22. package/dist/parsers/token-speed.js.map +1 -0
  23. package/dist/parsers/transcript.d.ts +4 -0
  24. package/dist/parsers/transcript.js +148 -0
  25. package/dist/parsers/transcript.js.map +1 -0
  26. package/dist/render/colors.d.ts +20 -0
  27. package/dist/render/colors.js +59 -0
  28. package/dist/render/colors.js.map +1 -0
  29. package/dist/render/icons.d.ts +19 -0
  30. package/dist/render/icons.js +20 -0
  31. package/dist/render/icons.js.map +1 -0
  32. package/dist/render/index.d.ts +2 -0
  33. package/dist/render/index.js +27 -0
  34. package/dist/render/index.js.map +1 -0
  35. package/dist/render/line1.d.ts +3 -0
  36. package/dist/render/line1.js +89 -0
  37. package/dist/render/line1.js.map +1 -0
  38. package/dist/render/line2.d.ts +3 -0
  39. package/dist/render/line2.js +108 -0
  40. package/dist/render/line2.js.map +1 -0
  41. package/dist/render/line3.d.ts +3 -0
  42. package/dist/render/line3.js +60 -0
  43. package/dist/render/line3.js.map +1 -0
  44. package/dist/render/line4.d.ts +3 -0
  45. package/dist/render/line4.js +17 -0
  46. package/dist/render/line4.js.map +1 -0
  47. package/dist/render/minimal.d.ts +3 -0
  48. package/dist/render/minimal.js +120 -0
  49. package/dist/render/minimal.js.map +1 -0
  50. package/dist/render/text.d.ts +5 -0
  51. package/dist/render/text.js +70 -0
  52. package/dist/render/text.js.map +1 -0
  53. package/dist/stdin.d.ts +3 -0
  54. package/dist/stdin.js +40 -0
  55. package/dist/stdin.js.map +1 -0
  56. package/dist/types.d.ts +152 -0
  57. package/dist/types.js +45 -0
  58. package/dist/types.js.map +1 -0
  59. package/dist/utils/cache.d.ts +8 -0
  60. package/dist/utils/cache.js +48 -0
  61. package/dist/utils/cache.js.map +1 -0
  62. package/dist/utils/exec.d.ts +5 -0
  63. package/dist/utils/exec.js +14 -0
  64. package/dist/utils/exec.js.map +1 -0
  65. package/dist/utils/format.d.ts +4 -0
  66. package/dist/utils/format.js +36 -0
  67. package/dist/utils/format.js.map +1 -0
  68. package/dist/utils/terminal.d.ts +2 -0
  69. package/dist/utils/terminal.js +53 -0
  70. package/dist/utils/terminal.js.map +1 -0
  71. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # lumira
2
+
3
+ Real-time statusline plugin for [Claude Code](https://code.claude.com).
4
+
5
+ ![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)
6
+ ![Tests](https://img.shields.io/badge/tests-136%20passing-green)
7
+ ![Dependencies](https://img.shields.io/badge/runtime%20deps-0-brightgreen)
8
+
9
+ ## Features
10
+
11
+ - **3-line custom mode** + **1-line minimal mode** (auto-switches at <70 columns)
12
+ - **Context bar** with color thresholds (green → yellow → orange → blinking red)
13
+ - **Git status** with branch, staged/modified/untracked counts (5s TTL cache)
14
+ - **Token metrics** — input/output counts, speed (tok/s), cost + burn rate ($/h)
15
+ - **Rate limits** — 5h/7d usage with color warnings and reset countdown
16
+ - **Transcript parsing** — active tools, agents, and todo progress
17
+ - **GSD integration** — current task and update notifications
18
+ - **Memory usage** display
19
+ - **Nerd Font icons** throughout
20
+ - **3-tier color system** — named ANSI, 256-color, truecolor (auto-detected)
21
+ - **Config-driven** — toggle any feature via JSON config + CLI flags
22
+ - **Zero runtime dependencies**
23
+
24
+ ## Install
25
+
26
+ Quick setup (auto-configures Claude Code):
27
+
28
+ ```bash
29
+ npx lumira install
30
+ ```
31
+
32
+ Or install globally:
33
+
34
+ ```bash
35
+ npm install -g lumira
36
+ lumira install
37
+ ```
38
+
39
+ To uninstall:
40
+
41
+ ```bash
42
+ npx lumira uninstall
43
+ ```
44
+
45
+ ### Manual setup
46
+
47
+ Add to `~/.claude/settings.json`:
48
+
49
+ ```json
50
+ {
51
+ "statusLine": {
52
+ "type": "command",
53
+ "command": "npx lumira@latest",
54
+ "padding": 0
55
+ }
56
+ }
57
+ ```
58
+
59
+ If installed from source:
60
+
61
+ ```json
62
+ {
63
+ "statusLine": {
64
+ "type": "command",
65
+ "command": "node /path/to/claude-cc/dist/index.js",
66
+ "padding": 0
67
+ }
68
+ }
69
+ ```
70
+
71
+ ## Display
72
+
73
+ ### Custom Mode (default, >=70 columns)
74
+
75
+ ```
76
+ Opus 4.6 (1M context) │ main ⇡1 !2 │ my-project +150 -30 │ default │ v2.1.92
77
+ [████████░░░░░░░░░░░░] 21% │ 131k↑ 25k↓ │ $1.31 $2.24/h │ 35m06s │ 142 tok/s │ 72%(5h)
78
+ ✓ Read ×3 | ✓ Edit ×2 | ✓ Bash ×5 │ ████████░░ 8/10 | ◐ 1 | ○ 1
79
+ ```
80
+
81
+ ### Minimal Mode (<70 columns or `--minimal`)
82
+
83
+ ```
84
+ my-project | main | Opus 4.6 | ████░░░░░░░░░░░░░░░░ 21% | 131k↑ 25k↓ | $1.31
85
+ ```
86
+
87
+ ## Configuration
88
+
89
+ Create `~/.config/lumira/config.json`:
90
+
91
+ ```json
92
+ {
93
+ "layout": "auto",
94
+ "gsd": false,
95
+ "display": {
96
+ "model": true,
97
+ "branch": true,
98
+ "gitChanges": true,
99
+ "directory": true,
100
+ "contextBar": true,
101
+ "tokens": true,
102
+ "cost": true,
103
+ "burnRate": true,
104
+ "duration": true,
105
+ "tokenSpeed": true,
106
+ "rateLimits": true,
107
+ "tools": true,
108
+ "todos": true,
109
+ "vim": true,
110
+ "effort": true,
111
+ "worktree": true,
112
+ "agent": true,
113
+ "sessionName": true,
114
+ "style": true,
115
+ "version": true,
116
+ "linesChanged": true,
117
+ "memory": true
118
+ },
119
+ "colors": {
120
+ "mode": "auto"
121
+ }
122
+ }
123
+ ```
124
+
125
+ All fields are optional — defaults are shown above.
126
+
127
+ ### CLI Flags
128
+
129
+ ```bash
130
+ lumira --minimal # Force minimal mode
131
+ lumira --gsd # Enable GSD integration
132
+ ```
133
+
134
+ ## Architecture
135
+
136
+ ```
137
+ stdin (JSON from Claude Code)
138
+ → parsers (git, transcript, token-speed, memory, gsd)
139
+ → RenderContext
140
+ → render (line1-4 or minimal)
141
+ → stdout
142
+ ```
143
+
144
+ - **Dependency injection** for testability
145
+ - **File caching** — TTL-based (git, speed) and mtime-based (transcript)
146
+ - **Progressive truncation** — adapts to terminal width
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ npm run dev # Watch mode (tsc --watch)
152
+ npm test # Run tests
153
+ npm run test:watch # Watch mode
154
+ npm run test:coverage # With coverage
155
+ npm run lint # Type check
156
+ npm run build # Compile to dist/
157
+ ```
158
+
159
+ ## Credits
160
+
161
+ Inspired by [claude-hud](https://github.com/jarrodwatts/claude-hud). Migrated from [claude-setup](https://github.com/cativo23/claude-setup) statusline.
162
+
163
+ ## License
164
+
165
+ MIT
@@ -0,0 +1,3 @@
1
+ import { type HudConfig } from './types.js';
2
+ export declare function loadConfig(configDir?: string): HudConfig;
3
+ export declare function mergeCliFlags(config: HudConfig, argv: string[]): HudConfig;
package/dist/config.js ADDED
@@ -0,0 +1,42 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { DEFAULT_CONFIG, DEFAULT_DISPLAY } from './types.js';
5
+ export function loadConfig(configDir = join(homedir(), '.config', 'lumira')) {
6
+ const p = join(configDir, 'config.json');
7
+ if (!existsSync(p))
8
+ return { ...DEFAULT_CONFIG, display: { ...DEFAULT_DISPLAY } };
9
+ try {
10
+ const raw = JSON.parse(readFileSync(p, 'utf8'));
11
+ return mergeConfig(raw);
12
+ }
13
+ catch {
14
+ return { ...DEFAULT_CONFIG, display: { ...DEFAULT_DISPLAY } };
15
+ }
16
+ }
17
+ function mergeConfig(raw) {
18
+ const layout = ['custom', 'minimal', 'auto'].includes(raw.layout) ? raw.layout : DEFAULT_CONFIG.layout;
19
+ const display = { ...DEFAULT_DISPLAY };
20
+ if (raw.display && typeof raw.display === 'object') {
21
+ for (const k of Object.keys(DEFAULT_DISPLAY)) {
22
+ if (typeof raw.display[k] === 'boolean')
23
+ display[k] = raw.display[k];
24
+ }
25
+ }
26
+ const colors = { ...DEFAULT_CONFIG.colors };
27
+ if (raw.colors && typeof raw.colors === 'object') {
28
+ const m = raw.colors.mode;
29
+ if (['auto', 'named', '256', 'truecolor'].includes(m))
30
+ colors.mode = m;
31
+ }
32
+ return { layout, gsd: typeof raw.gsd === 'boolean' ? raw.gsd : DEFAULT_CONFIG.gsd, display, colors };
33
+ }
34
+ export function mergeCliFlags(config, argv) {
35
+ const r = { ...config, display: { ...config.display }, colors: { ...config.colors } };
36
+ if (argv.includes('--minimal'))
37
+ r.layout = 'minimal';
38
+ if (argv.includes('--gsd'))
39
+ r.gsd = true;
40
+ return r;
41
+ }
42
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import type { Dependencies } from './types.js';
3
+ export declare function main(overrides?: Partial<Dependencies>): Promise<string>;
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'node:url';
3
+ import { readStdin as defaultReadStdin } from './stdin.js';
4
+ import { parseGitStatus } from './parsers/git.js';
5
+ import { parseTranscript } from './parsers/transcript.js';
6
+ import { getTokenSpeed } from './parsers/token-speed.js';
7
+ import { getMemoryInfo } from './parsers/memory.js';
8
+ import { getGsdInfo } from './parsers/gsd.js';
9
+ import { getTermCols, getLayoutCols } from './utils/terminal.js';
10
+ import { loadConfig, mergeCliFlags } from './config.js';
11
+ import { render } from './render/index.js';
12
+ import { install, uninstall } from './installer.js';
13
+ import { EMPTY_TRANSCRIPT } from './types.js';
14
+ const defaultDeps = {
15
+ readStdin: () => defaultReadStdin(process.stdin),
16
+ parseGit: (cwd) => parseGitStatus(cwd),
17
+ parseTranscript: (path) => parseTranscript(path),
18
+ getTokenSpeed: (ctx) => getTokenSpeed(ctx),
19
+ getMemoryInfo: () => getMemoryInfo(),
20
+ getGsdInfo: (session) => getGsdInfo(session),
21
+ getTermCols: () => getTermCols(),
22
+ };
23
+ export async function main(overrides = {}) {
24
+ const deps = { ...defaultDeps, ...overrides };
25
+ const config = mergeCliFlags(loadConfig(), process.argv);
26
+ const input = await deps.readStdin();
27
+ const cwd = input.cwd || input.workspace?.current_dir || process.cwd();
28
+ const [git, transcript] = await Promise.all([
29
+ deps.parseGit(cwd),
30
+ input.transcript_path ? deps.parseTranscript(input.transcript_path) : Promise.resolve(EMPTY_TRANSCRIPT),
31
+ ]);
32
+ const tokenSpeed = deps.getTokenSpeed(input.context_window);
33
+ const memory = deps.getMemoryInfo();
34
+ const gsd = config.gsd ? deps.getGsdInfo(input.session_id) : null;
35
+ const rawCols = deps.getTermCols();
36
+ const isTTY = !!(process.stdout.columns || process.stderr.columns);
37
+ const cols = getLayoutCols(rawCols, isTTY);
38
+ return render({ input, git, transcript, tokenSpeed, memory, gsd, cols, config });
39
+ }
40
+ // Run when invoked directly
41
+ const __filename = fileURLToPath(import.meta.url);
42
+ if (process.argv[1] && (__filename === process.argv[1] || __filename === process.argv[1] + '.js')) {
43
+ const cmd = process.argv[2];
44
+ if (cmd === 'install') {
45
+ install().then(o => process.stdout.write(o)).catch(e => process.stderr.write(`Install error: ${e.message}\n`));
46
+ }
47
+ else if (cmd === 'uninstall') {
48
+ const o = uninstall();
49
+ process.stdout.write(o);
50
+ }
51
+ else {
52
+ main().then(o => process.stdout.write(o)).catch(e => { if (!(e instanceof SyntaxError))
53
+ process.stderr.write(`Statusline error: ${e.message}\n`); });
54
+ }
55
+ }
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface InstallerOptions {
2
+ settingsPath?: string;
3
+ confirm?: (prompt: string) => Promise<boolean>;
4
+ }
5
+ export declare function promptYN(question: string): Promise<boolean>;
6
+ export declare function install(opts?: InstallerOptions): Promise<string>;
7
+ export declare function uninstall(opts?: InstallerOptions): string;
@@ -0,0 +1,105 @@
1
+ import { readFileSync, writeFileSync, existsSync, copyFileSync, unlinkSync, mkdirSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { createInterface } from 'node:readline';
5
+ // ── ANSI helpers ────────────────────────────────────────────────────
6
+ const RST = '\x1b[0m';
7
+ const GREEN = '\x1b[32m';
8
+ const YELLOW = '\x1b[33m';
9
+ const CYAN = '\x1b[36m';
10
+ const DIM = '\x1b[2m';
11
+ const ok = (msg) => `${GREEN}✓${RST} ${msg}`;
12
+ const warn = (msg) => `${YELLOW}⚠${RST} ${msg}`;
13
+ const header = () => `\n${CYAN} lumira installer${RST}\n`;
14
+ // ── StatusLine value ────────────────────────────────────────────────
15
+ const LUMIRA_STATUSLINE = {
16
+ type: 'command',
17
+ command: 'npx lumira@latest',
18
+ padding: 0,
19
+ };
20
+ function defaultSettingsPath() {
21
+ return join(homedir(), '.claude', 'settings.json');
22
+ }
23
+ function isLumira(statusLine) {
24
+ if (!statusLine || typeof statusLine !== 'object')
25
+ return false;
26
+ const sl = statusLine;
27
+ return typeof sl.command === 'string' && sl.command.includes('lumira');
28
+ }
29
+ // ── Prompt helper ───────────────────────────────────────────────────
30
+ export function promptYN(question) {
31
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
32
+ return new Promise((resolve) => {
33
+ rl.question(`${question} (y/N) `, (answer) => {
34
+ rl.close();
35
+ resolve(answer.trim().toLowerCase() === 'y');
36
+ });
37
+ });
38
+ }
39
+ // ── Install ─────────────────────────────────────────────────────────
40
+ export async function install(opts = {}) {
41
+ const settingsPath = opts.settingsPath ?? defaultSettingsPath();
42
+ const backupPath = settingsPath + '.lumira.bak';
43
+ const confirm = opts.confirm ?? promptYN;
44
+ const lines = [header()];
45
+ let settings = {};
46
+ if (existsSync(settingsPath)) {
47
+ try {
48
+ settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
49
+ }
50
+ catch {
51
+ lines.push(warn('Could not parse existing settings.json, creating fresh'));
52
+ settings = {};
53
+ }
54
+ }
55
+ if (settings.statusLine) {
56
+ if (isLumira(settings.statusLine)) {
57
+ lines.push(ok('lumira is already configured as your statusline'));
58
+ return lines.join('\n') + '\n';
59
+ }
60
+ const current = settings.statusLine.command ?? 'unknown';
61
+ lines.push(warn(`Current statusline: ${YELLOW}${current}${RST}`));
62
+ const accepted = await confirm('Replace with lumira?');
63
+ if (!accepted) {
64
+ lines.push(`\n Aborted. No changes made.\n`);
65
+ return lines.join('\n') + '\n';
66
+ }
67
+ copyFileSync(settingsPath, backupPath);
68
+ lines.push(ok(`Backed up existing settings → ${DIM}settings.json.lumira.bak${RST}`));
69
+ }
70
+ settings.statusLine = { ...LUMIRA_STATUSLINE };
71
+ mkdirSync(dirname(settingsPath), { recursive: true });
72
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 });
73
+ lines.push(ok('Configured lumira as statusline'));
74
+ lines.push(`\n Restart Claude Code to see your statusline.\n`);
75
+ return lines.join('\n') + '\n';
76
+ }
77
+ // ── Uninstall ───────────────────────────────────────────────────────
78
+ export function uninstall(opts = {}) {
79
+ const settingsPath = opts.settingsPath ?? defaultSettingsPath();
80
+ const backupPath = settingsPath + '.lumira.bak';
81
+ const lines = [header()];
82
+ if (!existsSync(settingsPath)) {
83
+ lines.push(ok('Nothing to uninstall — no settings.json found'));
84
+ return lines.join('\n') + '\n';
85
+ }
86
+ if (existsSync(backupPath)) {
87
+ copyFileSync(backupPath, settingsPath);
88
+ unlinkSync(backupPath);
89
+ lines.push(ok('Restored previous settings from backup'));
90
+ lines.push(`\n Restart Claude Code to apply changes.\n`);
91
+ return lines.join('\n') + '\n';
92
+ }
93
+ try {
94
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
95
+ delete settings.statusLine;
96
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 });
97
+ lines.push(ok('Removed lumira statusline from settings'));
98
+ }
99
+ catch {
100
+ lines.push(warn('Could not parse settings.json'));
101
+ }
102
+ lines.push(`\n Restart Claude Code to apply changes.\n`);
103
+ return lines.join('\n') + '\n';
104
+ }
105
+ //# sourceMappingURL=installer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer.js","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type ExecOptions } from '../utils/exec.js';
2
+ import type { GitStatus } from '../types.js';
3
+ type ExecFn = (cmd: string, args: string[], opts?: ExecOptions) => Promise<string>;
4
+ export declare function parseGitStatus(cwd: string, exec?: ExecFn): Promise<GitStatus>;
5
+ export {};
@@ -0,0 +1,31 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { tmpdir } from 'node:os';
3
+ import { readTtlCache, writeTtlCache } from '../utils/cache.js';
4
+ import { safeExec } from '../utils/exec.js';
5
+ import { EMPTY_GIT } from '../types.js';
6
+ const GIT_CACHE_TTL = 5000;
7
+ function cacheKey(cwd) {
8
+ return 'git-' + createHash('md5').update(cwd).digest('hex').slice(0, 8);
9
+ }
10
+ export async function parseGitStatus(cwd, exec = safeExec) {
11
+ const key = cacheKey(cwd);
12
+ const cached = readTtlCache(key, tmpdir(), GIT_CACHE_TTL);
13
+ if (cached)
14
+ return cached;
15
+ const branch = await exec('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, timeoutMs: 2000 });
16
+ if (!branch)
17
+ return EMPTY_GIT;
18
+ const result = { branch, staged: 0, modified: 0, untracked: 0 };
19
+ const status = await exec('git', ['status', '--porcelain'], { cwd, timeoutMs: 2000 });
20
+ if (status) {
21
+ const lines = status.split('\n').filter(Boolean);
22
+ // staged: index status is A/D/R/C/T (not space, not ?, not M which shows as modified)
23
+ result.staged = lines.filter(l => l[0] !== ' ' && l[0] !== '?' && l[0] !== 'M').length;
24
+ // modified: M in index (col 0) or worktree (col 1)
25
+ result.modified = lines.filter(l => l[0] === 'M' || l[1] === 'M').length;
26
+ result.untracked = lines.filter(l => l.startsWith('??')).length;
27
+ }
28
+ writeTtlCache(key, result, tmpdir());
29
+ return result;
30
+ }
31
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/parsers/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAoB,MAAM,kBAAkB,CAAC;AAE9D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,OAAe,QAAQ;IACvE,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,MAAM,GAAG,YAAY,CAAY,GAAG,EAAE,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC;IACrE,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClG,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,MAAM,MAAM,GAAc,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IAC3E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtF,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACjD,sFAAsF;QACtF,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;QACvF,mDAAmD;QACnD,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;QACzE,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;IAClE,CAAC;IAED,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { GsdInfo } from '../types.js';
2
+ export declare function getGsdInfo(session: string, claudeDir?: string): GsdInfo | null;
@@ -0,0 +1,37 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ export function getGsdInfo(session, claudeDir = join(process.env['CLAUDE_CONFIG_DIR'] || join(homedir(), '.claude'))) {
5
+ let updateAvailable = false;
6
+ let currentTask;
7
+ const cacheFile = join(claudeDir, 'cache', 'gsd-update-check.json');
8
+ if (existsSync(cacheFile)) {
9
+ try {
10
+ if (JSON.parse(readFileSync(cacheFile, 'utf8')).update_available)
11
+ updateAvailable = true;
12
+ }
13
+ catch { }
14
+ }
15
+ const todosDir = join(claudeDir, 'todos');
16
+ if (session && existsSync(todosDir)) {
17
+ try {
18
+ const sanitized = (session || '').replace(/[^\w-]/g, '');
19
+ const files = readdirSync(todosDir).filter(f => f.startsWith(sanitized) && f.includes('-agent-') && f.endsWith('.json'))
20
+ .map(f => ({ name: f, mtime: statSync(join(todosDir, f)).mtime })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
21
+ if (files.length > 0) {
22
+ const todoPath = join(todosDir, files[0].name);
23
+ if (!resolve(todoPath).startsWith(resolve(todosDir)))
24
+ return null;
25
+ const todos = JSON.parse(readFileSync(resolve(todoPath), 'utf8'));
26
+ const ip = todos.find((t) => t.status === 'in_progress');
27
+ if (ip?.activeForm)
28
+ currentTask = ip.activeForm;
29
+ }
30
+ }
31
+ catch { }
32
+ }
33
+ if (!updateAvailable && !currentTask)
34
+ return null;
35
+ return { updateAvailable, currentTask };
36
+ }
37
+ //# sourceMappingURL=gsd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gsd.js","sourceRoot":"","sources":["../../src/parsers/gsd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,YAAoB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAClI,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,IAAI,WAA+B,CAAC;IAEpC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC;IACpE,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAAC,IAAI,CAAC;YAAC,IAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,gBAAgB;gBAAE,eAAe,GAAG,IAAI,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IAAC,CAAC;IAEzI,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1C,IAAI,OAAO,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;iBACrH,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3H,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC/C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;oBAAE,OAAO,IAAI,CAAC;gBAClE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;gBAClE,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAA0C,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;gBAClG,IAAI,EAAE,EAAE,UAAU;oBAAE,WAAW,GAAG,EAAE,CAAC,UAAU,CAAC;YAClD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IAED,IAAI,CAAC,eAAe,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { MemoryInfo } from '../types.js';
2
+ export declare function getMemoryInfo(): MemoryInfo | null;
@@ -0,0 +1,28 @@
1
+ import { totalmem, freemem, platform } from 'node:os';
2
+ import { execFileSync } from 'node:child_process';
3
+ export function getMemoryInfo() {
4
+ try {
5
+ if (platform() === 'darwin') {
6
+ const output = execFileSync('vm_stat', [], { encoding: 'utf8', timeout: 2000 });
7
+ const psMatch = output.match(/page size of (\d+) bytes/);
8
+ const ps = psMatch ? parseInt(psMatch[1], 10) : 16384;
9
+ const active = output.match(/Pages active:\s+(\d+)/);
10
+ const wired = output.match(/Pages wired down:\s+(\d+)/);
11
+ const compressed = output.match(/Pages occupied by compressor:\s+(\d+)/);
12
+ if (!active || !wired)
13
+ return null;
14
+ const usedBytes = (parseInt(active[1], 10) + parseInt(wired[1], 10) + (compressed ? parseInt(compressed[1], 10) : 0)) * ps;
15
+ const totalBytes = totalmem();
16
+ return { usedBytes, totalBytes, percentage: Math.min(100, Math.max(0, Math.round((usedBytes / totalBytes) * 100))) };
17
+ }
18
+ const totalBytes = totalmem();
19
+ if (totalBytes <= 0)
20
+ return null;
21
+ const usedBytes = totalBytes - freemem();
22
+ return { usedBytes, totalBytes, percentage: Math.min(100, Math.max(0, Math.round((usedBytes / totalBytes) * 100))) };
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/parsers/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,IAAI,QAAQ,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAChF,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACzD,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACtD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YACrD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;YACxD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;YACzE,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YACnC,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YAC3H,MAAM,UAAU,GAAG,QAAQ,EAAE,CAAC;YAC9B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvH,CAAC;QACD,MAAM,UAAU,GAAG,QAAQ,EAAE,CAAC;QAC9B,IAAI,UAAU,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACjC,MAAM,SAAS,GAAG,UAAU,GAAG,OAAO,EAAE,CAAC;QACzC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACvH,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,9 @@
1
+ interface ContextWindow {
2
+ used_percentage: number;
3
+ remaining_percentage: number;
4
+ current_usage?: {
5
+ output_tokens: number;
6
+ };
7
+ }
8
+ export declare function getTokenSpeed(contextWindow: ContextWindow, cacheDir?: string): number | null;
9
+ export {};
@@ -0,0 +1,21 @@
1
+ import { readTtlCache, writeTtlCache } from '../utils/cache.js';
2
+ import { tmpdir } from 'node:os';
3
+ const SPEED_CACHE_TTL = 2000;
4
+ export function getTokenSpeed(contextWindow, cacheDir = tmpdir()) {
5
+ const outputTokens = contextWindow?.current_usage?.output_tokens;
6
+ if (typeof outputTokens !== 'number' || !Number.isFinite(outputTokens))
7
+ return null;
8
+ const now = Date.now();
9
+ const previous = readTtlCache('speed', cacheDir, SPEED_CACHE_TTL);
10
+ let speed = null;
11
+ if (previous && outputTokens >= previous.outputTokens) {
12
+ const deltaTokens = outputTokens - previous.outputTokens;
13
+ const deltaMs = now - previous.timestamp;
14
+ if (deltaTokens > 0 && deltaMs > 0 && deltaMs <= SPEED_CACHE_TTL) {
15
+ speed = Math.round(deltaTokens / (deltaMs / 1000));
16
+ }
17
+ }
18
+ writeTtlCache('speed', { outputTokens, timestamp: now }, cacheDir);
19
+ return speed;
20
+ }
21
+ //# sourceMappingURL=token-speed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-speed.js","sourceRoot":"","sources":["../../src/parsers/token-speed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,eAAe,GAAG,IAAI,CAAC;AAI7B,MAAM,UAAU,aAAa,CAAC,aAA4B,EAAE,WAAmB,MAAM,EAAE;IACrF,MAAM,YAAY,GAAG,aAAa,EAAE,aAAa,EAAE,aAAa,CAAC;IACjE,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,QAAQ,GAAG,YAAY,CAAa,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;IAE9E,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,QAAQ,IAAI,YAAY,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACtD,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QACzD,MAAM,OAAO,GAAG,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC;QACzC,IAAI,WAAW,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,eAAe,EAAE,CAAC;YACjE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,aAAa,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { TranscriptData, TodoStatus } from '../types.js';
2
+ export declare function normalizeTodoStatus(status: string | undefined): TodoStatus;
3
+ export declare function extractToolTarget(toolName: string, input: Record<string, unknown> | undefined): string | undefined;
4
+ export declare function parseTranscript(transcriptPath: string): Promise<TranscriptData>;