@litfamily/litclaude 1.0.24 → 1.0.26

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.26 - 2026-10-13
4
+
5
+ - The LIT card now shows where you are in the LIT workflow. A map row lights the current stage of the running family (interview, plan, work, review, loop, research), and a detail line under it says what that stage is doing: the interview round and ambiguity, whether the plan awaits approval, how many tasks are checked and which comes next, each review lane's verdict. On the right of that line the live step names the tool the main loop is running and for how long, or `thinking` between tool calls, and never shows its arguments.
6
+ - The map moves on its own. LitClaude's hooks advance it as lit-plan saves the plan, start-work's tasks are checked, review lanes report their verdicts and a lit-loop criterion goes from RED to GREEN to VERIFY, so the model does not have to report each step. For what the hooks cannot see, such as a loop step or a crucible verdict, the new `litclaude-ai workflow-stage` command records the stage.
7
+ - In the Claude desktop app the card is drawn as one picture. The app sets text in a proportional font, which pulled the terminal card apart there, so the desktop and editor surfaces now get the same rows as a single drawing, moving under the same `motion` setting. The terminal keeps the card it had.
8
+ - A new `notify` option raises a notification through your own notification channel when LIT work completes or a LIT turn longer than three minutes ends. It is `off` unless you turn it on with `/config` or `litclaude-ai config notify finish`.
9
+ - The start-work gate no longer blocks read-only searches. It used to read a grep or rg pattern, a quoted phrase passed to a node script, or `litclaude-ai start-work status` as an unverified path and deny it. Patterns and plain words now count as data, while the searched paths are still checked against the work's grants.
10
+ - `litclaude-ai doctor` reports the card options before the Claude validation step.
11
+ - The README card pictures are retaken to show the workflow map and the detail line.
12
+
13
+ ## 1.0.25 - 2026-10-12
14
+
15
+ - The LIT card now moves while LitClaude is at work, so a glance tells you it is busy. The LIT pill and the border slowly glow from pink to flame orange and back, a light passes over the current phase and the skill chips of this request, and a spark runs along the top edge and back along the bottom one. When the turn ends the card goes still again. With the `motion` setting at `subtle` only the glow stays, and `off` keeps the card still.
16
+ - The card is wider. It now grows with the terminal up to 120 columns instead of 80.
17
+ - The bottom edge shows the weekly usage window next to the five-hour one, as the status line does.
18
+ - The installer sets up the Claude desktop app too. The app's Code tab reads the same settings as the terminal but has no editor for the card's options, so the installer now writes the defaults for `motion`, `glyphs` and `band`, keeping any value you already chose. The new `litclaude-ai config` command shows them, and `litclaude-ai config <option> <value>` changes one from a terminal. Uninstall removes only the defaults it wrote that you never changed.
19
+ - The README demo film now opens on a title card and names each step with a numbered two-line caption, and the card pictures show the new width and the weekly window.
20
+
3
21
  ## 1.0.24 - 2026-10-12
4
22
 
5
23
  - 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.
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.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>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/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.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>
7
+ <img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/assets/readme/badge-version.svg" alt="1.0.26" />
8
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/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.24/README_ko-KR.md">한국어</a>
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.26/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.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)
76
+ - [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/privacy.md)
77
+ - [Migration and ownership conflicts](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/migration.md)
78
+ - [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/CHANGELOG.md)
79
+ - [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/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.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>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/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.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>
7
+ <img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/assets/readme/badge-version.svg" alt="1.0.26" />
8
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/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.24/README.md">English</a>
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.26/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.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)
74
+ - [개인정보와 네트워크 동작](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/privacy.md)
75
+ - [이전과 소유권 충돌](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/docs/migration.md)
76
+ - [변경 이력](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/CHANGELOG.md)
77
+ - [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.26/LICENSE)
@@ -30,6 +30,7 @@ import {
30
30
  themeForAccent,
31
31
  } from "../plugins/litclaude/lib/hud-accent-palette.mjs";
32
32
  import { readPublicSource } from "../plugins/litclaude/lib/public-source-reader/reader.mjs";
33
+ import { MOD_CHOICES } from "../plugins/litclaude/lib/mod/settings.mjs";
33
34
  import { verifyBundledSkillsIntegrity } from "../plugins/litclaude/lib/bundled-skills-integrity.mjs";
34
35
  import { verifyCanonicalFrontendCorpus } from "../plugins/litclaude/lib/canonical-frontend-corpus.mjs";
35
36
  import { verifyCanonicalRuntimeClosures } from "../plugins/litclaude/lib/canonical-runtime-closures.mjs";
@@ -38,6 +39,7 @@ import { runStartWorkContinuationCli } from "../plugins/litclaude/lib/start-work
38
39
  import { runStartWorkCli } from "../plugins/litclaude/lib/start-work-cli.mjs";
39
40
  import { runLitgoalCli } from "../plugins/litclaude/lib/litgoal/cli.mjs";
40
41
  import { runWorkflowCheckCli } from "../plugins/litclaude/lib/workflow-check.mjs";
