@litfamily/litclaude 1.0.23 → 1.0.24
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 +8 -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 +10 -2
- package/package.json +1 -1
- package/plugins/litclaude/.claude-plugin/plugin.json +1 -1
- package/plugins/litclaude/bin/litclaude-mcp.js +1 -1
- package/plugins/litclaude/hooks/lit-mod.mjs +30 -2
- package/plugins/litclaude/lib/canonical-skill-resources.mjs +4 -4
- package/plugins/litclaude/lib/mod/frame.mjs +1 -1
- package/plugins/litclaude/lib/mod/view.mjs +30 -1
- package/plugins/litclaude/lib/office-runtime-lock/package-lock.json +167 -119
- package/plugins/litclaude/lib/office-runtime-lock/package.json +1 -1
- package/plugins/litclaude/skills/readme-studio/templates/hyperframes-cover/package-lock.json +169 -121
- package/plugins/litclaude/skills/readme-studio/templates/remotion-cover/package-lock.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.24 - 2026-10-12
|
|
4
|
+
|
|
5
|
+
- The LIT card no longer drops to idle while background work is still running. When the main turn ends after starting background subagents or a workflow, the card stays at work and counts the running agents until the last one finishes, and it never reads `DONE` while an agent is still busy. A workflow's agents now count too, and a subagent that finishes in the middle of a turn no longer dims the chips of the request that is still running. An agent that never reports back stops counting after 30 quiet minutes, so it cannot hold the card at work.
|
|
6
|
+
- The README shows the LIT card as it really appears: resting before anything runs, at work with the skill's chip lit, and after the turn, when the chip dims into the recent list.
|
|
7
|
+
- The README walks through automatic handoff with terminal pictures: turning it on with `lit-handoff auto on 65`, checking and turning it off, the reminder to run `/compact`, and the two shell variables that switch it on before Claude Code starts.
|
|
8
|
+
- The install section has a prompt you can paste into a coding agent so it installs LitClaude for you. The agent shows what would change and waits for your yes, runs the doctor, and reminds you to restart Claude Code.
|
|
9
|
+
- The office runtime now pins sharp 0.35.5, and the README Studio cover templates use sharp 0.35.5 and source-map-js 1.2.2.
|
|
10
|
+
|
|
3
11
|
## 1.0.23 - 2026-10-12
|
|
4
12
|
|
|
5
13
|
- 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.
|
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.24/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/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.24/docs/assets/readme/badge-version.svg" alt="1.0.24" />
|
|
8
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/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.24/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.24/docs/privacy.md)
|
|
77
|
+
- [Migration and ownership conflicts](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/docs/migration.md)
|
|
78
|
+
- [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/CHANGELOG.md)
|
|
79
|
+
- [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/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.24/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/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.24/docs/assets/readme/badge-version.svg" alt="1.0.24" />
|
|
8
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/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.24/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.24/docs/privacy.md)
|
|
75
|
+
- [이전과 소유권 충돌](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/docs/migration.md)
|
|
76
|
+
- [변경 이력](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/CHANGELOG.md)
|
|
77
|
+
- [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.24/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.24"><title>release: 1.0.24</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.24</text></g></svg>
|
package/docs/hooks.md
CHANGED
|
@@ -365,10 +365,11 @@ requests are muted grey pills.
|
|
|
365
365
|
|
|
366
366
|
- The top edge names the discipline the prompt hook ignited (the last one, dimmed, while the
|
|
367
367
|
session is idle; `ready` before any) and, on the right, the current phase: `WORK ●`, `idle`,
|
|
368
|
-
or `DONE ✓` during the finish.
|
|
368
|
+
or `DONE ✓` during the finish. While a subagent is still running the card stays at work
|
|
369
|
+
(`LIVE ●` when no phase is active) and never reads `DONE`, even after the main turn ended.
|
|
369
370
|
- The phase row lights `PLAN ━━ WORK ━━ REVIEW` from the start-work plan, the litgoal goal and
|
|
370
371
|
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
|
+
passed criteria (`goal ✓ 5/5` once a goal is met), the number of live subagents (`● 2 lanes`), the autoloop
|
|
372
373
|
count with the time left in its 30-minute budget, and `ctx 76% → 80%` when the context is
|
|
373
374
|
within five points of the auto-handoff percent.
|
|
374
375
|
- The chips row shows each LitFamily skill that fired as a pill: a Skill tool call naming a LitClaude
|
|
@@ -389,6 +390,13 @@ requests are muted grey pills.
|
|
|
389
390
|
border amber and puts `⚠ <lane> quiet for 12m` on the bottom edge; a failed interface probe
|
|
390
391
|
turns it red.
|
|
391
392
|
|
|
393
|
+
Subagents and teammates come from Claude Code's agent list. A workflow script's agents never
|
|
394
|
+
appear there, so the mod also records every agent at `agent.spawn` and counts it until that
|
|
395
|
+
agent's own `turn.complete` arrives. A subagent's turn ending does not end the main turn: the
|
|
396
|
+
chips of the current request stay bright until the main turn completes. An agent the list shows
|
|
397
|
+
as ended stops counting at once, and one quiet for 30 minutes (no tool call, no status change)
|
|
398
|
+
stops counting too, so a crashed or lost agent cannot hold the card at work.
|
|
399
|
+
|
|
392
400
|
The card is the terminal's width up to 80 columns and every row is fitted to it, with Hangul and
|
|
393
401
|
emoji counted as two cells. Nothing wraps: on a narrow terminal the Git branch goes first, then
|
|
394
402
|
the rate window, the lanes, the autoloop count and the long phase labels (`P ━ W ━ R`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litfamily/litclaude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
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": {
|
|
@@ -22,7 +22,11 @@ import {
|
|
|
22
22
|
buildEvidenceModel,
|
|
23
23
|
detectCompletion,
|
|
24
24
|
diffNotices,
|
|
25
|
+
mergeSpawned,
|
|
25
26
|
noteAgentActivity,
|
|
27
|
+
noteSpawn,
|
|
28
|
+
noteSpawnActivity,
|
|
29
|
+
noteSpawnEnded,
|
|
26
30
|
spinnerSuffix,
|
|
27
31
|
trackAgents,
|
|
28
32
|
} from "../lib/mod/view.mjs";
|
|
@@ -44,6 +48,7 @@ const createLive = (options) => ({
|
|
|
44
48
|
snapshot: null,
|
|
45
49
|
model: null,
|
|
46
50
|
agents: [],
|
|
51
|
+
spawned: {},
|
|
47
52
|
usage: null,
|
|
48
53
|
activity: EMPTY_ACTIVITY,
|
|
49
54
|
statusById: {},
|
|
@@ -150,9 +155,10 @@ async function refresh($, live) {
|
|
|
150
155
|
const [cwd, sessionId] = await Promise.all([$.session.cwd(), $.session.id()]);
|
|
151
156
|
const fresh = await readLitSnapshot(stateIo($), { cwd, sessionId, env: live.env });
|
|
152
157
|
live.git = await readGitBranch(stateIo($), cwd).catch(() => live.git);
|
|
153
|
-
|
|
158
|
+
const listed = await $.agent.list().catch(() => []);
|
|
154
159
|
live.usage = await $.session.usage().catch(() => live.usage);
|
|
155
160
|
const now = await $.clock.now();
|
|
161
|
+
({ agents: live.agents, spawned: live.spawned } = mergeSpawned(listed, live.spawned, now));
|
|
156
162
|
live.activity = trackAgents(live.activity, live.agents, live.statusById, now);
|
|
157
163
|
live.statusById = Object.fromEntries(live.agents.map((agent) => [agent.id, agent.status]));
|
|
158
164
|
const before = live.snapshot;
|
|
@@ -217,6 +223,12 @@ export const register = (on, options) => {
|
|
|
217
223
|
});
|
|
218
224
|
|
|
219
225
|
on("turn.complete", async ($, e, next) => {
|
|
226
|
+
// A subagent's or teammate's turn ends its run, not the main turn the card follows.
|
|
227
|
+
if (typeof e.agentId === "string") {
|
|
228
|
+
live.spawned = noteSpawnEnded(live.spawned, e.agentId);
|
|
229
|
+
refreshSoon($, live);
|
|
230
|
+
return next(e);
|
|
231
|
+
}
|
|
220
232
|
live.isWorking = false;
|
|
221
233
|
// The request is over: its chips dim to the recent list until a skill fires again.
|
|
222
234
|
live.settledAt = await $.clock.now();
|
|
@@ -232,12 +244,28 @@ export const register = (on, options) => {
|
|
|
232
244
|
|
|
233
245
|
on("tool.call", async ($, e, next) => {
|
|
234
246
|
if (e.tool === "Skill") await noteFiring($, live, e.skill);
|
|
247
|
+
if (typeof e.agentId === "string") live.spawned = noteSpawnActivity(live.spawned, e.agentId, await $.clock.now());
|
|
235
248
|
const result = await next(e);
|
|
236
|
-
if (typeof e.agentId === "string")
|
|
249
|
+
if (typeof e.agentId === "string") {
|
|
250
|
+
const now = await $.clock.now();
|
|
251
|
+
live.activity = noteAgentActivity(live.activity, e.agentId, now);
|
|
252
|
+
live.spawned = noteSpawnActivity(live.spawned, e.agentId, now);
|
|
253
|
+
}
|
|
237
254
|
refreshSoon($, live);
|
|
238
255
|
return result;
|
|
239
256
|
}).catch(($, e, next) => next(e));
|
|
240
257
|
|
|
258
|
+
// Every subagent, teammate and workflow agent starts here. A workflow's never shows in
|
|
259
|
+
// `$.agent.list()`, so the card keeps its own record until the agent's turn.complete.
|
|
260
|
+
on("agent.spawn", async ($, e, next) => {
|
|
261
|
+
const result = await next(e);
|
|
262
|
+
if (typeof result?.agentId === "string") {
|
|
263
|
+
live.spawned = noteSpawn(live.spawned, { id: result.agentId, description: e.description, type: e.subagentType, at: await $.clock.now() });
|
|
264
|
+
refreshSoon($, live);
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}).catch(($, e, next) => next(e));
|
|
268
|
+
|
|
241
269
|
// A typed `/litclaude:<name>` and the Skill tool both expand the skill's prompt here; the text
|
|
242
270
|
// passes through untouched.
|
|
243
271
|
on("skill.prompt", async ($, e, next) => {
|
|
@@ -11,8 +11,8 @@ export const canonicalSkillResourceManifest = Object.freeze(
|
|
|
11
11
|
["lib/durable-plan.mjs", "33fc7c2ba6305602c73650c8f29e6141e93b8ac32a444f772807e11bf8c22e44"],
|
|
12
12
|
["lib/immutable-expected-file-map.mjs", "4061486bc8eda8a53e56b7b346a1e400e7c858c0a682c83362fea460b46dfd4b"],
|
|
13
13
|
["lib/lit-mark.mjs", "cb57921a3da0ff983c74e9426dcd3b2379db090198743139e379a059c128021e"],
|
|
14
|
-
["lib/office-runtime-lock/package-lock.json", "
|
|
15
|
-
["lib/office-runtime-lock/package.json", "
|
|
14
|
+
["lib/office-runtime-lock/package-lock.json", "16d19b420b410035e9ca6886133368444a2b438121919857b3e8915be280c6a6"],
|
|
15
|
+
["lib/office-runtime-lock/package.json", "5e4b0c7ee7bd5ff88c888f7f46152c605caf128cc4e7bfd16423571210925394"],
|
|
16
16
|
["lib/office-runtime-lock/requirements.lock", "7c63ce0de1ae9a912d1e546e9c01dd99a705d4a50435f36040d351cc3e6e6014"],
|
|
17
17
|
["lib/office-runtime.mjs", "24d108a4ae7aa98dd0d6f2fa66686d7c9f020ecf42fe641241be2869075bbea3"],
|
|
18
18
|
["lib/office_data.py", "0f4c2449622b6d59630e60508594d181279052c41918df4370e11a68df1ee239"],
|
|
@@ -848,11 +848,11 @@ export const canonicalSkillResourceManifest = Object.freeze(
|
|
|
848
848
|
["skills/readme-studio/templates/cover-source.json", "182051737e4cda9002360316bb2c01b500ecc20c27953a2d332d908f044f5cd7"],
|
|
849
849
|
["skills/readme-studio/templates/hyperframes-cover/index.html", "edaee52e01cc7d90524b43e3da80b4bc6514fef1d17b4317cbf15ed5e36a5c67"],
|
|
850
850
|
["skills/readme-studio/templates/hyperframes-cover/index.motion.json", "bef879ce5abe7810d73cc733ab635884ec807eccd7bc5e1c9bc9900049187249"],
|
|
851
|
-
["skills/readme-studio/templates/hyperframes-cover/package-lock.json", "
|
|
851
|
+
["skills/readme-studio/templates/hyperframes-cover/package-lock.json", "5adacd09e4608c8c3917ca0245052e4d3abae3a566ebe07a2fdad593c67cb91c"],
|
|
852
852
|
["skills/readme-studio/templates/hyperframes-cover/package.json", "0993c6f04892f5a318cc99030de653b7c68d3ea6789c69b30b6a2ae80e4120f0"],
|
|
853
853
|
["skills/readme-studio/templates/readme-cover-section.md", "1dc4c536f65d3437f12e09a76170a8dc76557225587c7453631a06604a21573d"],
|
|
854
854
|
["skills/readme-studio/templates/readme-facts.json", "fb95b8aece0e8591726d3a36e25cc66712b0c6e57835c848db0f07b745e73205"],
|
|
855
|
-
["skills/readme-studio/templates/remotion-cover/package-lock.json", "
|
|
855
|
+
["skills/readme-studio/templates/remotion-cover/package-lock.json", "422af74482c6c36e5bfaa86c2fe6a78e7ff54d27d3119f9c462685333a449857"],
|
|
856
856
|
["skills/readme-studio/templates/remotion-cover/package.json", "7921a415cf96343b46c9a2a3bea9160ecc807710fb6a34ba227704eff7d83887"],
|
|
857
857
|
["skills/readme-studio/templates/remotion-cover/src/index.tsx", "81cf3edd996a98ddca46465d4bc84ca0203ecd3bdce1736bf1dc3d9f2bc15af9"],
|
|
858
858
|
["skills/readme-studio/templates/typography/package-lock.json", "1d6dc1ae0d06d0a3a5e1b2b45bea1d40be7497f1f2fb856eefd33cc3649af5bd"],
|
|
@@ -163,7 +163,7 @@ const indicatorSpans = (model, sample, glyphs, accent) => {
|
|
|
163
163
|
if (model.idle) return [span("idle", { fg: PALETTE.dim })];
|
|
164
164
|
const current = PHASES.find((id) => model.phases[id] === "now");
|
|
165
165
|
if (current) return [span(`${LABELS[current]} ${warned(current, sample) ? glyphs.warn : glyphs.now}`, { fg: phaseColor(current, sample, inkOf(accent)), bold: true })];
|
|
166
|
-
if (PHASES.every((id) => model.phases[id] === "done")) return [span(`DONE ${glyphs.done}`, { fg: PALETTE.green, bold: true })];
|
|
166
|
+
if (PHASES.every((id) => model.phases[id] === "done") && model.lanes.count === 0) return [span(`DONE ${glyphs.done}`, { fg: PALETTE.green, bold: true })];
|
|
167
167
|
return [span(`LIVE ${glyphs.now}`, { fg: accent, bold: true })];
|
|
168
168
|
};
|
|
169
169
|
|
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
import { AUTOLOOP_LIMITS, GATE_LIMITS } from "./state.mjs";
|
|
6
6
|
|
|
7
7
|
export const STALL_MS = 10 * 60 * 1000;
|
|
8
|
+
// An agent quiet for this long no longer counts as running: a crashed loop or a lost report must not
|
|
9
|
+
// hold the card at work for the rest of the session.
|
|
10
|
+
export const AGENT_STALE_MS = 30 * 60 * 1000;
|
|
8
11
|
// The context warning starts this many points before the auto-handoff percent.
|
|
9
12
|
export const CONTEXT_LEAD = 5;
|
|
10
13
|
|
|
11
14
|
const LIVE_AGENT = new Set(["running", "pending", "waiting", "idle"]);
|
|
15
|
+
const ENDED_AGENT = new Set(["completed", "failed", "killed"]);
|
|
12
16
|
const NONTERMINAL_GOAL = new Set(["active", "blocked", "review_blocked", "needs_user_decision"]);
|
|
13
17
|
const WORK_DISCIPLINES = new Set(["lit-loop", "litwork", "litgoal", "start-work", "native-workflow"]);
|
|
14
18
|
|
|
@@ -44,6 +48,31 @@ export const trackAgents = (activity, agents, previousStatus, now) => {
|
|
|
44
48
|
export const noteAgentActivity = (activity, agentId, now) =>
|
|
45
49
|
activity.first[agentId] === undefined ? activity : { ...activity, last: { ...activity.last, [agentId]: now } };
|
|
46
50
|
|
|
51
|
+
// `spawned` maps an agent id to what `agent.spawn` said of it and when it was last seen working. It
|
|
52
|
+
// covers the agents `$.agent.list()` never shows (a workflow script's) and the moment before the
|
|
53
|
+
// list catches up; the agent's own `turn.complete` ends it.
|
|
54
|
+
export const noteSpawn = (spawned, { id, description, type, at }) => ({ ...spawned, [id]: { id, description, type, last: at } });
|
|
55
|
+
|
|
56
|
+
export const noteSpawnActivity = (spawned, id, at) => (spawned[id] ? { ...spawned, [id]: { ...spawned[id], last: at } } : spawned);
|
|
57
|
+
|
|
58
|
+
export const noteSpawnEnded = (spawned, id) => Object.fromEntries(Object.entries(spawned).filter(([key]) => key !== id));
|
|
59
|
+
|
|
60
|
+
// Folds the spawn records into one `$.agent.list()` reading. The list speaks for the agents it shows:
|
|
61
|
+
// a record whose agent it lists as ended is dropped. A record it does not show runs until its
|
|
62
|
+
// `turn.complete`, or until it has been quiet for AGENT_STALE_MS.
|
|
63
|
+
export const mergeSpawned = (listed, spawned, now) => {
|
|
64
|
+
const byId = new Map(listed.map((agent) => [agent.id, agent]));
|
|
65
|
+
const kept = {};
|
|
66
|
+
const unlisted = [];
|
|
67
|
+
for (const record of Object.values(spawned)) {
|
|
68
|
+
const row = byId.get(record.id);
|
|
69
|
+
if (row ? ENDED_AGENT.has(row.status) : now - record.last > AGENT_STALE_MS) continue;
|
|
70
|
+
kept[record.id] = record;
|
|
71
|
+
if (!row) unlisted.push({ id: record.id, description: record.description, type: record.type, status: "running" });
|
|
72
|
+
}
|
|
73
|
+
return { agents: [...listed, ...unlisted], spawned: kept };
|
|
74
|
+
};
|
|
75
|
+
|
|
47
76
|
export const stalledLanes = (agents, activity, now, thresholdMs = STALL_MS) =>
|
|
48
77
|
agents
|
|
49
78
|
.filter((agent) => LIVE_AGENT.has(agent.status) && Number.isFinite(activity.last[agent.id]) && now - activity.last[agent.id] > thresholdMs)
|
|
@@ -129,7 +158,7 @@ export const buildBandModel = ({ snapshot, agents = [], activity = EMPTY_ACTIVIT
|
|
|
129
158
|
const progress = progressOf(snapshot);
|
|
130
159
|
const completing = completion !== null;
|
|
131
160
|
const phases = phasesOf({ snapshot, discipline, progress, completing });
|
|
132
|
-
const liveAgents = agents.filter((agent) => LIVE_AGENT.has(agent.status));
|
|
161
|
+
const liveAgents = agents.filter((agent) => LIVE_AGENT.has(agent.status) && !(now - activity.last[agent.id] > AGENT_STALE_MS));
|
|
133
162
|
const boulderLanes = (snapshot.work?.lanes ?? []).filter(({ status }) => status === "active");
|
|
134
163
|
const stalls = stalledLanes(agents, activity, now);
|
|
135
164
|
const stalledIds = new Set(stalls.map(({ id }) => id));
|