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.
- package/README.md +165 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +42 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/installer.d.ts +7 -0
- package/dist/installer.js +105 -0
- package/dist/installer.js.map +1 -0
- package/dist/parsers/git.d.ts +5 -0
- package/dist/parsers/git.js +31 -0
- package/dist/parsers/git.js.map +1 -0
- package/dist/parsers/gsd.d.ts +2 -0
- package/dist/parsers/gsd.js +37 -0
- package/dist/parsers/gsd.js.map +1 -0
- package/dist/parsers/memory.d.ts +2 -0
- package/dist/parsers/memory.js +28 -0
- package/dist/parsers/memory.js.map +1 -0
- package/dist/parsers/token-speed.d.ts +9 -0
- package/dist/parsers/token-speed.js +21 -0
- package/dist/parsers/token-speed.js.map +1 -0
- package/dist/parsers/transcript.d.ts +4 -0
- package/dist/parsers/transcript.js +148 -0
- package/dist/parsers/transcript.js.map +1 -0
- package/dist/render/colors.d.ts +20 -0
- package/dist/render/colors.js +59 -0
- package/dist/render/colors.js.map +1 -0
- package/dist/render/icons.d.ts +19 -0
- package/dist/render/icons.js +20 -0
- package/dist/render/icons.js.map +1 -0
- package/dist/render/index.d.ts +2 -0
- package/dist/render/index.js +27 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/line1.d.ts +3 -0
- package/dist/render/line1.js +89 -0
- package/dist/render/line1.js.map +1 -0
- package/dist/render/line2.d.ts +3 -0
- package/dist/render/line2.js +108 -0
- package/dist/render/line2.js.map +1 -0
- package/dist/render/line3.d.ts +3 -0
- package/dist/render/line3.js +60 -0
- package/dist/render/line3.js.map +1 -0
- package/dist/render/line4.d.ts +3 -0
- package/dist/render/line4.js +17 -0
- package/dist/render/line4.js.map +1 -0
- package/dist/render/minimal.d.ts +3 -0
- package/dist/render/minimal.js +120 -0
- package/dist/render/minimal.js.map +1 -0
- package/dist/render/text.d.ts +5 -0
- package/dist/render/text.js +70 -0
- package/dist/render/text.js.map +1 -0
- package/dist/stdin.d.ts +3 -0
- package/dist/stdin.js +40 -0
- package/dist/stdin.js.map +1 -0
- package/dist/types.d.ts +152 -0
- package/dist/types.js +45 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cache.d.ts +8 -0
- package/dist/utils/cache.js +48 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/exec.d.ts +5 -0
- package/dist/utils/exec.js +14 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/format.d.ts +4 -0
- package/dist/utils/format.js +36 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/terminal.d.ts +2 -0
- package/dist/utils/terminal.js +53 -0
- package/dist/utils/terminal.js.map +1 -0
- 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
|
+

|
|
6
|
+

|
|
7
|
+

|
|
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
|
package/dist/config.d.ts
ADDED
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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"}
|
package/dist/index.d.ts
ADDED
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,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,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,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>;
|