42
+ import { runWorkflowStageCli } from "../plugins/litclaude/lib/workflow-stage.mjs";
41
43
  import { runWikifyKnowledgeCli } from "../plugins/litclaude/lib/wikify-knowledge-cli.mjs";
42
44
  import { automaticUpdateRoot, runAutomaticUpdate } from "../plugins/litclaude/lib/automatic-update.mjs";
43
45
  import { jevStatusLine } from "../plugins/litclaude/lib/jev-skill-hint.mjs";
@@ -80,12 +82,16 @@ const prewarmMotionRuntime = () => {
80
82
  };
81
83
  const version = packageJson.version;
82
84
 
83
- const usage = `Usage: litclaude-ai [--dry-run] <install|doctor|path|run|update|uninstall|litgoal|wikify|workflow-check|start-work|start-work-next|public-read|motion-runtime> [...args]
85
+ const usage = `Usage: litclaude-ai [--dry-run] <install|doctor|config|path|run|update|uninstall|litgoal|wikify|workflow-check|workflow-stage|start-work|start-work-next|public-read|motion-runtime> [...args]
84
86
  litclaude-ai --version
85
87
 
86
88
  Commands:
87
89
  install Register the packaged LitClaude plugin in Claude Code.
88
90
  doctor Validate the installed LitClaude plugin path.
91
+ config [<option> <value>]
92
+ Show or change the LIT card options (motion, glyphs, band, notify) in
93
+ pluginConfigs, the same values /config edits. Works for the
94
+ desktop app too, which shows no editor for them.
89
95
  path Print the installed Claude plugin path.
90
96
  run -- ... Run Claude Code after the global plugin install.
91
97
  litgoal Manage litgoal runtime state and evidence.
@@ -93,6 +99,9 @@ Commands:
93
99
  workflow-check Verify Dynamic workflow, /goal, and subagent delegation readiness.
94
100
  start-work Manage schema-3 bounded-authority lifecycle state.
95
101
  start-work-next Print the next active start-work continuation directive.
102
+ workflow-stage <family> <stage> [--n N --total M --unit U --verdict name=pass]
103
+ Record where a review, loop, iteration or crucible run stands, for
104
+ the LIT card's workflow map (.litclaude/workflow-stage.json).
96
105
  public-read Read a public http(s) source with FetchAttempt/FetchVerdict JSON.
97
106
  motion-runtime Pre-warm (install [--audio] [--word-timing]) or report (status) the
98
107
  lit-typographic-motion runtime cache. Run it outside a sandboxed session.
@@ -378,7 +387,7 @@ const familyBanner = ({ color = shouldColorInstall() } = {}) => [
378
387
  ].join("\n");
379
388
 
