@litfamily/litmuse 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +20 -0
  3. package/README_ko-KR.md +20 -0
  4. package/bin/litmuse.mjs +4 -0
  5. package/lib/stage-card.mjs +180 -5
  6. package/package.json +1 -1
  7. package/plugin/.muse-plugin/plugin.json +26 -11
  8. package/plugin/commands/lit-status.md +2 -1
  9. package/plugin/commands/lit.md +1 -1
  10. package/plugin/hooks/lib/digest.mjs +1 -1
  11. package/plugin/hooks/lib/next-step.mjs +68 -0
  12. package/plugin/hooks/lib/routes.mjs +59 -0
  13. package/plugin/hooks/lib/workflow-stack.mjs +318 -0
  14. package/plugin/hooks/session-start.mjs +29 -2
  15. package/plugin/hooks/user-prompt-submit.mjs +138 -7
  16. package/plugin/lib/capped-fetch.mjs +25 -0
  17. package/plugin/lib/pinned-fetch.mjs +79 -0
  18. package/plugin/lib/plan-task-rows.mjs +32 -0
  19. package/plugin/lib/render_pages.py +29 -1
  20. package/plugin/lib/rewind/collect.mjs +679 -0
  21. package/plugin/lib/rewind/gauge.mjs +72 -0
  22. package/plugin/lib/rewind/next-step.mjs +91 -0
  23. package/plugin/lib/rewind/redact.mjs +83 -0
  24. package/plugin/lib/rewind/transcripts.mjs +227 -0
  25. package/plugin/skills/frontend-ui-ux/SKILL.md +24 -9
  26. package/plugin/skills/frontend-ui-ux/kit/components.css +414 -0
  27. package/plugin/skills/frontend-ui-ux/kit/fonts.json +22 -0
  28. package/plugin/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
  29. package/plugin/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
  30. package/plugin/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
  31. package/plugin/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
  32. package/plugin/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
  33. package/plugin/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
  34. package/plugin/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
  35. package/plugin/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
  36. package/plugin/skills/frontend-ui-ux/kit/ui.js +226 -0
  37. package/plugin/skills/frontend-ui-ux/references/complete-contract.md +2 -1
  38. package/plugin/skills/frontend-ui-ux/references/craft-floor.md +9 -4
  39. package/plugin/skills/frontend-ui-ux/references/default-editorial-pixel.json +59 -7
  40. package/plugin/skills/frontend-ui-ux/references/house-styles.md +234 -0
  41. package/plugin/skills/frontend-ui-ux/references/motion-guide.md +3 -3
  42. package/plugin/skills/frontend-ui-ux/references/slop-register.md +7 -7
  43. package/plugin/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
  44. package/plugin/skills/frontend-ui-ux/scripts/interface-probe-lock.mjs +196 -0
  45. package/plugin/skills/frontend-ui-ux/scripts/interface-probe-page.mjs +31 -10
  46. package/plugin/skills/frontend-ui-ux/scripts/interface-probe-rules.mjs +19 -7
  47. package/plugin/skills/frontend-ui-ux/scripts/interface-probe-static.mjs +4 -0
  48. package/plugin/skills/frontend-ui-ux/scripts/interface-probe.mjs +59 -7
  49. package/plugin/skills/frontend-ui-ux/scripts/pixel-art.mjs +60 -0
  50. package/plugin/skills/lit-adhd/SKILL.md +188 -0
  51. package/plugin/skills/lit-adhd/references/card-examples.md +121 -0
  52. package/plugin/skills/lit-adhd/references/status-taxonomy.md +43 -0
  53. package/plugin/skills/lit-adhd/scripts/rewind.mjs +11 -0
  54. package/plugin/skills/lit-diagram-drawer/SKILL.md +3 -3
  55. package/plugin/skills/lit-diagram-drawer/scripts/diagram-craft-rules.mjs +1 -2
  56. package/plugin/skills/lit-diagram-drawer/scripts/visual-quality.mjs +0 -1
  57. package/plugin/skills/lit-docx/SKILL.md +1 -1
  58. package/plugin/skills/lit-hijack/SKILL.md +589 -0
  59. package/plugin/skills/lit-hijack/references/capture-and-provenance.md +123 -0
  60. package/plugin/skills/lit-hijack/references/clean-room-build.md +133 -0
  61. package/plugin/skills/lit-hijack/references/distil-and-licence.md +128 -0
  62. package/plugin/skills/lit-hijack/references/gap-and-port-design.md +152 -0
  63. package/plugin/skills/lit-hijack/references/parity-and-report.md +133 -0
  64. package/plugin/skills/lit-hijack/scripts/hijack.mjs +879 -0
  65. package/plugin/skills/lit-pptx/SKILL.md +20 -9
  66. package/plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
  67. package/plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
  68. package/plugin/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
  69. package/plugin/skills/lit-pptx/fonts/provenance.json +32 -1
  70. package/plugin/skills/lit-pptx/references/direction-step.md +19 -2
  71. package/plugin/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +173 -0
  72. package/plugin/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +154 -0
  73. package/plugin/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +139 -0
  74. package/plugin/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +158 -0
  75. package/plugin/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +179 -0
  76. package/plugin/skills/lit-pptx/references/tonalities/bauhaus.md +112 -0
  77. package/plugin/skills/lit-pptx/references/tonalities/computational-brutalism.md +118 -0
  78. package/plugin/skills/lit-pptx/references/tonalities/neo-brutalism.md +125 -0
  79. package/plugin/skills/lit-pptx/references/tonalities/newsroom.md +115 -0
  80. package/plugin/skills/lit-pptx/references/tonalities/riso.md +109 -0
  81. package/plugin/skills/lit-pptx/scripts/compile-deck.js +1 -1
  82. package/plugin/skills/lit-pptx/scripts/craft_extras.py +136 -92
  83. package/plugin/skills/lit-pptx/scripts/embed_fonts.py +13 -1
  84. package/plugin/skills/lit-pptx/scripts/lib/grid-resolver.js +105 -116
  85. package/plugin/skills/lit-pptx/scripts/lib/layout-resolver.js +9 -15
  86. package/plugin/skills/lit-pptx/scripts/lib/render-pack.js +368 -561
  87. package/plugin/skills/lit-pptx/scripts/lib/slide-grid.js +104 -0
  88. package/plugin/skills/lit-pptx/scripts/lib/template-registry.js +57 -6
  89. package/plugin/skills/lit-pptx/scripts/qa_deck.py +4 -4
  90. package/plugin/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +1 -1
  91. package/plugin/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +98 -0
  92. package/plugin/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +1 -1
  93. package/plugin/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
  94. package/plugin/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +1 -1
  95. package/plugin/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +1 -1
  96. package/plugin/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
  97. package/plugin/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
  98. package/plugin/skills/lit-pptx/templates/tonalities/night/pack.yaml +1 -1
  99. package/plugin/skills/lit-pptx/templates/tonalities/paper/pack.yaml +1 -1
  100. package/plugin/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
  101. package/plugin/skills/lit-pptx/templates/tonalities/signal/pack.yaml +2 -2
  102. package/plugin/skills/lit-pptx/templates/tonalities/studio/pack.yaml +1 -1
  103. package/plugin/skills/lit-recap/SKILL.md +3 -0
  104. package/plugin/skills/lit-schematic/SKILL.md +241 -0
  105. package/plugin/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +14 -0
  106. package/plugin/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +8 -0
  107. package/plugin/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +22 -0
  108. package/plugin/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +73 -0
  109. package/plugin/skills/lit-schematic/references/cutaway-outputs.md +25 -0
  110. package/plugin/skills/lit-schematic/references/isometric-craft.md +24 -0
  111. package/plugin/skills/lit-schematic/references/journal-figures.md +19 -0
  112. package/plugin/skills/lit-schematic/references/kit-api.md +27 -0
  113. package/plugin/skills/lit-schematic/references/pfd-and-layout.md +20 -0
  114. package/plugin/skills/lit-schematic/references/pid-drafting.md +26 -0
  115. package/plugin/skills/lit-schematic/references/pid-symbols.md +27 -0
  116. package/plugin/skills/lit-schematic/references/verification.md +24 -0
  117. package/plugin/skills/lit-schematic/scripts/doctor.mjs +13 -0
  118. package/plugin/skills/lit-schematic/scripts/lib/figure.mjs +49 -0
  119. package/plugin/skills/lit-schematic/scripts/lib/isa.mjs +18 -0
  120. package/plugin/skills/lit-schematic/scripts/lib/iso.mjs +58 -0
  121. package/plugin/skills/lit-schematic/scripts/lib/legend-sheet.mjs +10 -0
  122. package/plugin/skills/lit-schematic/scripts/lib/qa-core.mjs +241 -0
  123. package/plugin/skills/lit-schematic/scripts/lib/sheet.mjs +446 -0
  124. package/plugin/skills/lit-schematic/scripts/lib/svg.mjs +39 -0
  125. package/plugin/skills/lit-schematic/scripts/lib/theme.mjs +25 -0
  126. package/plugin/skills/lit-schematic/scripts/motion.mjs +8 -0
  127. package/plugin/skills/lit-schematic/scripts/qa.mjs +21 -0
  128. package/plugin/skills/lit-schematic/scripts/render.mjs +38 -0
  129. package/plugin/skills/lit-schematic/scripts/scaffold.mjs +73 -0
  130. package/plugin/skills/lit-scientific-visualization/SKILL.md +2 -0
  131. package/plugin/skills/lit-typographic-motion/SKILL.md +12 -8
  132. package/plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +11 -0
  133. package/plugin/skills/lit-typographic-motion/engine/blender/runner.py +218 -0
  134. package/plugin/skills/lit-typographic-motion/engine/core/presets.mjs +1 -1
  135. package/plugin/skills/lit-typographic-motion/engine/node/assets.mjs +218 -0
  136. package/plugin/skills/lit-typographic-motion/engine/node/blender.mjs +144 -0
  137. package/plugin/skills/lit-typographic-motion/engine/node/board.mjs +136 -0
  138. package/plugin/skills/lit-typographic-motion/engine/node/done.mjs +42 -3
  139. package/plugin/skills/lit-typographic-motion/engine/node/gallery.mjs +120 -0
  140. package/plugin/skills/lit-typographic-motion/engine/node/house.mjs +44 -0
  141. package/plugin/skills/lit-typographic-motion/engine/node/pipeline.mjs +18 -7
  142. package/plugin/skills/lit-typographic-motion/engine/node/runtime.mjs +6 -3
  143. package/plugin/skills/lit-typographic-motion/engine/node/scaffold.mjs +210 -0
  144. package/plugin/skills/lit-typographic-motion/engine/node/score.mjs +639 -0
  145. package/plugin/skills/lit-typographic-motion/engine/node/sound-cli.mjs +21 -4
  146. package/plugin/skills/lit-typographic-motion/engine/node/sound.mjs +126 -27
  147. package/plugin/skills/lit-typographic-motion/engine/node/stage-serve.mjs +29 -5
  148. package/plugin/skills/lit-typographic-motion/engine/node/stage.mjs +82 -14
  149. package/plugin/skills/lit-typographic-motion/engine/node/styles.mjs +214 -0
  150. package/plugin/skills/lit-typographic-motion/engine/node/treatment.mjs +311 -10
  151. package/plugin/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
  152. package/plugin/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
  153. package/plugin/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
  154. package/plugin/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
  155. package/plugin/skills/lit-typographic-motion/recipes/hero-3d/expected.json +69 -0
  156. package/plugin/skills/lit-typographic-motion/recipes/hero-3d/index.html +139 -0
  157. package/plugin/skills/lit-typographic-motion/references/depth.md +122 -0
  158. package/plugin/skills/lit-typographic-motion/references/sound.md +74 -16
  159. package/plugin/skills/lit-typographic-motion/references/stage.md +29 -5
  160. package/plugin/skills/lit-typographic-motion/references/studio.md +245 -0
  161. package/plugin/skills/lit-typographic-motion/references/treatment.md +31 -8
  162. package/plugin/skills/lit-typographic-motion/scripts/motion.mjs +48 -7
  163. package/plugin/skills/lit-typographic-motion/styles/paper.json +41 -0
  164. package/plugin/skills/lit-typographic-motion/styles/swiss-signal.json +41 -0
  165. package/plugin/skills/lit-typographic-motion/styles/terminalcore.json +42 -0
  166. package/plugin/skills/lit-typographic-motion/styles/tidal.json +41 -0
  167. package/plugin/skills/readme-studio/SKILL.md +1 -1
  168. package/plugin/skills/start-work/SKILL.md +6 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.2 — 2026-10-27
