@litfamily/litclaude 1.0.22 โ 1.0.23
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/CHANGELOG.md +6 -0
- package/README.md +8 -8
- package/README_ko-KR.md +8 -8
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/hooks.md +56 -25
- package/package.json +1 -1
- package/plugins/litclaude/.claude-plugin/plugin.json +3 -3
- package/plugins/litclaude/bin/litclaude-hud.js +2 -5
- package/plugins/litclaude/bin/litclaude-mcp.js +1 -1
- package/plugins/litclaude/hooks/lit-band-client.mjs +1 -1
- package/plugins/litclaude/hooks/lit-mod.mjs +53 -8
- package/plugins/litclaude/lib/hud-ignition.mjs +7 -37
- package/plugins/litclaude/lib/mod/frame.mjs +270 -194
- package/plugins/litclaude/lib/mod/layout.mjs +0 -23
- package/plugins/litclaude/lib/mod/render.mjs +4 -8
- package/plugins/litclaude/lib/mod/settings.mjs +52 -4
- package/plugins/litclaude/lib/mod/skills.mjs +81 -0
- package/plugins/litclaude/lib/mod/state.mjs +38 -1
- package/plugins/litclaude/lib/mod/timeline.mjs +30 -2
- package/plugins/litclaude/lib/mod/view.mjs +32 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.23 - 2026-10-12
|
|
4
|
+
|
|
5
|
+
- The LIT card above the prompt now stays on screen for the whole session, so you can still see the status after the work ends. It is a small four-row card in the colours of your status line. The top edge names the workflow and the current phase. Below it are the plan, work and review steps with the progress of the plan or goal. Every LitClaude skill that runs gets its own chip, several at once. A chip is bright while its request is running and dims into a recent list when the turn ends. At the bottom are the context use, the five-hour usage window and the Git branch. The card fits narrow terminals by leaving out the least important parts and never wraps. The `band` setting now defaults to `always`; `auto` brings back the old behaviour, and `off` hides the card.
|
|
6
|
+
- The status line no longer shows the `๐ฅ LIT IGNITED ยท <workflow> ๐ฅ` mark. It took up so much room that the model, context and usage figures were cut off, and the card now shows which workflow is active.
|
|
7
|
+
- Project rules now fit in the skill context when LitClaude is installed under a long folder path. Before, a long install path could cut off the first line of a rule or leave the rules out entirely.
|
|
8
|
+
|
|
3
9
|
## 1.0.22 - 2026-10-11
|
|
4
10
|
|
|
5
11
|
- The README is shorter for first-time readers. It says what LitClaude does, how to install it and what to type first, and the detailed material now lives in docs pages (guide, skills, Jev, development) in English and Korean. The GitHub page also plays a short film of a real session.
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitClaude robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
|
|
2
2
|
|
|
3
3
|
<h1 align="center">LitClaude</h1>
|
|
4
4
|
<p align="center"><strong>Keep the work lit.</strong></p>
|
|
5
5
|
<p align="center">A Claude Code plugin that helps every session plan the work, build it, check it, and leave a note the next session can pick up.</p>
|
|
6
6
|
<p align="center">
|
|
7
|
-
<img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
8
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
7
|
+
<img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/readme/badge-version.svg" alt="1.0.23" />
|
|
8
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
|
|
9
9
|
</p>
|
|
10
10
|
<p align="center">
|
|
11
|
-
<a href="https://github.com/wjgoarxiv/litclaude#readme">Full guide and skills gallery on GitHub</a> ยท <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
11
|
+
<a href="https://github.com/wjgoarxiv/litclaude#readme">Full guide and skills gallery on GitHub</a> ยท <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/README_ko-KR.md">ํ๊ตญ์ด</a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
Add `lit` to a Claude Code prompt and Claude pins the goal, writes a failing test first,
|
|
@@ -73,7 +73,7 @@ the `/plugin` menu.
|
|
|
73
73
|
## More
|
|
74
74
|
|
|
75
75
|
- [Full guide and skills gallery on GitHub](https://github.com/wjgoarxiv/litclaude#readme)
|
|
76
|
-
- [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
77
|
-
- [Migration and ownership conflicts](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
78
|
-
- [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
79
|
-
- [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
76
|
+
- [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/privacy.md)
|
|
77
|
+
- [Migration and ownership conflicts](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/migration.md)
|
|
78
|
+
- [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/CHANGELOG.md)
|
|
79
|
+
- [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/LICENSE)
|
package/README_ko-KR.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/cover-motion.webp" width="100%" alt="LitFamily ๋ชจ์
์ปค๋ฒ: ๋ค์ฏ ๋ก๋ด ํจ๋์ด ์ฐจ๋ก๋ก ์ผ์ง๊ณ , LitClaude ๋ก๋ด์ ๋๊ณผ ํ
๋๋ฆฌ๊ฐ ๋น๋ ๋ค LITFAMILY์ KEEP THE WORK LIT. ๋ฌธ๊ตฌ๊ฐ ๋ฐ์์ง๋ ์์" /></picture></p>
|
|
2
2
|
|
|
3
3
|
<h1 align="center">LitClaude</h1>
|
|
4
4
|
<p align="center"><strong>Keep the work lit.</strong></p>
|
|
5
5
|
<p align="center">Claude Code์์ ์ธ์
๋ง๋ค ๊ณํํ๊ณ , ๋ง๋ค๊ณ , ํ์ธํ ๋ค ๋ค์ ์ธ์
์ด ์ด์ด๋ฐ์ ๊ธฐ๋ก์ ๋จ๊ธฐ๊ฒ ๋๋ ํ๋ฌ๊ทธ์ธ์
๋๋ค.</p>
|
|
6
6
|
<p align="center">
|
|
7
|
-
<img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
8
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
7
|
+
<img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/readme/badge-version.svg" alt="1.0.23" />
|
|
8
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
|
|
9
9
|
</p>
|
|
10
10
|
<p align="center">
|
|
11
|
-
<a href="https://github.com/wjgoarxiv/litclaude/blob/main/README_ko-KR.md">์ ์ฒด ์๋ด์ ์คํฌ ๊ฐค๋ฌ๋ฆฌ๋ GitHub์์</a> ยท <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
11
|
+
<a href="https://github.com/wjgoarxiv/litclaude/blob/main/README_ko-KR.md">์ ์ฒด ์๋ด์ ์คํฌ ๊ฐค๋ฌ๋ฆฌ๋ GitHub์์</a> ยท <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/README.md">English</a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
Claude Code ํ๋กฌํํธ์ `lit`์ ๋ถ์ด๋ฉด Claude๊ฐ ๋ชฉํ๋ฅผ ๊ณ ์ ํ๊ณ , ์คํจํ๋ ํ
์คํธ๋ฅผ ๋จผ์ ์ฐ๊ณ ,
|
|
@@ -71,7 +71,7 @@ LitClaude๋ ํ๋ก์ ํธ ์ํ๋ฅผ `.litclaude/`์ ๋๊ณ , ๊ทธ ์์ `.gitign
|
|
|
71
71
|
## ๋ ๋ณด๊ธฐ
|
|
72
72
|
|
|
73
73
|
- [์ ์ฒด ์๋ด์ ์คํฌ ๊ฐค๋ฌ๋ฆฌ (GitHub)](https://github.com/wjgoarxiv/litclaude/blob/main/README_ko-KR.md)
|
|
74
|
-
- [๊ฐ์ธ์ ๋ณด์ ๋คํธ์ํฌ ๋์](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
75
|
-
- [์ด์ ๊ณผ ์์ ๊ถ ์ถฉ๋](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
76
|
-
- [๋ณ๊ฒฝ ์ด๋ ฅ](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
77
|
-
- [MIT ๋ผ์ด์ ์ค](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.
|
|
74
|
+
- [๊ฐ์ธ์ ๋ณด์ ๋คํธ์ํฌ ๋์](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/privacy.md)
|
|
75
|
+
- [์ด์ ๊ณผ ์์ ๊ถ ์ถฉ๋](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/docs/migration.md)
|
|
76
|
+
- [๋ณ๊ฒฝ ์ด๋ ฅ](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/CHANGELOG.md)
|
|
77
|
+
- [MIT ๋ผ์ด์ ์ค](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.23/LICENSE)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.23"><title>release: 1.0.23</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.23</text></g></svg>
|
package/docs/hooks.md
CHANGED
|
@@ -346,36 +346,65 @@ itself. LitClaude's is `plugins/litclaude/hooks/lit-mod.mjs`, named under `modul
|
|
|
346
346
|
`hooks/hooks.json` next to the settings hooks above. It installs with the plugin and needs no
|
|
347
347
|
setup. The status line stays as it is; the band adds a live layer above the prompt.
|
|
348
348
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
349
|
+
From the first prompt to the end of the session a LIT card sits above the prompt. It keeps the
|
|
350
|
+
same four rows whether LitClaude is working or idle, and takes the status line's accent
|
|
351
|
+
(`LITCLAUDE_HUD_ACCENT`, read from the installed status line command), so the card and the
|
|
352
|
+
status line read as one family:
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
โญโ [๐ฅ LIT] ยท lit-loop โโโโโโโโโโโโโโโโโโโโโโโโโโโ WORK โ โโฎ
|
|
356
|
+
โ โ PLAN โโ [WORK] โโ โ REVIEW goal โโโโโ 2/5 โ
|
|
357
|
+
โ skills [โก lit-plan] [โก litresearch] [โฏ lit-docx] โ
|
|
358
|
+
โฐโ ctx 42% โฐโฐโฑโฑโฑ ยท 5h 31% ยท git main โโโโโโโโโโโโโโโโโโโโโโโฏ
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The brackets stand for the ends of filled pills: rounded caps from the icon font with `nerd`
|
|
362
|
+
glyphs, one cell of the pill's colour with `basic`. The brand is a pink pill, the active phase
|
|
363
|
+
and the skills of this request are pills on the accent with dark text, and skills from earlier
|
|
364
|
+
requests are muted grey pills.
|
|
365
|
+
|
|
366
|
+
- The top edge names the discipline the prompt hook ignited (the last one, dimmed, while the
|
|
367
|
+
session is idle; `ready` before any) and, on the right, the current phase: `WORK โ`, `idle`,
|
|
368
|
+
or `DONE โ` during the finish.
|
|
369
|
+
- The phase row lights `PLAN โโ WORK โโ REVIEW` from the start-work plan, the litgoal goal and
|
|
370
|
+
the discipline (the active phase as a pill, done phases `โ`, the rest `โ`), and on its right shows a pink-to-cyan progress bar with the checked tasks or
|
|
371
|
+
passed criteria (`goal โ 5/5` once a goal is met), the number of live subagents, the autoloop
|
|
372
|
+
count with the time left in its 30-minute budget, and `ctx 76% โ 80%` when the context is
|
|
373
|
+
within five points of the auto-handoff percent.
|
|
374
|
+
- The chips row shows each LitFamily skill that fired as a pill: a Skill tool call naming a LitClaude
|
|
375
|
+
skill, a typed `/litclaude:<name>`, or bare `lit` routing seen through the ignition record.
|
|
376
|
+
A skill fired in the current request is bright, with a short light passing over its name as
|
|
377
|
+
it arrives; when the turn completes it dims into the `recent` list, and a new firing brightens
|
|
378
|
+
it again. Chips follow a dim `skills` label; those that do not fit collapse into a muted `+N`. Skills of other plugins never get a chip.
|
|
379
|
+
With no chip yet, the row shows the next task while work runs, or how to start (`lit <task>`)
|
|
380
|
+
while idle.
|
|
381
|
+
- The bottom edge carries the live figures from the status line: context fill, the five-hour
|
|
382
|
+
rate window and the Git branch (read from `HEAD`, across a linked worktree).
|
|
383
|
+
- A notice takes the chips row for a few seconds, with a toast, when a gate fires: lit-plan not
|
|
384
|
+
saved, interface probe pending or failed, motion gate, context near the auto-handoff percent,
|
|
385
|
+
auto-handoff fired. The completion moment takes it when the last task is checked, the work
|
|
386
|
+
completes or the goal is met: a light passes over the brand, embers scatter, and `COMPLETE`
|
|
387
|
+
shows for about a second.
|
|
388
|
+
- Amber or red when something needs attention: a lane quiet for more than 10 minutes turns the
|
|
389
|
+
border amber and puts `โ <lane> quiet for 12m` on the bottom edge; a failed interface probe
|
|
390
|
+
turns it red.
|
|
391
|
+
|
|
392
|
+
The card is the terminal's width up to 80 columns and every row is fitted to it, with Hangul and
|
|
393
|
+
emoji counted as two cells. Nothing wraps: on a narrow terminal the Git branch goes first, then
|
|
394
|
+
the rate window, the lanes, the autoloop count and the long phase labels (`P โ W โ R`).
|
|
366
395
|
|
|
367
396
|
The spinner gains ` ยท <discipline> ยท n/m` after its ellipsis. `/lit-evidence` opens a pane with
|
|
368
397
|
three tabs (keys `1`, `2`, `3`): the goal's criteria with their last evidence, blockers and the
|
|
369
|
-
newest litgoal ledger entries; the lanes; and the gates.
|
|
370
|
-
drawn at all.
|
|
398
|
+
newest litgoal ledger entries; the lanes; and the gates.
|
|
371
399
|
|
|
372
400
|
Motion: entrances pop in 60 ms apart with a slight overshoot (160 ms each), fills and colour
|
|
373
401
|
changes ease in and out (300 ms and 140 ms), notices slide in over 220 ms and leave over 150 ms,
|
|
374
|
-
|
|
402
|
+
a chip's arrival light runs 400 ms (a plain fade under `subtle`, nothing under `off`), and the
|
|
403
|
+
finish lasts at most 1.2 s. The band's `Client` element animates on Claude Code's frame
|
|
375
404
|
clock only while something moves and stops its timer as soon as everything settles. In Claude
|
|
376
405
|
Code 2.1.292 a terminal row whose only change is a non-ASCII glyph is not repainted, so every
|
|
377
406
|
moving part changes ASCII text or colour: bar cells are coloured spaces, embers are ASCII sparks,
|
|
378
|
-
and a glyph swaps only together with its words.
|
|
407
|
+
a chip's light moves over its ASCII name, and a glyph swaps only together with its words.
|
|
379
408
|
|
|
380
409
|
Options, in `/config` under LitClaude (stored in `pluginConfigs`):
|
|
381
410
|
|
|
@@ -383,14 +412,15 @@ Options, in `/config` under LitClaude (stored in `pluginConfigs`):
|
|
|
383
412
|
| --- | --- | --- |
|
|
384
413
|
| `motion` | `full`, `subtle` (no overshoot, shimmer or embers, shorter), `off` (final frames only) | `full` |
|
|
385
414
|
| `glyphs` | `auto` (Nerd glyphs when the icon font is installed), `nerd`, or `basic` | `auto` |
|
|
386
|
-
| `band` | `auto` (only while work is active), `
|
|
415
|
+
| `band` | `always` (the whole session, idle included), `auto` (only while work is active or a skill of this request is lit), `off` | `always` |
|
|
387
416
|
|
|
388
417
|
Unknown values fall back to the default. The fields are plain strings with no `options` list on
|
|
389
418
|
purpose: Claude Code 2.1.270 and older refuse to load a plugin whose `userConfig` has one, and
|
|
390
419
|
that would take every LitClaude hook down with it. Nerd glyphs reach only the terminal; the
|
|
391
420
|
desktop app and the other surfaces always get the basic set.
|
|
392
421
|
|
|
393
|
-
The rounded pill caps
|
|
422
|
+
The icons on `nerd` (the rounded pill caps U+E0B6 and U+E0B4, the chip bolt and the branch mark)
|
|
423
|
+
come from a Nerd Font. `litclaude-ai install
|
|
394
424
|
--icon-font`, or a Yes to the install question (default No), adds the symbols-only Nerd Font,
|
|
395
425
|
Symbols Nerd Font Mono (MIT), to the per-user font folder (`~/Library/Fonts` on macOS,
|
|
396
426
|
`~/.local/share/fonts` on Linux, then `fc-cache -f` if present; on Windows it prints how to
|
|
@@ -403,8 +433,9 @@ records the two files, `uninstall` removes only those (and only while unchanged)
|
|
|
403
433
|
prints `ICON_FONT:`. With `glyphs` on `auto`, the band checks those font folders by stat at session
|
|
404
434
|
start and says what it decided on the `/lit-evidence` gates tab.
|
|
405
435
|
|
|
406
|
-
The mod is read-only. It reads LitClaude's own state
|
|
407
|
-
the
|
|
436
|
+
The mod is read-only. It reads LitClaude's own state, the Git `HEAD` of the working directory and
|
|
437
|
+
the merged settings (for the status line's accent) and nothing else: the ignition record in the
|
|
438
|
+
per-user temp folder, `.litclaude/boulder.json` (found the way the hooks find it, across a
|
|
408
439
|
linked worktree), and under the outermost Git root's `.litclaude/` the litgoal goal, autoloop
|
|
409
440
|
counter and ledger, the lit-plan, interface-probe and motion turn records, and the auto-handoff
|
|
410
441
|
settings and session record. Each file must be a regular file under its size cap that resolves
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litfamily/litclaude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "Claude Code plugin: add lit to a prompt and Claude plans, tests, checks and reviews the work, then leaves a handoff for the next session.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litclaude",
|
|
3
3
|
"description": "Claude Code-native workflow plugin with a bounded-authority start-work lifecycle.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.23",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "LitClaude contributors"
|
|
7
7
|
},
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"band": {
|
|
31
31
|
"type": "string",
|
|
32
32
|
"title": "LIT band",
|
|
33
|
-
"description": "When the
|
|
34
|
-
"default": "
|
|
33
|
+
"description": "When the LIT card shows above the prompt: always (the whole session, idle included), auto (only while LitClaude work is active or a skill of this request is lit) or off.",
|
|
34
|
+
"default": "always"
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
toneText,
|
|
15
15
|
} from "../lib/hud-accent-palette.mjs";
|
|
16
16
|
import { latestUsageTokensCached } from "../lib/cache-measurement.mjs";
|
|
17
|
-
import { ignitionStatePath
|
|
17
|
+
import { ignitionStatePath } from "../lib/hud-ignition.mjs";
|
|
18
18
|
import { jevHudState } from "../lib/jev-skill-hint.mjs";
|
|
19
19
|
import { resolveProjectStateRoot } from "../lib/project-state-root.mjs";
|
|
20
20
|
import { recordContextUsage, resolveAutoHandoff } from "../lib/auto-handoff.mjs";
|
|
@@ -344,13 +344,10 @@ const main = async () => {
|
|
|
344
344
|
const contextText = contextPct === null
|
|
345
345
|
? `${text("ctx")} ${text(`?/${windowText}`)}`
|
|
346
346
|
: `${text("ctx")} ${accent("[")}${makeContextBar(contextPct)}${accent("]")} ${text(`${contextPct}%/${windowText}`)}`;
|
|
347
|
-
const ignition = renderIgnitionSegment(readIgnitionState(status.session_id), {
|
|
348
|
-
depth: plainOutput ? "plain" : colorDepth,
|
|
349
|
-
});
|
|
350
347
|
const effort = effortLabel(status);
|
|
351
348
|
const jev = jevBadge(status);
|
|
352
349
|
const autoHandoff = autoHandoffSegment(status, knownContextPct);
|
|
353
|
-
const line = `${prefix}
|
|
350
|
+
const line = `${prefix} ${accent("|")} ${text(model)}${effort ? ` ${text(effort)}` : ""}${jev ? ` ${jev}` : ""} ${accent(sep)} ${contextText}${autoHandoff ? ` ${accent(sep)} ${autoHandoff}` : ""}${usageText}${git ? ` ${accent(sep)} ${text("git")} ${text(git)}` : ""}`;
|
|
354
351
|
process.stdout.write(`${line}\n`);
|
|
355
352
|
|
|
356
353
|
const lastMessage = latestUserMessage(status.transcript_path);
|
|
@@ -39,6 +39,6 @@ export default function LitBand(props, surface) {
|
|
|
39
39
|
if (surface.state?.live !== holder) surface.setState({ live: holder, now: holder.now });
|
|
40
40
|
|
|
41
41
|
if (!model.visible && !timelineBusy(holder.timeline, holder.now, profile)) return empty();
|
|
42
|
-
const frame = composeBand(model, sampleTimeline(holder.timeline, holder.now, profile), { columns: props.columns, glyphs: props.glyphs });
|
|
42
|
+
const frame = composeBand(model, sampleTimeline(holder.timeline, holder.now, profile), { columns: props.columns, glyphs: props.glyphs, accent: props.accent });
|
|
43
43
|
return renderBand(surface.elements, frame);
|
|
44
44
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
// LitClaude's Claude Code mod (hooks/hooks.json `modules`). It draws the LIT
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// LitClaude's Claude Code mod (hooks/hooks.json `modules`). It draws the LIT card above the prompt
|
|
2
|
+
// for the whole session (the `band` setting can limit it to active work or turn it off), lights a
|
|
3
|
+
// chip for each LitFamily skill that fires, adds the discipline and progress to the spinner, raises
|
|
4
|
+
// gate notices, and serves /lit-evidence. It is read-only: it reads LitClaude's own state files and
|
|
5
|
+
// the Git HEAD through lib/mod/state.mjs, the settings for the status line's accent, and the host's
|
|
6
|
+
// live figures, and never writes, spawns, fetches or calls a model.
|
|
5
7
|
// Claude Code releases before mods ignore the `modules` key and keep running the settings hooks.
|
|
6
8
|
//
|
|
7
9
|
// The mods loader only follows `$` into functions declared at the top of this file, so each helper
|
|
@@ -10,8 +12,9 @@
|
|
|
10
12
|
import { composeBand, composeEvidence } from "../lib/mod/frame.mjs";
|
|
11
13
|
import { TIMINGS } from "../lib/mod/motion.mjs";
|
|
12
14
|
import { renderBand, renderRows } from "../lib/mod/render.mjs";
|
|
13
|
-
import { normalizeModSettings, resolveGlyphs } from "../lib/mod/settings.mjs";
|
|
14
|
-
import {
|
|
15
|
+
import { accentFromSettings, normalizeModSettings, resolveGlyphs } from "../lib/mod/settings.mjs";
|
|
16
|
+
import { chipStates, litSkillId, noteSkill } from "../lib/mod/skills.mjs";
|
|
17
|
+
import { detectIconFont, readGitBranch, readLedgerTail, readLitSnapshot } from "../lib/mod/state.mjs";
|
|
15
18
|
import { settledSample } from "../lib/mod/timeline.mjs";
|
|
16
19
|
import {
|
|
17
20
|
EMPTY_ACTIVITY,
|
|
@@ -55,6 +58,12 @@ const createLive = (options) => ({
|
|
|
55
58
|
evidenceTab: "goal",
|
|
56
59
|
ledger: [],
|
|
57
60
|
celebrated: new Set(),
|
|
61
|
+
accent: undefined,
|
|
62
|
+
git: null,
|
|
63
|
+
fired: [],
|
|
64
|
+
settledAt: 0,
|
|
65
|
+
ignitionAt: undefined,
|
|
66
|
+
lastDiscipline: null,
|
|
58
67
|
});
|
|
59
68
|
|
|
60
69
|
const stateIo = ($) => ({
|
|
@@ -77,6 +86,9 @@ async function rebuild($, live) {
|
|
|
77
86
|
isWorking: live.isWorking,
|
|
78
87
|
notice: live.notice,
|
|
79
88
|
completion: live.completion,
|
|
89
|
+
skills: chipStates(live.fired, live.settledAt),
|
|
90
|
+
lastDiscipline: live.lastDiscipline,
|
|
91
|
+
git: live.git,
|
|
80
92
|
});
|
|
81
93
|
live.model = next;
|
|
82
94
|
if (previous?.visible && !next.visible && !live.exiting) {
|
|
@@ -112,12 +124,32 @@ function celebrate($, live, finish) {
|
|
|
112
124
|
});
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
// A firing of a skill or command by name; anything that is not LitClaude's is ignored.
|
|
128
|
+
async function noteFiring($, live, name) {
|
|
129
|
+
const id = litSkillId(name);
|
|
130
|
+
if (!id) return;
|
|
131
|
+
live.fired = noteSkill(live.fired, id, await $.clock.now());
|
|
132
|
+
live.lastDiscipline = id;
|
|
133
|
+
await rebuild($, live);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The prompt hook rewrites the ignition record on every prompt; a new record naming a discipline is
|
|
137
|
+
// a bare `lit` (or slash) routing, and lights that skill's chip at the time the hook wrote it.
|
|
138
|
+
function noteIgnition(live, ignition, now) {
|
|
139
|
+
if (!ignition || ignition.at === live.ignitionAt) return;
|
|
140
|
+
live.ignitionAt = ignition.at;
|
|
141
|
+
live.lastDiscipline = ignition.discipline;
|
|
142
|
+
const id = litSkillId(ignition.discipline);
|
|
143
|
+
if (id) live.fired = noteSkill(live.fired, id, ignition.at ?? now);
|
|
144
|
+
}
|
|
145
|
+
|
|
115
146
|
async function refresh($, live) {
|
|
116
147
|
if (live.refreshing) return;
|
|
117
148
|
live.refreshing = true;
|
|
118
149
|
try {
|
|
119
150
|
const [cwd, sessionId] = await Promise.all([$.session.cwd(), $.session.id()]);
|
|
120
151
|
const fresh = await readLitSnapshot(stateIo($), { cwd, sessionId, env: live.env });
|
|
152
|
+
live.git = await readGitBranch(stateIo($), cwd).catch(() => live.git);
|
|
121
153
|
live.agents = await $.agent.list().catch(() => []);
|
|
122
154
|
live.usage = await $.session.usage().catch(() => live.usage);
|
|
123
155
|
const now = await $.clock.now();
|
|
@@ -126,6 +158,7 @@ async function refresh($, live) {
|
|
|
126
158
|
const before = live.snapshot;
|
|
127
159
|
const previousModel = live.model;
|
|
128
160
|
live.snapshot = fresh;
|
|
161
|
+
noteIgnition(live, fresh.ignition, now);
|
|
129
162
|
const finish = detectCompletion(before, fresh);
|
|
130
163
|
if (finish) celebrate($, live, finish);
|
|
131
164
|
const next = await rebuild($, live);
|
|
@@ -167,6 +200,8 @@ export const register = (on, options) => {
|
|
|
167
200
|
XDG_DATA_HOME: await $.env.get("XDG_DATA_HOME"),
|
|
168
201
|
LOCALAPPDATA: await $.env.get("LOCALAPPDATA"),
|
|
169
202
|
});
|
|
203
|
+
live.settledAt = await $.clock.now();
|
|
204
|
+
live.accent = accentFromSettings(await $.settings.read().catch(() => null), await $.env.get("LITCLAUDE_HUD_ACCENT"));
|
|
170
205
|
await $.command.register({ name: PANE, description: "Show LitClaude goal criteria, evidence, lanes and gates in a pane" });
|
|
171
206
|
await refresh($, live);
|
|
172
207
|
$.clock.every(POLL_MS, () => {
|
|
@@ -183,6 +218,8 @@ export const register = (on, options) => {
|
|
|
183
218
|
|
|
184
219
|
on("turn.complete", async ($, e, next) => {
|
|
185
220
|
live.isWorking = false;
|
|
221
|
+
// The request is over: its chips dim to the recent list until a skill fires again.
|
|
222
|
+
live.settledAt = await $.clock.now();
|
|
186
223
|
refreshSoon($, live);
|
|
187
224
|
return next(e);
|
|
188
225
|
});
|
|
@@ -194,12 +231,20 @@ export const register = (on, options) => {
|
|
|
194
231
|
});
|
|
195
232
|
|
|
196
233
|
on("tool.call", async ($, e, next) => {
|
|
234
|
+
if (e.tool === "Skill") await noteFiring($, live, e.skill);
|
|
197
235
|
const result = await next(e);
|
|
198
236
|
if (typeof e.agentId === "string") live.activity = noteAgentActivity(live.activity, e.agentId, await $.clock.now());
|
|
199
237
|
refreshSoon($, live);
|
|
200
238
|
return result;
|
|
201
239
|
}).catch(($, e, next) => next(e));
|
|
202
240
|
|
|
241
|
+
// A typed `/litclaude:<name>` and the Skill tool both expand the skill's prompt here; the text
|
|
242
|
+
// passes through untouched.
|
|
243
|
+
on("skill.prompt", async ($, e, next) => {
|
|
244
|
+
await noteFiring($, live, e.skill);
|
|
245
|
+
return next(e);
|
|
246
|
+
}).catch(($, e, next) => next(e));
|
|
247
|
+
|
|
203
248
|
on("ui.fault", async ($, e, next) => {
|
|
204
249
|
if (e.component === "AbovePrompt") live.clientFailed = true;
|
|
205
250
|
return next(e);
|
|
@@ -214,9 +259,9 @@ export const register = (on, options) => {
|
|
|
214
259
|
const columns = Math.max(20, e.props.bodyColumns ?? e.viewport?.columns ?? 80);
|
|
215
260
|
let drawn = null;
|
|
216
261
|
if (elements.Client && !live.clientFailed) {
|
|
217
|
-
drawn = elements.Client({ key: "lit-band", module: "./lit-band-client.mjs", props: { model, columns, glyphs, motion: settings.motion } });
|
|
262
|
+
drawn = elements.Client({ key: "lit-band", module: "./lit-band-client.mjs", props: { model, columns, glyphs, motion: settings.motion, accent: live.accent } });
|
|
218
263
|
} else if (model.visible) {
|
|
219
|
-
drawn = renderBand(elements, composeBand(model, settledSample(model), { columns, glyphs }));
|
|
264
|
+
drawn = renderBand(elements, composeBand(model, settledSample(model), { columns, glyphs, accent: live.accent }));
|
|
220
265
|
}
|
|
221
266
|
const below = await next(e);
|
|
222
267
|
if (!drawn) return below;
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
|
-
// Per-session ignition record: the prompt hook writes it on every UserPromptSubmit and the
|
|
6
|
-
//
|
|
7
|
-
// the current turn instead of anything the model chooses to
|
|
8
|
-
// durable, so the record lives in the per-user temp directory:
|
|
9
|
-
// root (activation must stay side-effect-free there) and not in the
|
|
10
|
-
|
|
11
|
-
const IGNITION_PALETTE = Object.freeze({
|
|
12
|
-
truecolor: "\x1b[38;2;255;99;55m",
|
|
13
|
-
"256": "\x1b[38;5;203m",
|
|
14
|
-
"16": "\x1b[91m",
|
|
15
|
-
});
|
|
5
|
+
// Per-session ignition record: the prompt hook writes it on every UserPromptSubmit and the LIT
|
|
6
|
+
// card (hooks/lit-mod.mjs, through lib/mod/state.mjs) reads it back, so the card reflects the
|
|
7
|
+
// discipline actually selected for the current turn instead of anything the model chooses to
|
|
8
|
+
// draw. Mode is per turn and never durable, so the record lives in the per-user temp directory:
|
|
9
|
+
// not in the repository state root (activation must stay side-effect-free there) and not in the
|
|
10
|
+
// home directory.
|
|
16
11
|
|
|
17
12
|
export const ignitionStateRoot = (env = process.env) => {
|
|
18
13
|
const override = env.LITCLAUDE_HUD_STATE_ROOT?.trim();
|
|
@@ -47,28 +42,3 @@ export function writeIgnitionState({ sessionId, discipline }, env = process.env,
|
|
|
47
42
|
renameSync(staging, target);
|
|
48
43
|
return record;
|
|
49
44
|
}
|
|
50
|
-
|
|
51
|
-
export function readIgnitionState(sessionId, env = process.env) {
|
|
52
|
-
const target = ignitionStatePath(sessionId, env);
|
|
53
|
-
if (!existsSync(target)) return null;
|
|
54
|
-
try {
|
|
55
|
-
const parsed = JSON.parse(readFileSync(target, "utf8"));
|
|
56
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
57
|
-
return {
|
|
58
|
-
sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : null,
|
|
59
|
-
discipline: typeof parsed.discipline === "string" && parsed.discipline ? parsed.discipline : null,
|
|
60
|
-
at: typeof parsed.at === "string" ? parsed.at : null,
|
|
61
|
-
};
|
|
62
|
-
} catch {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function renderIgnitionSegment(state, { depth = "plain" } = {}) {
|
|
68
|
-
const discipline = state?.discipline ?? null;
|
|
69
|
-
if (!discipline) return "";
|
|
70
|
-
const label = ignitionMark(discipline);
|
|
71
|
-
if (depth === "plain") return label;
|
|
72
|
-
const paint = IGNITION_PALETTE[depth] ?? IGNITION_PALETTE["256"];
|
|
73
|
-
return `\x1b[1m${paint}${label}\x1b[0m`;
|
|
74
|
-
}
|