380
389
  const printCommandBanner = (parsed) => {
381
- if (["install", "update"].includes(parsed.command)) return;
390
+ if (["install", "update", "workflow-stage"].includes(parsed.command)) return;
382
391
  if (parsed.rest.some((arg) => arg === "--json" || arg.startsWith("--json="))) return;
383
392
  const color = shouldColorInstall();
384
393
  process.stdout.write(`${renderLitClaudeWordmark({ color }).join("\n")}\n`);
@@ -720,6 +729,82 @@ const registerMarketplace = (marketplacePath, home = claudeHome()) => {
720
729
  writeKnownMarketplaces(knownMarketplaces, home);
721
730
  };
722
731
 
732
+ // The LIT card's options live in settings.json `pluginConfigs`, which the terminal and the desktop
733
+ // app both read; the desktop app has no editor for them, so install writes each default the user
734
+ // has not set and records which ones it wrote. Uninstall removes only those still at that value.
735
+ const modOptionDefaults = () => Object.fromEntries(Object.keys(MOD_CHOICES).map((key) => [key, MOD_CHOICES[key][0]]));
736
+ const modOptionsOf = (settings) => ({ ...modOptionDefaults(), ...(settings.pluginConfigs?.[pluginKey]?.options ?? {}) });
737
+ const modOptionsLine = (settings) => {
738
+ const options = modOptionsOf(settings);
739
+ return Object.keys(MOD_CHOICES).map((key) => `${key} ${options[key]}`).join(" · ");
740
+ };
741
+
742
+ const writeModOptions = (settings, options) => {
743
+ settings.pluginConfigs = settings.pluginConfigs ?? {};
744
+ const entry = settings.pluginConfigs[pluginKey] ?? {};
745
+ settings.pluginConfigs[pluginKey] = { ...entry, options: { ...(entry.options ?? {}), ...options } };
746
+ };
747
+
748
+ const installModOptions = (home = claudeHome()) => {
749
+ const settings = readClaudeSettings(home);
750
+ const present = settings.pluginConfigs?.[pluginKey]?.options ?? {};
751
+ const existingLitClaude = settings[litClaudeSettingsKey] ?? {};
752
+ const written = { ...(existingLitClaude.modOptionsWritten ?? {}) };
753
+ const missing = Object.fromEntries(Object.entries(modOptionDefaults()).filter(([key]) => present[key] === undefined));
754
+ if (Object.keys(missing).length === 0) return;
755
+ writeModOptions(settings, missing);
756
+ settings[litClaudeSettingsKey] = { ...existingLitClaude, modOptionsWritten: { ...written, ...missing } };
757
+ writeClaudeSettings(settings, home);
758
+ };
759
+
760
+ const uninstallModOptions = (home = claudeHome()) => {
761
+ if (!existsSync(claudeSettingsPath(home))) return;
762
+ const settings = readClaudeSettings(home);
763
+ const litClaude = settings[litClaudeSettingsKey];
764
+ if (!litClaude?.modOptionsWritten) return;
765
+ const entry = settings.pluginConfigs?.[pluginKey];
766
+ if (entry?.options) {
767
+ const options = { ...entry.options };
768
+ for (const [key, value] of Object.entries(litClaude.modOptionsWritten)) if (options[key] === value) delete options[key];
769
+ const rest = { ...entry };
770
+ if (Object.keys(options).length > 0) rest.options = options;
771
+ else delete rest.options;
772
+ if (Object.keys(rest).length > 0) settings.pluginConfigs[pluginKey] = rest;
773
+ else delete settings.pluginConfigs[pluginKey];
774
+ if (Object.keys(settings.pluginConfigs).length === 0) delete settings.pluginConfigs;
775
+ }
776
+ const remainingLitClaude = { ...litClaude };
777
+ delete remainingLitClaude.modOptionsWritten;
778
+ if (Object.keys(remainingLitClaude).length > 0) settings[litClaudeSettingsKey] = remainingLitClaude;
779
+ else delete settings[litClaudeSettingsKey];
780
+ writeClaudeSettings(settings, home);
781
+ };
782
+
783
+ // `config` prints the options; `config <option> <value>` sets one. A value the user sets is theirs,
784
+ // so it leaves the install record and uninstall keeps it.
785
+ const configModOptions = (rest, home = claudeHome()) => {
786
+ if (rest.length === 0) {
787
+ const options = modOptionsOf(readClaudeSettings(home));
788
+ for (const key of Object.keys(MOD_CHOICES)) process.stdout.write(`${key.padEnd(7)} ${options[key].padEnd(7)} (${MOD_CHOICES[key].join(", ")})\n`);
789
+ process.stdout.write("Change one with: litclaude-ai config <option> <value>. New Claude Code sessions, terminal or desktop app, pick it up.\n");
790
+ return;
791
+ }
792
+ const [key, raw] = rest;
793
+ if (!Object.hasOwn(MOD_CHOICES, key)) fail(`Unknown option: ${key}. Options: ${Object.keys(MOD_CHOICES).join(", ")}`, 64);
794
+ const value = String(raw ?? "").trim().toLowerCase();
795
+ if (!MOD_CHOICES[key].includes(value) || rest.length > 2) fail(`${key} takes one of: ${MOD_CHOICES[key].join(", ")}`, 64);
796
+ const settings = readClaudeSettings(home);
797
+ writeModOptions(settings, { [key]: value });
798
+ const litClaude = settings[litClaudeSettingsKey];
799
+ if (litClaude?.modOptionsWritten && Object.hasOwn(litClaude.modOptionsWritten, key)) {
800
+ const written = { ...litClaude.modOptionsWritten };
801
+ delete written[key];
802
+ settings[litClaudeSettingsKey] = { ...litClaude, modOptionsWritten: written };
803
+ }
804
+ writeClaudeSettings(settings, home);
805
+ process.stdout.write(`MOD_OPTIONS: ${modOptionsLine(settings)}\n`);
806
+ };
807
+
723
808
  const ownsHudStatusLine = (settings, ownership) =>
724
809
  settings.statusLine?.type === "command" && settings.statusLine.command === ownership.statusLineCommand &&
725
810
  Object.keys(settings.statusLine).every((key) => key === "type" || key === "command");
@@ -1025,6 +1110,7 @@ const registerClaudePlugin = (installRoot, marketplacePath, home = claudeHome(),
1025
1110
  writeInstalledPlugins(registry, home);
1026
1111
  registerMarketplace(marketplacePath, home);
1027
1112
  installHudStatusLine(installRoot, home, hudAccent);
1113
+ installModOptions(home);
1028
1114
  recordPermissionConfiguration(permissionMode, home);
1029
1115
  };
1030
1116
 
@@ -1384,6 +1470,8 @@ const install = async ({ dryRun, rest }) => {
1384
1470
  process.stdout.write(`Plugin path: ${intendedPluginPath(home)}\n`);
1385
1471
  process.stdout.write(hudManaged ? `HUD: LitClaude statusLine installed (${hudAccent})\n` : "HUD: user statusLine setting preserved\n");
1386
1472
  process.stdout.write(`Global Claude settings permissions.allow/deny: ${permissionMode} — ${permissionProfile(permissionMode).description}\n`);
1473
+ process.stdout.write(`MOD_OPTIONS: ${modOptionsLine(readClaudeSettings(home))} (change: litclaude-ai config <option> <value>)\n`);
1474
+ process.stdout.write("DESKTOP_APP: ready — the Claude desktop app's Code tab reads these same settings; open a new session there\n");
1387
1475
  process.stdout.write(`${prewarmMotionRuntime()}\n`);
1388
1476
  if (wantsIconFont) await runIconFontInstall();
1389
1477
  process.stdout.write("Launch with: claude\n");
@@ -1505,6 +1593,7 @@ const doctor = ({ dryRun }) => {
1505
1593
  process.stdout.write(`${jevStatusLine(process.env)}\n`);
1506
1594
  writeAutoHandoffDoctorLines();
1507
1595
  process.stdout.write(`${iconFontDoctorLine()}\n`);
1596
+ process.stdout.write(`MOD_OPTIONS: ${modOptionsLine(readClaudeSettings())}\n`);
1508
1597
 
1509
1598
  // lit-typographic-motion: the five readiness probes, every run. Read-only and never fatal; a
1510
1599
  // missing pre-warm names the command that fixes it.
@@ -1635,6 +1724,7 @@ const uninstall = ({ dryRun }) => {
1635
1724
  uninstallHudStatusLine();
1636
1725
  uninstallOutputStyle();
1637
1726
  uninstallPermissionRules();
1727
+ uninstallModOptions();
1638
1728
  rmSync(claudePluginRoot(), { recursive: true, force: true });
1639
1729
  removeEmptyDirectory(dirname(claudePluginRoot()));
1640
1730
  removeEmptyDirectory(pluginCacheRoot);
@@ -1730,6 +1820,9 @@ const main = async () => {
1730
1820
  case "doctor":
1731
1821
  doctor(parsed);
1732
1822
  break;
1823
+ case "config":
1824
+ configModOptions(parsed.rest);
1825
+ break;
1733
1826
  case "path":
1734
1827
  printPath(parsed);
1735
1828
  break;
@@ -1748,6 +1841,9 @@ const main = async () => {
1748
1841
  case "workflow-check":
1749
1842
  exitAfterFlush(runWorkflowCheckCli(root, version, parsed.rest));
1750
1843
  break;
1844
+ case "workflow-stage":
1845
+ exitAfterFlush(runWorkflowStageCli(parsed.rest, process.cwd()));
1846
+ break;
1751
1847
  case "public-read":
1752
1848
  exitAfterFlush(await runPublicRead(parsed));
1753
1849
  break;
@@ -1 +1 @@
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>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.26"><title>release: 1.0.26</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.26</text></g></svg>
package/docs/hooks.md CHANGED
@@ -105,10 +105,10 @@ silent no-op for callers that still invoke it directly.
105
105
  | `SessionStart` | `plugins/litclaude/bin/litclaude-hook.js session-start` | Runs the bounded foreground automatic-update barrier on a fresh session (or starts the detached version-cache refresh when the cache is stale), discovers repo-local rule files, names the newest valid `plans/<slug>.md`, and on `source: compact` spends one of two durable re-injection reservations at reduced caps and, with automatic handoff on, reloads the fresh handoff once. |
106
106
  | `UserPromptSubmit` | `plugins/litclaude/bin/litclaude-hook.js user-prompt-submit` | Detects prompt routes and injects workflow context; independently delivers static rules not already recorded for this session; with `LITCLAUDE_JEV=1` and `TYPESAFE_API_KEY` set, adds at most one Jev skill hint line to an unrouted turn; answers the exact prompts `lit-handoff auto on <percent>`, `lit-handoff auto off` and `lit-handoff auto status` by changing or reporting the automatic handoff setting. |
107
107
  | `PreToolUse` | `plugins/litclaude/bin/litclaude-hook.js pre-tool-use` | Enforces semantic action/root grants before Write, Edit, MultiEdit, NotebookEdit, Bash, Agent, and bounded read tools execute; denies supported reader-facing text writes with a humanizer block-tier finding. |
108
- | `PostToolUse` | `plugins/litclaude/bin/litclaude-hook.js post-tool-use` | Names the post-edit checks the edit actually earned, injects any glob-scoped rule matching the edited paths, and checks successfully created DOCX/PPTX/PDF files for humanizer findings, including paths reported by Bash output. |
108
+ | `PostToolUse` | `plugins/litclaude/bin/litclaude-hook.js post-tool-use` | Moves the LIT card's stage record on from what the tool did (plan saved, task checked, lit-loop step, a reviewer's verdict from an `Agent` result). Names the post-edit checks the edit actually earned, injects any glob-scoped rule matching the edited paths, and checks successfully created DOCX/PPTX/PDF files for humanizer findings, including paths reported by Bash output. |
109
109
  | `Stop` | `plugins/litclaude/bin/litclaude-hook.js stop` | Holds a lit-plan turn with no persisted plan and a lit workflow turn that edited interface files without a clean interface-probe run (each at most twice per turn), emits bounded start-work continuation on new progress, otherwise applies the opt-in litgoal autoloop gate, and last of all, with automatic handoff on, blocks once per crossing of the chosen percent to have the handoff written. |
110
110
  | `SubagentStart` | `plugins/litclaude/bin/litclaude-hook.js subagent-start` | Registers child-lane and Claude-owned worktree identity. |
111
- | `SubagentStop` | `plugins/litclaude/bin/litclaude-hook.js subagent-stop` | Finalizes the lane without child continuation. |
111
+ | `SubagentStop` | `plugins/litclaude/bin/litclaude-hook.js subagent-stop` | Finalizes the lane without child continuation, and gives a background review-work reviewer's verdict to the LIT card's stage record. |
112
112
  | `SessionEnd` | `plugins/litclaude/bin/litclaude-hook.js session-end` | Records root-session end without blocking Claude. |
113
113
 
114
114
  ### Changed-text humanizer guard
@@ -347,40 +347,76 @@ itself. LitClaude's is `plugins/litclaude/hooks/lit-mod.mjs`, named under `modul
347
347
  setup. The status line stays as it is; the band adds a live layer above the prompt.
348
348
 
349
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
350
+ same five rows whether LitClaude is working or idle, and takes the status line's accent
351
351
  (`LITCLAUDE_HUD_ACCENT`, read from the installed status line command), so the card and the
352
352
  status line read as one family:
353
353
 
354
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 ──────────────────────╯
355
+ ╭─ [🔥 LIT] · start-work ───────────────────────────────── WORK ● ─╮
356
+ │ interview ✓ ━━ plan ✓ ━━ [work 3/7] ━━ review · ── done · 1 lane │
357
+ │ └ task 3/7 ███░░░░░ · next ▸ 4. README demo - Bash · 0:12 │
358
+ │ skills [⚡ start-work] [↯ lit-plan] │
359
+ ╰─ ctx 42% ▰▰▱▱▱ · 5h 31% · 1w 12% · git main ─────────────────────╯
359
360
  ```
360
361
 
361
362
  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
+ glyphs, one cell of the pill's colour with `basic`. The brand is a pink pill, the current stage
363
364
  and the skills of this request are pills on the accent with dark text, and skills from earlier
364
365
  requests are muted grey pills.
365
366
 
366
367
  - 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. 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.
370
- - The phase row lights `PLAN ━━ WORK ━━ REVIEW` from the start-work plan, the litgoal goal and
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
372
- passed criteria (`goal ✓ 5/5` once a goal is met), the number of live subagents (`● 2 lanes`), the autoloop
373
- count with the time left in its 30-minute budget, and `ctx 76% → 80%` when the context is
374
- within five points of the auto-handoff percent.
375
- - The chips row shows each LitFamily skill that fired as a pill: a Skill tool call naming a LitClaude
368
+ session is idle; `ready` before any) and, on the right, the current stage: `WORK ●`, `RED ●`,
369
+ `idle`, or `DONE ✓` during the finish. While a subagent is still running the card stays at
370
+ work (`LIVE ●` when no stage is current) and never reads `DONE`, even after the main turn ended.
371
+ - The second row is the workflow map: where the session stands in its LIT family
372
+ (`lib/mod/workflow.mjs`). The main chain is `interview › plan › work n/m › review › done`
373
+ (litgoal criteria and autoresearch iterations ride the work stage); litresearch is
374
+ `decompose › wave n › expand › verify › synthesis`; a lit-loop criterion cycles
375
+ `PIN › RED › GREEN › VERIFY › REVIEW › RECORD` (SURFACE shows on VERIFY, CLEAN on RECORD).
376
+ Done stages carry `✓` in a colour graded along the rail from flame orange through pink to the
377
+ accent, the current stage is a pill, the stages ahead are dim `name ·`. The rail into the
378
+ current stage is solid `━`; the rail leaving it fills with the stage's count and, while
379
+ LitClaude works, carries a bright head; the rails ahead are dim `─`. On its right the row keeps
380
+ the live subagents (`● 2 lanes`), the autoloop count and its time left, and `ctx 76% → 80%` when
381
+ the context nears the auto-handoff percent. A session no family places (a lit-docx request, a
382
+ fresh session) keeps the old `PLAN ━━ WORK ━━ REVIEW` track there.
383
+ - The stage comes from the newest of the ignited discipline, a LitClaude skill this request
384
+ fired, and the stage record; with none of those, from the state that says work is under way.
385
+ The detail comes from files LitClaude already writes: the deep-interview state
386
+ (`round 3/12 · ambiguity 42% → 20%`), the lit-plan draft's approval gate (`drafting`,
387
+ `awaiting approval`), the start-work plan's tasks and next task, the litgoal criteria and the
388
+ litresearch session folder (`wave-*.md`, `verify-*.md`, `SYNTHESIS.md`). What no file
389
+ records goes into `.litclaude/workflow-stage.json`, a small record LitClaude's hooks keep
390
+ moving without the model's help. The prompt and PreToolUse hooks start it when lit-plan,
391
+ review-work, lit-loop, autoresearch or lit-crucible starts, and clear it for any other skill. PostToolUse checks the plan stage when lit-plan saves
392
+ `plans/<slug>.md` (`plan ✓`, detail `plan saved`), counts the tasks each time the active
393
+ start-work plan is edited (`work 1/2`), and steps a lit-loop criterion to RED when a test
394
+ file is written, GREEN when the code changes after it, VERIFY when the tests run and RECORD
395
+ when `litgoal record-evidence` runs. For review-work, PreToolUse marks a lane running when a
396
+ prompt titled `Review lane: <lane>.` is spawned, and the lane's `verdict:` line, read by
397
+ PostToolUse for a foreground reviewer and by SubagentStop for a background one, marks it
398
+ passed or failed. The card names the lanes `goal`, `quality`, `security`, `qa` and `context`
399
+ (review-work's scope/diff, package/payload, security/provenance, tests/evidence and
400
+ real-surface/docs lanes); five passes finish the review. The hooks keep only the stage, the
401
+ lane and the counts, never a path, command or prompt. The model can still refine the record
402
+ with `litclaude-ai workflow-stage <family> <stage> [--n --total --unit --verdict
403
+ name=pass|fail|pending|running]` for what the hooks cannot see: a lane run without a child
404
+ agent, a loop step such as SURFACE, an autoresearch iteration, the crucible verdict. Another
405
+ session's record is ignored and a source older than six hours no longer counts.
406
+ - The third row is the detail line: `└` and the current stage's detail (a pink-to-cyan bar for a
407
+ count, the lane verdicts as `✓`, `✗`, `●` while a reviewer runs, or `·`, the next task), and on the right the live step,
408
+ the tool the main loop is running and for how long (`- Bash · 0:12`, `thinking` between tool
409
+ calls). The step shows the tool's name and its clock only, never its arguments, command,
410
+ prompt or paths.
411
+ - The fourth row shows each LitFamily skill that fired as a pill: a Skill tool call naming a LitClaude
376
412
  skill, a typed `/litclaude:<name>`, or bare `lit` routing seen through the ignition record.
377
413
  A skill fired in the current request is bright, with a short light passing over its name as
378
414
  it arrives; when the turn completes it dims into the `recent` list, and a new firing brightens
379
415
  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.
380
- With no chip yet, the row shows the next task while work runs, or how to start (`lit <task>`)
381
- while idle.
416
+ With no chip yet, the row shows the next task while work runs (when the detail line does not),
417
+ or how to start (`lit <task>`) while idle.
382
418
  - The bottom edge carries the live figures from the status line: context fill, the five-hour
383
- rate window and the Git branch (read from `HEAD`, across a linked worktree).
419
+ and weekly rate windows and the Git branch (read from `HEAD`, across a linked worktree).
384
420
  - A notice takes the chips row for a few seconds, with a toast, when a gate fires: lit-plan not
385
421
  saved, interface probe pending or failed, motion gate, context near the auto-handoff percent,
386
422
  auto-handoff fired. The completion moment takes it when the last task is checked, the work
@@ -397,9 +433,11 @@ chips of the current request stay bright until the main turn completes. An agent
397
433
  as ended stops counting at once, and one quiet for 30 minutes (no tool call, no status change)
398
434
  stops counting too, so a crashed or lost agent cannot hold the card at work.
399
435
 
400
- The card is the terminal's width up to 80 columns and every row is fitted to it, with Hangul and
436
+ The card is the terminal's width up to 120 columns and every row is fitted to it, with Hangul and
401
437
  emoji counted as two cells. Nothing wraps: on a narrow terminal the Git branch goes first, then
402
- the rate window, the lanes, the autoloop count and the long phase labels (`P ━ W ━ R`).
438
+ the weekly and five-hour windows, the lanes and the autoloop count. The map drops its rails, then
439
+ shortens the names (`int ✓ pla ✓ [wor 3/7] rev ·`), then shows only the current stage and its
440
+ neighbours; the review verdicts shorten to three letters and then to their marks.
403
441
 
404
442
  The spinner gains ` · <discipline> · n/m` after its ellipsis. `/lit-evidence` opens a pane with
405
443
  three tabs (keys `1`, `2`, `3`): the goal's criteria with their last evidence, blockers and the
@@ -408,19 +446,42 @@ newest litgoal ledger entries; the lanes; and the gates.
408
446
  Motion: entrances pop in 60 ms apart with a slight overshoot (160 ms each), fills and colour
409
447
  changes ease in and out (300 ms and 140 ms), notices slide in over 220 ms and leave over 150 ms,
410
448
  a chip's arrival light runs 400 ms (a plain fade under `subtle`, nothing under `off`), and the
411
- finish lasts at most 1.2 s. The band's `Client` element animates on Claude Code's frame
412
- clock only while something moves and stops its timer as soon as everything settles. In Claude
449
+ finish lasts at most 1.2 s. While LitClaude is at work the card keeps moving so you can tell it
450
+ is active at a glance: the LIT pill breathes from pink to flame orange every 2.4 s, a light sweeps
451
+ across the current stage and this request's skill chips every 1.8 s, a bright head runs the rail
452
+ leaving the current stage every 1.4 s, the count bar's head shimmers, and a comet runs along the
453
+ top rule and back along the bottom one every 2.6 s. Under `subtle` only the breathing stays; under `off` nothing moves. An
454
+ idle card holds still. The band's `Client` element animates on Claude Code's frame clock, every
455
+ 33 ms while a transition plays and every 66 ms during work, and stops its timer once the card is
456
+ idle and settled. In Claude
413
457
  Code 2.1.292 a terminal row whose only change is a non-ASCII glyph is not repainted, so every
414
458
  moving part changes ASCII text or colour: bar cells are coloured spaces, embers are ASCII sparks,
415
- a chip's light moves over its ASCII name, and a glyph swaps only together with its words.
416
-
417
- Options, in `/config` under LitClaude (stored in `pluginConfigs`):
459
+ a chip's light moves over its ASCII name, the map's `━` and `─` cells move by colour alone, the
460
+ step's spinner (`- \ | /`) and clock are ASCII, and a glyph swaps only together with its words.
461
+
462
+ The desktop app's Code tab, and the other surfaces that are not a terminal, draw a mod's `Text` in
463
+ a proportional font and cut each span to the cells it was laid out for, so the card above comes
464
+ apart there. On those surfaces the mod draws the same five rows as one `Svg` element instead
465
+ (`lib/mod/svg-card.mjs`): monospace text placed by counted cells, real pills and a rounded border.
466
+ It shows the settled state, with no entrances or crossfades. While LitClaude works it moves with
467
+ SMIL animation inside the drawing, which the surface runs in a script-less sandboxed frame: the
468
+ pill and the border breathe and the step's spinner turns, and on `full` a light crosses the
469
+ current stage and the current chips, a head runs the rail leaving it, the count bar's head
470
+ shimmers and a comet runs around the border. An idle card is a still image. The step's clock
471
+ moves with each new drawing, about every two seconds.
472
+
473
+ Options, in `/config` under LitClaude (stored in `pluginConfigs` in `~/.claude/settings.json`,
474
+ which the terminal and the desktop app's Code tab both read). The installer writes each default
475
+ you have not set, so the desktop app, which has no editor for them, starts with all four;
476
+ `litclaude-ai config` prints them and `litclaude-ai config <option> <value>` changes one.
477
+ Uninstall removes only the defaults it wrote that are still unchanged.
418
478
 
419
479
  | Option | Values | Default |
420
480
  | --- | --- | --- |
421
- | `motion` | `full`, `subtle` (no overshoot, shimmer or embers, shorter), `off` (final frames only) | `full` |
481
+ | `motion` | `full`, `subtle` (no overshoot, shimmer, embers, sweep or comet, shorter; the LIT pill still breathes during work), `off` (final frames only) | `full` |
422
482
  | `glyphs` | `auto` (Nerd glyphs when the icon font is installed), `nerd`, or `basic` | `auto` |
423
483
  | `band` | `always` (the whole session, idle included), `auto` (only while work is active or a skill of this request is lit), `off` | `always` |
484
+ | `notify` | `off`, `finish` (a notification through your own notification channel, `$.ui.notify`, when LIT work completes or a LIT turn longer than three minutes ends), `finish+sound` (the same; a sound is added once LitClaude ships one, and none ships yet) | `off` |
424
485
 
425
486
  Unknown values fall back to the default. The fields are plain strings with no `options` list on
426
487
  purpose: Claude Code 2.1.270 and older refuse to load a plugin whose `userConfig` has one, and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litclaude",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
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.24",
4
+ "version": "1.0.26",
5
5
  "author": {
6
6
  "name": "LitClaude contributors"
7
7
  },
@@ -18,7 +18,7 @@
18
18
  "motion": {
19
19
  "type": "string",
20
20
  "title": "LIT band motion",
21
- "description": "How the band above the prompt moves: full, subtle (no overshoot, shimmer or embers) or off.",
21
+ "description": "How the band above the prompt moves: full (during work the LIT pill breathes, a light sweeps the phase and a comet runs the border), subtle (only the breathing) or off.",
22
22
  "default": "full"
23
23
  },
24
24
  "glyphs": {
@@ -32,6 +32,12 @@
32
32
  "title": "LIT band",
33
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
34
  "default": "always"
35
+ },
36
+ "notify": {
37
+ "type": "string",
38
+ "title": "LIT finish notification",
39
+ "description": "off, or finish: a notification through your own notification channel when LIT work completes or a LIT turn longer than three minutes ends. finish+sound also plays a sound once LitClaude ships one; until then it notifies like finish.",
40
+ "default": "off"
35
41
  }
36
42
  }
37
43
  }
@@ -6,6 +6,8 @@ import { fileURLToPath } from "node:url";
6
6
  import { standard, micro, colorMode, supportsBlocks } from "../lib/lit-mark.mjs";
7
7
  import { NEON_END, NEON_START, rainbowText } from "../lib/hud-accent-palette.mjs";
8
8
  import { ignitionMark, writeIgnitionState } from "../lib/hud-ignition.mjs";
9
+ import { DISCIPLINE_ENTRY } from "../lib/mod/workflow.mjs";
10
+ import { observeWorkflowPostTool, observeWorkflowPreTool, observeWorkflowSubagentStop, startWorkflowStage } from "../lib/workflow-stage.mjs";
9
11
  import { renameAliases, renameNote } from "../lib/rename-aliases.mjs";
10
12
  import { hasInterfaceModeWord, interfaceMode } from "../lib/interface-mode.mjs";
11
13
  import { transcriptHasContextPressure } from "../lib/context-pressure.mjs";
@@ -1322,6 +1324,13 @@ switch (eventName) {
1322
1324
  else clearLitPlanTurn(stateRoot);
1323
1325
  recordInterfaceProbeTurn(stateRoot, { sessionId: input.session_id, discipline: trigger?.safetyBlock ? null : trigger?.discipline });
1324
1326
  recordMotionTurn(stateRoot, { sessionId: input.session_id, discipline: trigger?.discipline });
1327
+ // The LIT card's stage record follows the discipline this prompt ignited: it starts over for a
1328
+ // skill only it can follow and is cleared for any other.
1329
+ // A typed `/litclaude:<skill>` is expanded by the host and never routes here, so its skill
1330
+ // is read off the prompt's first word.
1331
+ const typed = /^\/(?:litclaude:)?([a-z][a-z-]{0,40})(?=\s|$)/u.exec(prompt.trim())?.[1];
1332
+ const started = trigger?.discipline && !trigger.safetyBlock ? trigger.discipline : typed && Object.hasOwn(DISCIPLINE_ENTRY, typed) ? typed : null;
1333
+ if (started) startWorkflowStage(cwd, { sessionId: input.session_id, skill: started });
1325
1334
  } catch {
1326
1335
  // Persistence-gate state is advisory to routing; an unwritable record never alters the prompt hook.
1327
1336
  }
@@ -1407,6 +1416,13 @@ switch (eventName) {
1407
1416
  },
1408
1417
  }));
1409
1418
  } else {
1419
+ // The LIT card's stage record: a workflow skill starts it, a review lane's reviewer marks
1420
+ // its lane running.
1421
+ try {
1422
+ observeWorkflowPreTool(cwd, guardInput);
1423
+ } catch {
1424
+ // The stage record only feeds the card; a failed write never changes the tool decision.
1425
+ }
1410
1426
  let humanizerContext = "";
1411
1427
  try {
1412
1428
  const result = evaluateDeliverableHedgeGuard({ input: guardInput, pluginRoot });
@@ -1455,6 +1471,16 @@ switch (eventName) {
1455
1471
  case "post-tool-use": {
1456
1472
  const toolName = typeof input.tool_name === "string" ? input.tool_name : "unknown";
1457
1473
  const filePaths = extractMutatedFilePaths(input);
1474
+ // The LIT card's stage record moves on from what this tool did: the plan saved, a task checked
1475
+ // in the active plan, a lit-loop step, a reviewer's verdict.
1476
+ try {
1477
+ const cwd = typeof input.cwd === "string" ? input.cwd : process.cwd();
1478
+ observeWorkflowPostTool(cwd, input, { filePaths, lifecycleRoot: lifecycleRootFor(cwd) });
1479
+ } catch {
1480
+ // The stage record only feeds the card; a failed write never changes the tool's result.
1481
+ }
1482
+ // A reviewer's report only moves the card; the post-edit checks below are for writes.
1483
+ if (toolName === "Agent") break;
1458
1484
  try {
1459
1485
  const cwd = typeof input.cwd === "string" ? input.cwd : process.cwd();
1460
1486
  observeInterfaceProbeTool(resolveProjectStateRoot(cwd), { input, filePaths });
@@ -1545,6 +1571,11 @@ switch (eventName) {
1545
1571
  } catch {
1546
1572
  // SubagentStop remains continuation-inert; completion will fail closed on an active lane.
1547
1573
  }
1574
+ try {
1575
+ observeWorkflowSubagentStop(typeof input.cwd === "string" ? input.cwd : process.cwd(), input);
1576
+ } catch {
1577
+ // A background reviewer's verdict only feeds the card.
1578
+ }
1548
1579
  break;
1549
1580
  }
1550
1581
  case "session-end": {
@@ -9,7 +9,7 @@ import {
9
9
  } from "../lib/wikify-knowledge.mjs";
10
10
 
11
11
  const protocolVersion = "2024-11-05";
12
- const serverVersion = "1.0.24";
12
+ const serverVersion = "1.0.26";
13
13
 
14
14
  const publicSourceReadTool = {
15
15
  name: "public_source_read",
@@ -40,7 +40,7 @@
40
40
  ],
41
41
  "PostToolUse": [
42
42
  {
43
- "matcher": "^(Write|Edit|MultiEdit|NotebookEdit|Bash)$",
43
+ "matcher": "^(Write|Edit|MultiEdit|NotebookEdit|Bash|Agent)$",
44
44
  "hooks": [
45
45
  {
46
46
  "type": "command",