4
+
5
+ - Three new skills, so LitMuse now has 42. `lit-schematic` draws P&IDs with ISA-5.1 tags and control loops, apparatus figures for papers and isometric cutaways, as editable SVG with a QA gate. `lit-hijack` studies a tool you point it at and rebuilds what you want from it clean-room, with a gate after each phase. `lit-adhd` (also `lit-rewind`) is a briefing across your recent Muse sessions: what each one asked, did and is waiting on, with a single next step that a bare `go` picks up.
6
+ - Workflow stacks: a lit route started inside another one pauses the first, and `lit continue` picks it back up. start-work waits for a typed `lit start plans/<name>.md` before it acts on a plan, and a bare `go` follows the next step the brief suggested.
7
+ - The LIT card (`/lit-status`, `litmuse status --card`) shows a row per open thread, an amber `YOUR TURN` headline when Muse waits on you, and a rainbow headline for every way of opening `lit-adhd` (`/lit-adhd`, `/litmuse:lit-adhd`, `lit-rewind`).
8
+ - Slides: five new tonalities (`bauhaus`, `computational-brutalism`, `neo-brutalism`, `newsroom`, `riso`), hero and decoration on every pack, grid zones, and JetBrains Mono as a face for titles and labels beside Pretendard. The slide, document, interface, diagram, schematic, README and film skills ask which theme or style you want before building, unless you already named one or said to choose.
9
+ - Films: house styles, a storyboard board you see before the full render, a 3D stage and an original layered score made for each film.
10
+ - The README has a gallery of real renders from these skills.
11
+ - Security fixes from review: the session brief masks secrets more reliably, including those in URLs and environment values; streamed downloads have size caps; three.js is pinned; and `lit-hijack` and the film asset downloads connect only to the address they checked and re-check every redirect, which closes a DNS-rebinding gap.
12
+ - The hook code changed, so Muse asks you to approve the LitMuse hooks again after the update; `npx @litfamily/litmuse install` lists them and asks once.
13
+
3
14
  ## 0.1.1 — 2026-10-26
4
15
 
5
16
  - The README studio's HyperFrames and Remotion cover templates pin newer `hono` and `fast-uri` in their lockfiles, picking up upstream fixes. A fresh template setup installs these versions.
package/README.md CHANGED
@@ -133,6 +133,26 @@ The snapshot names the trigger, the session, the route, the plan with its open r
133
133
 
134
134
  <p align="center"><img src="./docs/assets/handoff/latest.webp" width="960" alt="The handoff snapshot: trigger, session, the lit-plan route, the plan with 0 ticked and 2 open rows, and recent ledger lines." /></p>
135
135
 
136
+ ## What you can make
137
+
138
+ Four real outputs rendered on a Mac during the quality parity round, each opened, read, and fixed until it looked right:
139
+
140
+ <p align="center"><img src="./docs/assets/parity/lit-adhd-brief.png" width="960" alt="The lit-adhd brief in a real terminal: rainbow headline, one active session card with goal and work time, tools, the finished commit, and a boxed recommended next step." /></p>
141
+
142
+ `lit-adhd` briefs your live sessions: session cards with status badges, an ended table, and a boxed recommended step — colour on TTY, no internal field names leaking.
143
+
144
+ <p align="center"><img src="./docs/assets/parity/lit-pptx-deck.png" width="960" alt="A lit-pptx status deck: KPI row and a column chart of suite growth per wave." /></p>
145
+
146
+ `lit-pptx` compiles real decks from data: this one charts the port's own test-suite growth, wave by wave.
147
+
148
+ <p align="center"><img src="./docs/assets/parity/lit-schematic-pid.png" width="960" alt="A lit-schematic P&ID: a sodium hypochlorite dosing skid with tank TK-101, duty and standby dosing pumps, a relief-valve spillback, instrument bubbles, line numbers, a drawn-symbol legend, and title block EX-PID-001." /></p>
149
+
150
+ `lit-schematic` draws QA-checked P&IDs: a dosing skid with real symbols, a drawn legend, line numbers, and a title block — seven look rounds until every label read cleanly.
151
+
152
+ <p align="center"><img src="./docs/assets/parity/lit-frontend-page.png" width="960" alt="A frontend-ui-ux gallery page: dark ledger, phosphor accents, one plate per render, checked at phone, tablet, and desktop widths." /></p>
153
+
154
+ `frontend-ui-ux` builds the page around them: one gallery, one plate per render, checked at phone, tablet, and desktop widths.
155
+
136
156
  ## What you get
137
157
 
138
158
  **39 skills**, each a dense Muse skill loaded on demand, with the references, scripts, templates and engines it needs inside its own folder. The catalog only holds their short descriptions, about 3.7 KB in total, so they keep their place even next to a large personal skill collection.
package/README_ko-KR.md CHANGED
@@ -133,6 +133,26 @@ Muse의 `/hud`는 켜고 끄는 고정 항목 목록이라서 플러그인 글
133
133
 
134
134
  <p align="center"><img src="./docs/assets/handoff/latest.webp" width="960" alt="인계 스냅샷. 실행 계기와 세션, lit-plan 경로, 0개 체크와 2개 열린 행의 계획, 최근 기록 줄을 보여 줍니다." /></p>
135
135
 
136
+ ## 만들 수 있는 것
137
+
138
+ 품질 패리티 라운드에서 Mac에서 직접 렌더한 결과물 4종입니다. 파일을 열어 눈으로 읽고, 어색한 곳은 고쳐 다시 렌더했습니다.
139
+
140
+ <p align="center"><img src="./docs/assets/parity/lit-adhd-brief.png" width="960" alt="실제 터미널의 lit-adhd 브리프. 무지개 헤드라인, 목표와 작업 시간이 적힌 활성 세션 카드 하나, 도구, 완료된 커밋, 박스형 권장 다음 단계." /></p>
141
+
142
+ `lit-adhd`는 실행 중 세션을 브리핑합니다. 상태 뱃지가 달린 세션 카드와 종료 표, 박스형 권장 다음 단계 — 터미널 색상 그대로, 내부 필드 이름은 새지 않습니다.
143
+
144
+ <p align="center"><img src="./docs/assets/parity/lit-pptx-deck.png" width="960" alt="lit-pptx 상태 덱. KPI 행과 웨이브별 스위트 증가 막대 차트." /></p>
145
+
146
+ `lit-pptx`는 자료로 실제 덱을 엮습니다. 이 덱은 포팅 웨이브별 테스트 스위트 증가를 그립니다.
147
+
148
+ <p align="center"><img src="./docs/assets/parity/lit-schematic-pid.png" width="960" alt="lit-schematic P&ID. 탱크 TK-101, 상용·예비 도징 펌프, 릴리프 밸브 스필백, 계장 버블, 라인 번호, 심벌 범례, EX-PID-001 타이틀 블록을 갖춘 차아염소산나트륨 도징 스키드." /></p>
149
+
150
+ `lit-schematic`은 QA를 통과한 P&ID를 그립니다. 실제 심벌과 범례, 라인 번호, 타이틀 블록을 갖춘 도징 스키드이며, 모든 라벨이 읽힐 때까지 일곱 번 고쳐 그렸습니다.
151
+
152
+ <p align="center"><img src="./docs/assets/parity/lit-frontend-page.png" width="960" alt="frontend-ui-ux 갤러리 페이지. 어두운 원장과 형광 악센트, 렌더마다 플레이트 하나, 휴대전화·태블릿·데스크톱 너비에서 확인." /></p>
153
+
154
+ `frontend-ui-ux`는 이를 감싸는 페이지를 만듭니다. 렌더 한 종마다 플레이트 하나인 갤러리이며, 휴대전화·태블릿·데스크톱 너비에서 확인했습니다.
155
+
136
156
  ## 들어 있는 것
137
157
 
138
158
  **스킬 39개.** 필요할 때 불러오는 밀도 높은 Muse 스킬이며, 각 스킬에 필요한 참고 문서, 스크립트, 템플릿, 엔진이 그 스킬 폴더 안에 함께 들어 있습니다. 카탈로그에는 짧은 설명만 올라가며, 모두 합쳐 약 3.7 KB라서 개인 스킬이 많은 환경에서도 자리를 지킵니다.
package/bin/litmuse.mjs CHANGED
@@ -13,6 +13,7 @@ import { checkUpdate } from '../lib/update-check.mjs';
13
13
  import { runtime } from '../lib/runtime.mjs';
14
14
  import { buildCard, renderCard } from '../lib/stage-card.mjs';
15
15
  import { styleCommand } from '../lib/style.mjs';
16
+ import { runRewindCli } from '../plugin/lib/rewind/collect.mjs';
16
17
 
17
18
  const HELP = `LitMuse ${packageVersion()}: LitFamily workflows for Meta's Muse Code
18
19
 
@@ -26,6 +27,7 @@ Commands
26
27
  hud Show the recommended /hud and /theme preset
27
28
  style eli5 | asd-ste100 | off | status: the answer style LitMuse adds to every session
28
29
  runtime status [name] | install <name|all>: the pinned engine runtimes, cached under the LitMuse data dir
30
+ rewind Cross-session brief records [--json] [--here] [--since 7d] [--max 12] [--session <id>]
29
31
  uninstall Remove the plugin from Muse and the staged bundle
30
32
  help This text
31
33
 
@@ -97,6 +99,8 @@ async function main() {
97
99
  return models({ env, flags, print });
98
100
  case 'runtime':
99
101
  return runtime({ env, flags, args, print });
102
+ case 'rewind':
103
+ return runRewindCli(flags.json ? [...args, '--json'] : args, process.cwd(), { env });
100
104
  case 'hud':
101
105
  print(readFileSync(join(PACKAGE_ROOT, 'docs', 'hud-preset.md'), 'utf8'));
102
106
  return 0;
@@ -1,6 +1,8 @@
1
1
  import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { latestFile, readStage, stageFile, workspaceHash } from '../plugin/hooks/lib/stage.mjs';
4
+ import { gauge } from '../plugin/lib/rewind/gauge.mjs';
5
+ import { REVIEW_LANES } from '../plugin/hooks/lib/workflow-stack.mjs';
4
6
 
5
7
  export function fmtDuration(ms) {
6
8
  const s = Math.max(0, Math.floor(ms / 1000));
@@ -28,9 +30,16 @@ export function planProgress(cwd, planFile) {
28
30
  const rows = [...readFileSync(join(cwd, file), 'utf8').matchAll(/^- \[([ xX])\] (\d+)\.\s*(.*)$/gm)];
29
31
  if (!rows.length) return null;
30
32
  const open = rows.find((r) => r[1] === ' ');
33
+ let mtimeMs = null;
34
+ try {
35
+ mtimeMs = statSync(join(cwd, file)).mtimeMs;
36
+ } catch {
37
+ // Readable just now but stat fails: freshness stays unknown.
38
+ }
31
39
  return {
32
40
  file, total: rows.length, done: rows.filter((r) => r[1] !== ' ').length,
33
41
  next: open ? { row: Number(open[2]), text: open[3].slice(0, 120) } : null,
42
+ mtimeMs,
34
43
  };
35
44
  } catch {
36
45
  return null;
@@ -54,6 +63,7 @@ export function activeGoal(cwd) {
54
63
  slug: best.g.slug ?? null, objective: String(best.g.objective ?? '').slice(0, 200),
55
64
  met: criteria.filter((c) => c.status === 'pass').length, total: criteria.length,
56
65
  open: criteria.filter((c) => c.status !== 'pass').map((c) => ({ id: c.id, text: String(c.text ?? '').slice(0, 120) })),
66
+ mtimeMs: best.m,
57
67
  };
58
68
  } catch {
59
69
  return null;
@@ -67,10 +77,109 @@ export function loadStage({ cwd, session, env = process.env }) {
67
77
  return entry && entry.session ? readStage(stageFile(cwd, entry.session, env)) : null;
68
78
  }
69
79
 
80
+ // One rainbow-tinted line at most: a static ANSI gradient, no animation, no blinking, no
81
+ // full-field luminance swing, so it stays flash-safe on any terminal. It marks the adhd turn.
82
+ const RAINBOW = [196, 208, 226, 46, 51, 21, 201];
83
+ export function rainbowLine(text) {
84
+ let color = 0;
85
+ let out = '';
86
+ for (const ch of String(text)) {
87
+ out += ch === ' ' ? ch : `\u001b[38;5;${RAINBOW[color++ % RAINBOW.length]}m${ch}`;
88
+ }
89
+ return `${out}\u001b[0m`;
90
+ }
91
+
92
+ // Fresh workflow stacks mean recent sessions worth briefing: any stack file touched in 24 h.
93
+ export function freshStacks(cwd, now = Date.now()) {
94
+ try {
95
+ const dir = join(cwd, '.litmuse', 'workflow-stack');
96
+ const day = 24 * 60 * 60 * 1000;
97
+ return readdirSync(dir).some((name) => name.endsWith('.json') && now - statSync(join(dir, name)).mtimeMs <= day);
98
+ } catch {
99
+ return false;
100
+ }
101
+ }
102
+
103
+ // Thread rows for the status card, read from the workflow stacks at render time. Every count
104
+ // resolves from exactly one source, in this order: the frame's recorded k/n, the named plan's
105
+ // rows, the active goal's criteria, otherwise the indeterminate pulse. Frames without a stage
106
+ // (a pure `record` spine) and frames older than 24 h are dropped; sibling sessions older than
107
+ // 15 minutes are skipped. Order is fixed (own stack, then siblings by file name), so a repaint
108
+ // with no change is byte-identical.
109
+ const THREAD_CAP = 6;
110
+ const THREAD_STALE_MS = 24 * 60 * 60 * 1000;
111
+ const SIBLING_FRESH_MS = 15 * 60 * 1000;
112
+
113
+ const readStackFile = (path) => {
114
+ try {
115
+ const text = readFileSync(path, 'utf8');
116
+ if (text.length > 64 * 1024) return null;
117
+ const parsed = JSON.parse(text);
118
+ return Array.isArray(parsed.frames) ? parsed : null;
119
+ } catch {
120
+ return null;
121
+ }
122
+ };
123
+
124
+ const frameGauge = (frame, cwd, goal) => {
125
+ if (Number.isInteger(frame.n) && Number.isInteger(frame.total)) return gauge(frame.n, frame.total);
126
+ if (frame.ref?.plan) {
127
+ const plan = planProgress(cwd, frame.ref.plan);
128
+ if (plan) return gauge(plan.done, plan.total);
129
+ }
130
+ if (goal) return gauge(goal.met, goal.total);
131
+ return gauge(null, null);
132
+ };
133
+
134
+ const frameRow = (frame, session, cwd, goal) => ({
135
+ session,
136
+ skill: typeof frame.skill === 'string' ? frame.skill : 'unknown',
137
+ stage: typeof frame.stage === 'string' ? frame.stage : null,
138
+ status: frame.status === 'paused' ? 'paused' : 'active',
139
+ gauge: frameGauge(frame, cwd, goal),
140
+ verdicts: frame.verdicts && typeof frame.verdicts === 'object' ? { ...frame.verdicts } : undefined,
141
+ });
142
+
143
+ export function threadRows({ cwd, session, now = Date.now() }) {
144
+ const goal = activeGoal(cwd);
145
+ let dir = null;
146
+ try {
147
+ dir = readdirSync(join(cwd, '.litmuse', 'workflow-stack'));
148
+ } catch {
149
+ return [];
150
+ }
151
+ const own = session ? `${String(session).replace(/[^A-Za-z0-9_-]/gu, '_').slice(0, 128)}.json` : null;
152
+ const rows = [];
153
+ if (own) {
154
+ const stack = readStackFile(join(cwd, '.litmuse', 'workflow-stack', own));
155
+ for (const frame of stack?.frames ?? []) {
156
+ if (!frame.stage) continue;
157
+ if (frame.at && now - Date.parse(frame.at) > THREAD_STALE_MS) continue;
158
+ rows.push(frameRow(frame, session, cwd, goal));
159
+ }
160
+ }
161
+ for (const name of dir.filter((entry) => entry.endsWith('.json') && entry !== own).sort()) {
162
+ const path = join(cwd, '.litmuse', 'workflow-stack', name);
163
+ let mtime = 0;
164
+ try {
165
+ mtime = statSync(path).mtimeMs;
166
+ } catch {
167
+ continue;
168
+ }
169
+ if (now - mtime > SIBLING_FRESH_MS) continue;
170
+ const stack = readStackFile(path);
171
+ const top = stack?.frames.at(-1);
172
+ if (!top?.stage) continue;
173
+ rows.push({ ...frameRow(top, name.replace(/\.json$/, ''), cwd, goal), sibling: true });
174
+ }
175
+ return { rows: rows.slice(0, THREAD_CAP), omitted: Math.max(0, rows.length - THREAD_CAP) };
176
+ }
177
+
70
178
  export function buildCard({ cwd, session, env = process.env, now = Date.now() }) {
71
179
  const rec = loadStage({ cwd, session, env });
72
180
  const goal = activeGoal(cwd);
73
- if (!rec) return { active: false, route: null, goal, plan: planProgress(cwd, null), lanes: [], now };
181
+ const threads = threadRows({ cwd, session: rec?.session ?? session ?? null, now });
182
+ if (!rec) return { active: false, route: null, goal, plan: planProgress(cwd, null), lanes: [], threads, idle: freshStacks(cwd, now), now, noColor: Boolean(env.NO_COLOR) };
74
183
  const lanes = (rec.lanes ?? []).map((l) => ({
75
184
  id: l.id, type: l.type, status: l.status,
76
185
  elapsedMs: (l.endedAt ?? now) - l.startedAt,
@@ -78,15 +187,78 @@ export function buildCard({ cwd, session, env = process.env, now = Date.now() })
78
187
  return {
79
188
  active: Boolean(rec.route), route: rec.route ?? null, keyword: rec.keyword ?? null, kind: rec.kind ?? null,
80
189
  session: rec.session ?? null, activeMs: rec.route && rec.activatedAt ? now - rec.activatedAt : null,
81
- plan: planProgress(cwd, rec.plan ?? null), lanes, goal, updated: rec.updated ?? null, now,
190
+ plan: planProgress(cwd, rec.plan ?? null), lanes, goal, threads, idle: !rec.route && freshStacks(cwd, now), updated: rec.updated ?? null, now, noColor: Boolean(env.NO_COLOR),
82
191
  };
83
192
  }
84
193
 
194
+ // Skill looks (BRIEF-mod-looks). Static TTY only: colour plus a label, never animation —
195
+ // the host has no card animation surface, and NO_COLOR strips even the static colour.
196
+ // The registry maps a card state to its headline paint; the lanes strip and the done burst
197
+ // are extra lines drawn from real reports only, never placeholders.
198
+ const paint = (code, text, color) => (color ? `\u001b[${code}m${text}\u001b[0m` : text);
199
+
200
+ // Waiting on the user: a paused thread, or a lane that reported failure. A turn where the
201
+ // model asked a question but left no tool state is not visible to the card.
202
+ const waitingOf = (card) => (card.threads?.rows ?? []).some((row) => row.status === 'paused')
203
+ || (card.lanes ?? []).some((lane) => lane.status === 'failed');
204
+
205
+ const headlineFor = (card, color) => {
206
+ if (!card.active) return 'LIT · no active route in this workspace';
207
+ const route = card.route ?? 'lit-loop';
208
+ if (waitingOf(card)) return paint('33;1', `LIT · ${route} · YOUR TURN`, color);
209
+ if (route === 'lit-adhd') return color ? rainbowLine('LIT · lit-adhd · brief on screen') : 'LIT · lit-adhd · brief on screen';
210
+ return `LIT · ${route}${card.activeMs !== null ? ` · active ${fmtDuration(card.activeMs)}` : ''}${card.kind && card.kind !== 'ignite' ? ` (${card.kind})` : ''}`;
211
+ };
212
+
213
+ // Canonical review-lane display names, in REVIEW_LANES order; the strip reads the same
214
+ // list, so the two cannot drift apart.
215
+ const REVIEW_LANE_LABELS = Object.freeze({ goal: 'scope', quality: 'package', security: 'security', qa: 'tests+evidence', context: 'surface' });
216
+ const VERDICT_DOT = Object.freeze({ pass: ['32;1', '●'], fail: ['31;1', '●'], running: ['33;1', '◐'], pending: ['33', '○'] });
217
+
218
+ const laneSegment = (id, verdict, color) => {
219
+ const label = REVIEW_LANE_LABELS[id] ?? id;
220
+ const known = verdict ? VERDICT_DOT[verdict] : null;
221
+ if (!known) return paint('2', `${label} ○`, color);
222
+ return paint(known[0], `${label} ${known[1]}`, color);
223
+ };
224
+
225
+ const lanesStrip = (card, color) => {
226
+ const frame = (card.threads?.rows ?? []).find((row) => row.skill === 'review-work' && row.verdicts);
227
+ const verdicts = frame?.verdicts ?? {};
228
+ return `review lanes: ${REVIEW_LANES.map(({ id }) => laneSegment(id, verdicts[id], color)).join(' · ')}`;
229
+ };
230
+
231
+ // Static TTY cannot play a one-shot animation and settle: a freshly completed plan or goal
232
+ // prints one celebratory line until the freshness window passes, then the card settles on
233
+ // its own. A published release has no signal the card can see.
234
+ const BURST_FRESH_MS = 15 * 60 * 1000;
235
+ const burstOf = (card, now) => {
236
+ const plan = card.plan;
237
+ if (plan && plan.total > 0 && plan.done >= plan.total && Number.isFinite(plan.mtimeMs) && now - plan.mtimeMs <= BURST_FRESH_MS) {
238
+ return `✨ done: ${plan.file} · ${plan.done}/${plan.total} rows ticked ✨`;
239
+ }
240
+ const goal = card.goal;
241
+ if (goal && goal.total > 0 && goal.met >= goal.total && Number.isFinite(goal.mtimeMs) && now - goal.mtimeMs <= BURST_FRESH_MS) {
242
+ return `✨ goal done: ${goal.objective} ✨`;
243
+ }
244
+ return null;
245
+ };
246
+
85
247
  export function renderCard(card) {
86
248
  const out = [];
87
- out.push(card.active
88
- ? `LIT · ${card.route}${card.activeMs !== null ? ` · active ${fmtDuration(card.activeMs)}` : ''}${card.kind && card.kind !== 'ignite' ? ` (${card.kind})` : ''}`
89
- : 'LIT · no active route in this workspace');
249
+ const color = !card.noColor;
250
+ out.push(headlineFor(card, color));
251
+ if (!card.active && card.idle) out.push('idle — say `lit adhd` for the cross-session brief');
252
+ const threads = card.threads?.rows ?? [];
253
+ if (threads.length) {
254
+ out.push('threads:');
255
+ threads.forEach((thread, index) => {
256
+ const branch = index === threads.length - 1 && !card.threads.omitted ? '└─' : '├─';
257
+ const where = thread.sibling ? ` (${thread.session})` : '';
258
+ out.push(`${branch} ${thread.skill}${thread.stage ? ` ${thread.stage}` : ''} ${thread.status} ${thread.gauge}${where}`);
259
+ });
260
+ if (card.threads.omitted) out.push(`+${card.threads.omitted} more`);
261
+ }
90
262
  if (card.plan) {
91
263
  out.push(`plan ${card.plan.done}/${card.plan.total} · ${card.plan.file}${card.plan.next ? ` · next ${card.plan.next.row}. ${card.plan.next.text}` : ' · all rows ticked'}`);
92
264
  }
@@ -94,9 +266,12 @@ export function renderCard(card) {
94
266
  const finished = card.lanes.filter((l) => l.status !== 'running');
95
267
  if (running.length) out.push(`lanes running: ${running.map((l) => `${l.type} ${fmtDuration(l.elapsedMs)}`).join(', ')}`);
96
268
  if (finished.length) out.push(`lanes finished: ${finished.map((l) => `${l.type} ${l.status} ${fmtDuration(l.elapsedMs)}`).join(', ')}`);
269
+ if (card.route === 'review-work') out.push(lanesStrip(card, color));
97
270
  if (card.goal) {
98
271
  out.push(`goal: ${card.goal.objective} (${card.goal.met}/${card.goal.total} criteria met)`);
99
272
  for (const c of card.goal.open.slice(0, 5)) out.push(` open ${c.id}: ${c.text}`);
100
273
  }
274
+ const burst = burstOf(card, card.now ?? Date.now());
275
+ if (burst) out.push(burst);
101
276
  return out.join('\n');
102
277
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litmuse",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Installer for LitMuse: skills, hooks and agents that help Meta's Muse Code plan, build, check and hand off work.",
5
5
  "keywords": [
6
6
  "muse",
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "name": "litmuse",
4
4
  "displayName": "LitMuse",
5
- "version": "0.1.1",
5
+ "version": "0.1.2",
6
6
  "description": "LitFamily workflows for Muse Code: bare lit activation, planning, evidence-bound execution, review, research, handoff and Korean prose care.",
7
7
  "compat": {
8
8
  "source": "native",
@@ -115,6 +115,16 @@
115
115
  "path": "skills/lit-handoff/SKILL.md",
116
116
  "enabledDefault": true
117
117
  },
118
+ {
119
+ "id": "lit-hijack",
120
+ "path": "skills/lit-hijack/SKILL.md",
121
+ "enabledDefault": true
122
+ },
123
+ {
124
+ "id": "lit-adhd",
125
+ "path": "skills/lit-adhd/SKILL.md",
126
+ "enabledDefault": true
127
+ },
118
128
  {
119
129
  "id": "lit-init",
120
130
  "path": "skills/lit-init/SKILL.md",
@@ -150,6 +160,11 @@
150
160
  "path": "skills/lit-diagram-drawer/SKILL.md",
151
161
  "enabledDefault": true
152
162
  },
163
+ {
164
+ "id": "lit-schematic",
165
+ "path": "skills/lit-schematic/SKILL.md",
166
+ "enabledDefault": true
167
+ },
153
168
  {
154
169
  "id": "lit-typographic-motion",
155
170
  "path": "skills/lit-typographic-motion/SKILL.md",
@@ -236,7 +251,7 @@
236
251
  "node",
237
252
  "hooks/session-start.mjs",
238
253
  "--digest",
239
- "b8961789243b3149"
254
+ "11ba46df6010f4d8"
240
255
  ],
241
256
  "timeoutMs": 5000
242
257
  },
@@ -247,7 +262,7 @@
247
262
  "node",
248
263
  "hooks/user-prompt-submit.mjs",
249
264
  "--digest",
250
- "91a1d749ba4c3fd3"
265
+ "c55594062398b5ea"
251
266
  ],
252
267
  "timeoutMs": 5000,
253
268
  "statusMessage": "LitMuse route check"
@@ -259,7 +274,7 @@
259
274
  "node",
260
275
  "hooks/pre-tool-use.mjs",
261
276
  "--digest",
262
- "eedfd1f3ebe5cb38"
277
+ "b98a3b323bb1756c"
263
278
  ],
264
279
  "timeoutMs": 5000
265
280
  },
@@ -270,7 +285,7 @@
270
285
  "node",
271
286
  "hooks/post-tool-use.mjs",
272
287
  "--digest",
273
- "dac8628eaa6da79a"
288
+ "1aae7c29d41747b8"
274
289
  ],
275
290
  "timeoutMs": 5000
276
291
  },
@@ -281,7 +296,7 @@
281
296
  "node",
282
297
  "hooks/post-tool-use-failure.mjs",
283
298
  "--digest",
284
- "b905e80e38695ee0"
299
+ "3f1b940d78192ff9"
285
300
  ],
286
301
  "timeoutMs": 5000
287
302
  },
@@ -292,7 +307,7 @@
292
307
  "node",
293
308
  "hooks/stop.mjs",
294
309
  "--digest",
295
- "d7f72526de2e1053"
310
+ "0bd9020bf0bbc1af"
296
311
  ],
297
312
  "timeoutMs": 5000
298
313
  },
@@ -303,7 +318,7 @@
303
318
  "node",
304
319
  "hooks/subagent-start.mjs",
305
320
  "--digest",
306
- "20285c4542661b42"
321
+ "6694ead2b6e3ac2b"
307
322
  ],
308
323
  "timeoutMs": 5000
309
324
  },
@@ -314,7 +329,7 @@
314
329
  "node",
315
330
  "hooks/subagent-stop.mjs",
316
331
  "--digest",
317
- "35624765c5646ff0"
332
+ "023daac9ec9f7aeb"
318
333
  ],
319
334
  "timeoutMs": 5000
320
335
  },
@@ -325,7 +340,7 @@
325
340
  "node",
326
341
  "hooks/session-end.mjs",
327
342
  "--digest",
328
- "6e0bc285570a4cc2"
343
+ "18f92d375de7d7b0"
329
344
  ],
330
345
  "timeoutMs": 5000
331
346
  },
@@ -336,7 +351,7 @@
336
351
  "node",
337
352
  "hooks/pre-compact.mjs",
338
353
  "--digest",
339
- "9c089c69dc07f94a"
354
+ "dfdcd29de71fafe0"
340
355
  ],
341
356
  "timeoutMs": 5000
342
357
  }
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Read-only LIT card for this workspace: route, time active, plan n/m, running lanes, goal, last handoff"
2
+ description: "Read-only LIT card for this workspace: route, plan n/m, lanes, threads, goal, last handoff"
3
3
  argument-hint: "[slug]"
4
4
  ---
5
5
  Print the LitMuse status card for this workspace. Scope: $ARGUMENTS (empty means everything). Write nothing.
@@ -9,6 +9,7 @@ Print the LitMuse status card for this workspace. Scope: $ARGUMENTS (empty means
9
9
  - Route and time active: the newest record from `latest.json` (route, activatedAt, kind), or else the `route` on the last line of the newest `.litmuse/ledger/*.jsonl`.
10
10
  - Plan: the plan named in the record, or else the newest `plans/*.md`. Count `- [x] N.` against all `- [ ] N.` and `- [x] N.` rows, and name the first open row.
11
11
  - Lanes: the record's `lanes` entries with `status` running, done or failed, and their elapsed time (`endedAt` or now, minus `startedAt`).
12
+ - Threads: `.litmuse/workflow-stack/<session>.json` frames (paused spines above running sides) plus other sessions' fresh paused tops: a real gauge when the count is known, the `… N steps` fact line otherwise — never a placeholder. Six rows at most, then `+N more`; an idle card offers `lit adhd` instead.
12
13
  - Goal: each `.litmuse/goals/*.json` whose status is `active`, with its objective and the criteria that are not `pass`.
13
14
  3. Add the first lines of `.litmuse/handoff/latest.md` if present, and the count of tool events and failures in the newest ledger.
14
15
 
@@ -6,7 +6,7 @@ LitMuse route request: $ARGUMENTS
6
6
 
7
7
  Treat the line above as a LitMuse request; the prompt hook has already recorded its route.
8
8
 
9
- 1. The first word picks the route: loop → lit-loop, plan → lit-plan, work → litwork, goal → litgoal, research → litresearch, review → review-work, start → start-work, recap → lit-recap, status → lit-recap, handoff → lit-handoff, init → lit-init, debug → debugging, interview → deep-interview, crucible → lit-crucible, commit → lit-commit, refactor → refactor, burnoff → lit-burnoff, burnoff-file → lit-burnoff-file, brainstorm → lit-brainstorm, brainstorming → lit-brainstorm, ideation → lit-brainstorm, ideate → lit-brainstorm, idea → lit-brainstorm, ideas → lit-brainstorm, team → lit-team, conference → autoconference, rules → rules, lsp → lsp, diagnostics → lsp, lsp-setup → lsp-setup, motion → lit-typographic-motion, film → lit-typographic-motion, video → lit-typographic-motion, 영상 → lit-typographic-motion, explain → lit-comprehend, humanize → lit-humanizer, korean → lit-humanizer, 다듬어 → lit-humanizer, 윤문 → lit-humanizer. Any other first word means lit-loop, except a task that asks to make a film (a film compound such as kinetic type, lyric video, title sequence, cover loop, 모션그래픽, 가사 영상, or a bare video noun with a making verb), which goes to lit-typographic-motion. Skills without a keyword (frontend-ui-ux, readme-studio, visual-qa, comment-checker, wikify, autoresearch, structural-search, browser-drive, lit-code) are reached through lit-loop or their own `/<skill>` command.
9
+ 1. The first word picks the route: loop → lit-loop, plan → lit-plan, work → litwork, goal → litgoal, research → litresearch, review → review-work, start → start-work, recap → lit-recap, status → lit-recap, handoff → lit-handoff, init → lit-init, debug → debugging, interview → deep-interview, crucible → lit-crucible, commit → lit-commit, refactor → refactor, burnoff → lit-burnoff, burnoff-file → lit-burnoff-file, brainstorm → lit-brainstorm, brainstorming → lit-brainstorm, ideation → lit-brainstorm, ideate → lit-brainstorm, idea → lit-brainstorm, ideas → lit-brainstorm, hijack → lit-hijack, hijacking → lit-hijack, 하이재킹 → lit-hijack, 하이잭 → lit-hijack, adhd → lit-adhd, rewind → lit-adhd, approve → start-work, deny → start-work, team → lit-team, conference → autoconference, rules → rules, lsp → lsp, diagnostics → lsp, lsp-setup → lsp-setup, motion → lit-typographic-motion, film → lit-typographic-motion, video → lit-typographic-motion, 영상 → lit-typographic-motion, explain → lit-comprehend, humanize → lit-humanizer, korean → lit-humanizer, 다듬어 → lit-humanizer, 윤문 → lit-humanizer. Any other first word means lit-loop, except a task that asks to make a film (a film compound such as kinetic type, lyric video, title sequence, cover loop, 모션그래픽, 가사 영상, or a bare video noun with a making verb), which goes to lit-typographic-motion. Skills without a keyword (frontend-ui-ux, readme-studio, visual-qa, comment-checker, wikify, autoresearch, structural-search, browser-drive, lit-code) are reached through lit-loop or their own `/<skill>` command.
10
10
  2. Load that skill with read_skill using the id `plugin:litmuse:<route>` before any other tool, and follow it. When the hook context says the route is already active or resuming, skip steps 2 and 3: keep the criteria, plan and ledger you have.
11
11
  3. Your first output, before any tool call or other text, is the line `🔥 **LIT IGNITED · <route>** 🔥` on its own line, once. When the hook context says the route continues, the line is `🔥 **LIT CONTINUES · <route>** 🔥` instead.
12
12
  4. Report in reader mode: the result, any material risk, and the action the user must take.
@@ -10,7 +10,7 @@ const PLUGIN = join(LIB, '..', '..');
10
10
 
11
11
  // Files outside hooks/lib that a hook loads at run time: PostToolUse runs the lit-humanizer detector, and Stop runs the
12
12
  // lit-typographic-motion done-check with every engine module it imports.
13
- const MOTION_DONE = ['node/done.mjs', 'node/cover-assets.mjs', 'core/constants.mjs', 'node/chrome.mjs', 'node/cover-image.mjs', 'node/cover-treatment.mjs', 'node/treatment.mjs', 'node/sound.mjs', 'core/util.mjs', 'node/look.mjs', 'node/stills.mjs', 'node/analysis.mjs', 'core/flash.mjs', 'node/png.mjs'];
13
+ const MOTION_DONE = ['node/done.mjs', 'node/cover-assets.mjs', 'core/constants.mjs', 'node/chrome.mjs', 'node/cover-image.mjs', 'node/cover-treatment.mjs', 'node/treatment.mjs', 'node/sound.mjs', 'node/house.mjs', 'node/styles.mjs', 'node/score.mjs', 'core/presets.mjs', 'core/util.mjs', 'node/look.mjs', 'node/stills.mjs', 'node/analysis.mjs', 'core/flash.mjs', 'node/png.mjs'];
14
14
  export const DIGEST_EXTRAS = ['skills/lit-humanizer/scripts/core.mjs', 'skills/lit-humanizer/rules.json', ...MOTION_DONE.map((f) => `skills/lit-typographic-motion/engine/${f}`)];
15
15
 
16
16
  const listed = (dir, ext) => readdirSync(dir).filter((f) => f.endsWith(ext)).sort().map((f) => join(dir, f));
@@ -0,0 +1,68 @@
1
+ // The `go` half of the lit-adhd brief: the collector writes this session's next-step record
2
+ // under `.litmuse/adhd/`, and a later whole-prompt `go` reads it back and deletes it. The session
3
+ // binding is the file name, so another session's record is never opened. A step older than 30
4
+ // minutes is stale: the brief it came from no longer describes the work.
5
+ import { closeSync, constants, fstatSync, openSync, readFileSync, unlinkSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+
8
+ import { workspaceOf } from './common.mjs';
9
+ import { resolveProjectStateRoot } from './workflow-stack.mjs';
10
+
11
+ // The whole prompt, nothing more: `go`, `lit go`, `ㄱㄱ`, `고` or `진행`, with trailing punctuation.
12
+ const GO_PROMPT = /^\s*(?:(?:lit\s+)?go|ㄱㄱ|고|진행)[\s.!?]*$/iu;
13
+ export const isGoPrompt = (prompt) => typeof prompt === 'string' && prompt.length <= 64 && GO_PROMPT.test(prompt);
14
+
15
+ export const NEXT_STEP_MAX_AGE_MS = 30 * 60_000;
16
+ const KINDS = new Set(['resume-frame', 'approve-boundary', 'continue-work', 'start-plan', 'none']);
17
+
18
+ const nextStepFile = (cwd, sessionId) => {
19
+ const id = String(sessionId ?? 'session').replace(/[^A-Za-z0-9_-]/gu, '_').slice(0, 128) || 'session';
20
+ return join(resolveProjectStateRoot(cwd), '.litmuse', 'adhd', `next-${id}.json`);
21
+ };
22
+
23
+ /**
24
+ * Reads and deletes this session's next-step record. Returns `{kind, target}` when the record is
25
+ * whole and at most 30 minutes old, otherwise null; a stale or unreadable record is deleted all
26
+ * the same. Never throws.
27
+ */
28
+ export const takeNextStep = (payload, now = Date.now()) => {
29
+ const sessionId = typeof payload?.session_id === 'string' && payload.session_id ? payload.session_id : null;
30
+ if (!sessionId) return null;
31
+ let cwd = null;
32
+ try {
33
+ cwd = workspaceOf(payload);
34
+ } catch {
35
+ return null;
36
+ }
37
+ if (!cwd) return null;
38
+ const path = nextStepFile(cwd, sessionId);
39
+ let text;
40
+ try {
41
+ const fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
42
+ try {
43
+ const stat = fstatSync(fd);
44
+ if (!stat.isFile() || stat.size > 16_384) return null;
45
+ text = readFileSync(fd, 'utf8');
46
+ } catch {
47
+ return null;
48
+ } finally {
49
+ try {
50
+ closeSync(fd);
51
+ } catch { /* already closed */ }
52
+ }
53
+ } catch {
54
+ return null;
55
+ } finally {
56
+ try {
57
+ unlinkSync(path);
58
+ } catch { /* nothing to consume */ }
59
+ }
60
+ try {
61
+ const record = JSON.parse(text);
62
+ const age = now - Date.parse(record.at);
63
+ if (!KINDS.has(record.kind) || !(age >= -60_000 && age <= NEXT_STEP_MAX_AGE_MS)) return null;
64
+ return { kind: record.kind, target: record.target && typeof record.target === 'object' ? record.target : null };
65
+ } catch {
66
+ return null;
67
+ }
68
+ };