@litfamily/lithermes 1.0.29 → 1.0.31

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/README.md +12 -9
  2. package/README_Ko-KR.md +12 -9
  3. package/assets/lithermes-plugin/README.md +15 -2
  4. package/assets/lithermes-plugin/__init__.py +49 -10
  5. package/assets/lithermes-plugin/adhd_stack.py +272 -0
  6. package/assets/lithermes-plugin/bounded_work.py +41 -0
  7. package/assets/lithermes-plugin/core.py +53 -4
  8. package/assets/lithermes-plugin/core_commands.py +99 -0
  9. package/assets/lithermes-plugin/core_contexts.py +54 -4
  10. package/assets/lithermes-plugin/core_contract.py +2 -2
  11. package/assets/lithermes-plugin/core_plans.py +80 -0
  12. package/assets/lithermes-plugin/core_routing.py +126 -0
  13. package/assets/lithermes-plugin/core_workflow_commands.py +11 -5
  14. package/assets/lithermes-plugin/diagnostics.py +3 -2
  15. package/assets/lithermes-plugin/lit_mark.py +75 -0
  16. package/assets/lithermes-plugin/payload-version.json +473 -61
  17. package/assets/lithermes-plugin/plugin.yaml +1 -1
  18. package/assets/lithermes-plugin/scientific_visualization.py +23 -0
  19. package/assets/lithermes-plugin/skills/frontend-ui-ux/SKILL.md +7 -7
  20. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/components.css +414 -0
  21. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/fonts.json +42 -0
  22. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
  23. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
  24. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
  25. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
  26. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
  27. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
  28. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
  29. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
  30. package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/ui.js +226 -0
  31. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/README.md +2 -4
  32. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +4 -4
  33. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/craft-floor.md +10 -0
  34. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
  35. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe-rules.json +6 -1
  36. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe.mjs +76 -4
  37. package/assets/lithermes-plugin/skills/lit-adhd/SKILL.md +202 -0
  38. package/assets/lithermes-plugin/skills/lit-adhd/references/brief-contract.md +78 -0
  39. package/assets/lithermes-plugin/skills/lit-adhd/references/card-examples.md +97 -0
  40. package/assets/lithermes-plugin/skills/lit-adhd/references/status-taxonomy.md +56 -0
  41. package/assets/lithermes-plugin/skills/lit-adhd/scripts/adhd.mjs +11 -0
  42. package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/collect.mjs +650 -0
  43. package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/gauge.mjs +89 -0
  44. package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/hermes-sessions.mjs +219 -0
  45. package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/next-step.mjs +101 -0
  46. package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/plan-task-rows.mjs +32 -0
  47. package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/redact.mjs +42 -0
  48. package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/secret-shapes.mjs +59 -0
  49. package/assets/lithermes-plugin/skills/lit-diagram-drawer/SKILL.md +7 -4
  50. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/style-guide.md +20 -11
  51. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-architecture.md +3 -3
  52. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dependency.md +1 -1
  53. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-tree.md +3 -3
  54. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-wardley.md +1 -1
  55. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/verifier-guide.md +5 -0
  56. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_quality.py +4 -4
  57. package/assets/lithermes-plugin/skills/lit-docx/SKILL.md +1 -1
  58. package/assets/lithermes-plugin/skills/lit-hijack/SKILL.md +239 -0
  59. package/assets/lithermes-plugin/skills/lit-hijack/references/capture-and-provenance.md +123 -0
  60. package/assets/lithermes-plugin/skills/lit-hijack/references/clean-room-build.md +128 -0
  61. package/assets/lithermes-plugin/skills/lit-hijack/references/distil-and-licence.md +128 -0
  62. package/assets/lithermes-plugin/skills/lit-hijack/references/gap-and-port-design.md +152 -0
  63. package/assets/lithermes-plugin/skills/lit-hijack/references/parity-and-report.md +131 -0
  64. package/assets/lithermes-plugin/skills/lit-hijack/scripts/hijack.mjs +995 -0
  65. package/assets/lithermes-plugin/skills/lit-plan/SKILL.md +34 -1
  66. package/assets/lithermes-plugin/skills/lit-pptx/SKILL.md +11 -6
  67. package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
  68. package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
  69. package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
  70. package/assets/lithermes-plugin/skills/lit-pptx/fonts/provenance.json +1 -0
  71. package/assets/lithermes-plugin/skills/lit-pptx/references/anti-slop-checklist.md +3 -0
  72. package/assets/lithermes-plugin/skills/lit-pptx/references/authoring-guide.md +1 -1
  73. package/assets/lithermes-plugin/skills/lit-pptx/references/consensus-protocol.md +1 -1
  74. package/assets/lithermes-plugin/skills/lit-pptx/references/direction-step.md +8 -2
  75. package/assets/lithermes-plugin/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +175 -0
  76. package/assets/lithermes-plugin/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +156 -0
  77. package/assets/lithermes-plugin/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +141 -0
  78. package/assets/lithermes-plugin/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +160 -0
  79. package/assets/lithermes-plugin/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +183 -0
  80. package/assets/lithermes-plugin/skills/lit-pptx/references/layout-families.md +1 -1
  81. package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/bauhaus.md +105 -0
  82. package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/computational-brutalism.md +111 -0
  83. package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/neo-brutalism.md +114 -0
  84. package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/newsroom.md +106 -0
  85. package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/riso.md +101 -0
  86. package/assets/lithermes-plugin/skills/lit-pptx/scripts/craft_extras.py +49 -0
  87. package/assets/lithermes-plugin/skills/lit-pptx/scripts/deck_output.py +4 -2
  88. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/render-pack.js +96 -10
  89. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/template-registry.js +50 -6
  90. package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +103 -0
  91. package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
  92. package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
  93. package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
  94. package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
  95. package/assets/lithermes-plugin/skills/lit-recap/SKILL.md +1 -1
  96. package/assets/lithermes-plugin/skills/lit-schematic/SKILL.md +144 -0
  97. package/assets/lithermes-plugin/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +38 -0
  98. package/assets/lithermes-plugin/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +133 -0
  99. package/assets/lithermes-plugin/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +87 -0
  100. package/assets/lithermes-plugin/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +85 -0
  101. package/assets/lithermes-plugin/skills/lit-schematic/references/cutaway-outputs.md +70 -0
  102. package/assets/lithermes-plugin/skills/lit-schematic/references/isometric-craft.md +101 -0
  103. package/assets/lithermes-plugin/skills/lit-schematic/references/journal-figures.md +72 -0
  104. package/assets/lithermes-plugin/skills/lit-schematic/references/kit-api.md +134 -0
  105. package/assets/lithermes-plugin/skills/lit-schematic/references/pfd-and-layout.md +53 -0
  106. package/assets/lithermes-plugin/skills/lit-schematic/references/pid-drafting.md +103 -0
  107. package/assets/lithermes-plugin/skills/lit-schematic/references/pid-symbols.md +163 -0
  108. package/assets/lithermes-plugin/skills/lit-schematic/references/verification.md +100 -0
  109. package/assets/lithermes-plugin/skills/lit-schematic/scripts/doctor.mjs +57 -0
  110. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/figure.mjs +81 -0
  111. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/fonts.mjs +27 -0
  112. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/geom.mjs +113 -0
  113. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/interactive.mjs +292 -0
  114. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/isa.mjs +115 -0
  115. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/iso.mjs +719 -0
  116. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/legend-sheet.mjs +162 -0
  117. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/pid.mjs +937 -0
  118. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/png.mjs +132 -0
  119. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/qa-core.mjs +658 -0
  120. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/route.mjs +218 -0
  121. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/sheet.mjs +890 -0
  122. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/svg.mjs +82 -0
  123. package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/theme.mjs +115 -0
  124. package/assets/lithermes-plugin/skills/lit-schematic/scripts/motion.mjs +129 -0
  125. package/assets/lithermes-plugin/skills/lit-schematic/scripts/qa.mjs +92 -0
  126. package/assets/lithermes-plugin/skills/lit-schematic/scripts/render.mjs +233 -0
  127. package/assets/lithermes-plugin/skills/lit-schematic/scripts/scaffold.mjs +60 -0
  128. package/assets/lithermes-plugin/skills/lit-scientific-visualization/SKILL.md +19 -1
  129. package/assets/lithermes-plugin/skills/lit-typographic-motion/SKILL.md +12 -11
  130. package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/motion.mjs +56 -2
  131. package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/runtime.mjs +129 -4
  132. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +9 -0
  133. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/blender/runner.py +218 -0
  134. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/blender.mjs +154 -0
  135. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/board.mjs +132 -0
  136. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/completion.mjs +48 -5
  137. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/gate.mjs +13 -0
  138. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/house.mjs +44 -0
  139. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/scaffold.mjs +238 -0
  140. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/score.mjs +643 -0
  141. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound-dsp.mjs +21 -0
  142. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound.mjs +171 -0
  143. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-qa.mjs +23 -3
  144. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage.mjs +124 -17
  145. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/styles.mjs +240 -0
  146. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/treatment.mjs +152 -10
  147. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
  148. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
  149. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
  150. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
  151. package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/hero-3d/expected.json +67 -0
  152. package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/hero-3d/index.html +139 -0
  153. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/depth.md +74 -0
  154. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/studio.md +87 -0
  155. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/style-bibles.md +9 -2
  156. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/treatment.md +24 -8
  157. package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/paper.json +41 -0
  158. package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/swiss-signal.json +41 -0
  159. package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/terminalcore.json +42 -0
  160. package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/tidal.json +41 -0
  161. package/assets/lithermes-plugin/skills/litresearch/SKILL.md +4 -0
  162. package/assets/lithermes-plugin/skills/readme-studio/SKILL.md +1 -1
  163. package/assets/lithermes-plugin/skills/review-work/SKILL.md +2 -0
  164. package/assets/lithermes-plugin/skills/start-work/SKILL.md +2 -1
  165. package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +3 -3
  166. package/package.json +1 -1
  167. package/readme-assets/badge-version.svg +1 -1
  168. package/src/lib/skillPayload.js +1 -1
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/lithermes@1.0.29/readme-assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: seven armored robots power on one by one, the LitHermes 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/lithermes@1.0.31/readme-assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: seven armored robots power on one by one, the LitHermes robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
2
2
 
3
3
  <p align="center">
4
- <a href="#install"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/badge-version.svg" alt="1.0.29" /></a>
5
- <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/badge-license.svg" alt="MIT license" /></a>
4
+ <a href="#install"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/badge-version.svg" alt="1.0.31" /></a>
5
+ <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/badge-license.svg" alt="MIT license" /></a>
6
6
  </p>
7
7
 
8
8
  <p align="center">
9
- <a href="https://github.com/wjgoarxiv/lithermes/blob/main/docs/guide.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/lucide-book-open.svg" width="16" alt="" /> Guide</a> &nbsp;
10
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/ignition-film.mp4"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/lucide-play.svg" width="16" alt="" /> Ignition film</a> &nbsp;
11
- <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/lucide-shield-check.svg" width="16" alt="" /> MIT</a>
9
+ <a href="https://github.com/wjgoarxiv/lithermes/blob/main/docs/guide.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/lucide-book-open.svg" width="16" alt="" /> Guide</a> &nbsp;
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/ignition-film.mp4"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/lucide-play.svg" width="16" alt="" /> Ignition film</a> &nbsp;
11
+ <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/lucide-shield-check.svg" width="16" alt="" /> MIT</a>
12
12
  </p>
13
13
 
14
14
  # LitHermes
@@ -17,7 +17,7 @@
17
17
 
18
18
  A plugin for **Hermes Agent**. Put `lit` in front of a request, and Hermes plans the task, does it, checks the result and writes down where things stand, so the next session can pick up where this one stopped.
19
19
 
20
- **[README and docs on GitHub](https://github.com/wjgoarxiv/lithermes#readme)** · [한국어](https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/README_Ko-KR.md)
20
+ **[README and docs on GitHub](https://github.com/wjgoarxiv/lithermes#readme)** · [한국어](https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/README_Ko-KR.md)
21
21
 
22
22
  ## Install
23
23
 
@@ -51,11 +51,14 @@ The reply opens with a `🔥 **LIT IGNITED · <discipline>** 🔥` line once the
51
51
  | `lit research <question>` | `litresearch` | Research with source notes |
52
52
  | `/lit-humanizer` | `lit-humanizer` | Revising Korean or English prose, meaning kept |
53
53
  | `/lit-diagram-drawer <brief>` | `lit-diagram-drawer` | A checked, editable diagram |
54
- | `/lit-pptx <brief>` | `lit-pptx` | A PowerPoint deck in one of eight named looks, with its Markdown source |
54
+ | `/lit-schematic <brief>` | `lit-schematic` | A QA-gated P&ID, apparatus figure or isometric cutaway |
55
+ | `/lit-pptx <brief>` | `lit-pptx` | A PowerPoint deck in one of thirteen named looks, with its Markdown source |
55
56
  | `/lit-docx <brief>` | `lit-docx` | A restrained Word document in one of six named shapes, with its Markdown source |
57
+ | `/lit-hijack <brief>` | `lit-hijack` | A clean-room port of published know-how, licence-gated with parity proof |
58
+ | `/lit-adhd [--here] [--since 7d] [--max 12]` | `lit-adhd` | A cross-session brief with gauges and one next step, read-only |
56
59
  | `/lit-typographic-motion <brief>` | `lit-typographic-motion` | A short film, treatment first |
57
60
 
58
- On a Telegram gateway, use `/lit_loop` and `/lit_plan`. Every skill also loads by name as `lithermes:<name>`, for example `lithermes:lit-pptx` or `lithermes:lit-docx`. [Skills and commands](https://github.com/wjgoarxiv/lithermes/blob/main/docs/skills.md) shows all 37 with pictures.
61
+ On a Telegram gateway, use `/lit_loop` and `/lit_plan`. Every skill also loads by name as `lithermes:<name>`, for example `lithermes:lit-pptx` or `lithermes:lit-docx`. [Skills and commands](https://github.com/wjgoarxiv/lithermes/blob/main/docs/skills.md) shows all 39 with pictures.
59
62
 
60
63
  ## Check, update, remove
61
64
 
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/lithermes@1.0.29/readme-assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 일곱 로봇 패널이 차례로 켜지고, LitHermes 로봇의 눈과 테두리가 빛난 뒤 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/lithermes@1.0.31/readme-assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 일곱 로봇 패널이 차례로 켜지고, LitHermes 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
2
2
 
3
3
  <p align="center">
4
- <a href="#설치"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/badge-version.svg" alt="1.0.29" /></a>
5
- <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/badge-license.svg" alt="MIT license" /></a>
4
+ <a href="#설치"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/badge-version.svg" alt="1.0.31" /></a>
5
+ <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/badge-license.svg" alt="MIT license" /></a>
6
6
  </p>
7
7
 
8
8
  <p align="center">
9
- <a href="https://github.com/wjgoarxiv/lithermes/blob/main/docs/guide.ko.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/lucide-book-open.svg" width="16" alt="" /> 안내</a> &nbsp;
10
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/ignition-film.mp4"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/lucide-play.svg" width="16" alt="" /> Ignition 영상</a> &nbsp;
11
- <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/readme-assets/lucide-shield-check.svg" width="16" alt="" /> MIT</a>
9
+ <a href="https://github.com/wjgoarxiv/lithermes/blob/main/docs/guide.ko.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/lucide-book-open.svg" width="16" alt="" /> 안내</a> &nbsp;
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/ignition-film.mp4"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/lucide-play.svg" width="16" alt="" /> Ignition 영상</a> &nbsp;
11
+ <a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/readme-assets/lucide-shield-check.svg" width="16" alt="" /> MIT</a>
12
12
  </p>
13
13
 
14
14
  # LitHermes
@@ -17,7 +17,7 @@
17
17
 
18
18
  **Hermes Agent**용 플러그인입니다. 요청 앞에 `lit`을 붙이면 Hermes가 작업을 계획하고, 해내고, 결과를 확인한 뒤 어디까지 왔는지 적어 둡니다. 그래서 다음 세션이 그 기록을 읽고 멈춘 자리에서 이어 갈 수 있습니다.
19
19
 
20
- **[GitHub의 README와 문서](https://github.com/wjgoarxiv/lithermes/blob/main/README_Ko-KR.md)** · [English](https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.29/README.md)
20
+ **[GitHub의 README와 문서](https://github.com/wjgoarxiv/lithermes/blob/main/README_Ko-KR.md)** · [English](https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.31/README.md)
21
21
 
22
22
  ## 설치
23
23
 
@@ -51,11 +51,14 @@ lit 외부 의존성 없이 HTML 파일 하나로 할 일 목록을 만들어줘
51
51
  | `lit research <질문>` | `litresearch` | 출처 메모가 있는 조사 |
52
52
  | `/lit-humanizer` | `lit-humanizer` | 의미를 지키며 한국어·영어 문장 다듬기 |
53
53
  | `/lit-diagram-drawer <brief>` | `lit-diagram-drawer` | 검사를 거친, 편집 가능한 다이어그램 |
54
- | `/lit-pptx <brief>` | `lit-pptx` | 여덟 가지 디자인 방향 가운데 하나로 만든 PowerPoint 발표자료와 Markdown 원본 |
54
+ | `/lit-schematic <brief>` | `lit-schematic` | QA 게이트를 거친 P&ID·장치도·아이소메트릭 단면도 |
55
+ | `/lit-hijack <brief>` | `lit-hijack` | 라이선스 게이트와 parity 검증을 거친 공개 노하우의 클린룸 포팅 |
56
+ | `/lit-adhd [--here] [--since 7d] [--max 12]` | `lit-adhd` | 게이지와 다음 한 단계로 최근 세션을 브리핑합니다. 읽기 전용입니다. |
57
+ | `/lit-pptx <brief>` | `lit-pptx` | 열세 가지 디자인 방향 가운데 하나로 만든 PowerPoint 발표자료와 Markdown 원본 |
55
58
  | `/lit-docx <brief>` | `lit-docx` | 여섯 가지 문서 형식 가운데 하나로 짠 절제된 Word 문서와 Markdown 원본 |
56
59
  | `/lit-typographic-motion <brief>` | `lit-typographic-motion` | 트리트먼트부터 쓰는 짧은 영상 |
57
60
 
58
- Telegram gateway에서는 `/lit_loop`와 `/lit_plan`을 쓰세요. 모든 스킬은 `lithermes:<이름>`으로도 불러올 수 있습니다. 예를 들어 `lithermes:lit-pptx`, `lithermes:lit-docx`처럼 씁니다. 스킬 37개 전체는 [스킬과 명령](https://github.com/wjgoarxiv/lithermes/blob/main/docs/skills.ko.md)에서 그림과 함께 볼 수 있습니다.
61
+ Telegram gateway에서는 `/lit_loop`와 `/lit_plan`을 쓰세요. 모든 스킬은 `lithermes:<이름>`으로도 불러올 수 있습니다. 예를 들어 `lithermes:lit-pptx`, `lithermes:lit-docx`처럼 씁니다. 스킬 39개 전체는 [스킬과 명령](https://github.com/wjgoarxiv/lithermes/blob/main/docs/skills.ko.md)에서 그림과 함께 볼 수 있습니다.
59
62
 
60
63
  ## 확인, 업데이트, 삭제
61
64
 
@@ -86,6 +86,18 @@ CLI/plugin name, not the npm package name.
86
86
  installed diagram skill. Its Python checks cover source geometry, accessibility,
87
87
  content requirements, motion source rules, and visible-text findings; actual
88
88
  rendering remains a separate local capability.
89
+ - `/lit-schematic <brief>` and the exact `lit-schematic` route load the
90
+ installed schematic skill. Drawings of real equipment (P&IDs, PFDs, apparatus
91
+ figures, isometric cutaways) belong here; abstract boxes-and-arrows stay with
92
+ `lithermes:lit-diagram-drawer`.
93
+ - `/lit-hijack <brief>` and the exact `lit-hijack` route (also `lit hijack`,
94
+ `lit hijacking`, `lit 하이재킹`, `lit 하이잭`) load the installed hijack skill.
95
+ Published know-how enters only through capture with provenance, a licence gate,
96
+ a source-free spec and a parity proof.
97
+ - `/lit-adhd [--here] [--since 7d] [--max 12]` and the exact `lit-adhd` route
98
+ (also `lit adhd`, `lit rewind`, `lit-rewind`) load the installed adhd skill.
99
+ One collector run reads the session store read-only and prints the
100
+ cross-session brief; a bare `go` continues the offered step within 30 minutes.
89
101
  - The `pre_llm_call` hook injects an Litwork directive when the user says
90
102
  a standalone `lit` or `litwork`. It also routes `lit plan`, `lit review`,
91
103
  `lit research`, `lit goal`, and durable `lit workflow` / `lit kanban` / `lit team`
@@ -179,7 +191,8 @@ CLI/plugin name, not the npm package name.
179
191
  `lithermes:frontend-ui-ux`, `lithermes:readme-studio`, `lithermes:lit-commit`,
180
192
  `lithermes:lit-crucible`, `lithermes:lit-init`, `lithermes:lit-recap`,
181
193
  `lithermes:lit-handoff`, `lithermes:lit-scientific-visualization`,
182
- `lithermes:lit-diagram-drawer`,
194
+ `lithermes:lit-diagram-drawer`, `lithermes:lit-schematic`, `lithermes:lit-hijack`,
195
+ `lithermes:lit-adhd`,
183
196
  `lithermes:lsp`, `lithermes:lsp-setup`,
184
197
  `lithermes:litresearch`, `lithermes:lit-code`, `lithermes:refactor`,
185
198
  `lithermes:lit-burnoff`, `lithermes:review-work`,
@@ -379,7 +392,7 @@ npm --prefix packages/lithermes-installer run sync-plugin -- --in-place
379
392
  uses A/B checks, and keeps a claim/source/confidence/uncertainty graph. The
380
393
  acquisition procedure loads from `lithermes:litresearch` through `skill_view`.
381
394
  - `lit-crucible`, `lit-init`, `lit-recap`, `lit-handoff`,
382
- `lit-scientific-visualization`, and `lit-diagram-drawer`: bounded bare or exact natural routes load the bundled
395
+ `lit-scientific-visualization`, `lit-diagram-drawer`, `lit-schematic`, `lit-hijack`, and `lit-adhd`: bounded bare or exact natural routes load the bundled
383
396
  skill body for the matching explicit skill; they do not fall back to generic
384
397
  Litwork.
385
398
  - `lit goal`: litgoal sets and keeps the finish line; lit-loop and start-work run toward it.
@@ -39,8 +39,11 @@ _NATIVE_COMMANDS = frozenset(
39
39
  "lit-handoff",
40
40
  "lit-scientific-visualization",
41
41
  "lit-diagram-drawer",
42
+ "lit-schematic",
42
43
  "lit-pptx",
43
44
  "lit-docx",
45
+ "lit-hijack",
46
+ "lit-adhd",
44
47
  "lit-typographic-motion",
45
48
  *core.KOREAN_PROSE_COMMANDS,
46
49
  *core.KOREAN_PROSE_ALIASES,
@@ -81,11 +84,11 @@ PORTED_SKILLS = [
81
84
  ),
82
85
  (
83
86
  "debugging",
84
- "Use when something fails, hangs or returns a wrong result and the cause must be reproduced, fixed and rechecked.",
87
+ "Use when something fails, hangs or returns a wrong result; the cause must be reproduced, fixed and rechecked.",
85
88
  ),
86
89
  (
87
90
  "deep-interview",
88
- "Run the LitHermes Socratic deep-interview clarity gate before planning.",
91
+ "Run the Socratic deep-interview clarity gate before planning.",
89
92
  ),
90
93
  (
91
94
  "frontend-ui-ux",
@@ -95,16 +98,28 @@ PORTED_SKILLS = [
95
98
  "lit-diagram-drawer",
96
99
  "Create clear, accessible, Korean-ready diagrams with product-local Python verification and optional export.",
97
100
  ),
101
+ (
102
+ "lit-schematic",
103
+ "Draw P&IDs, PFDs, apparatus figures and isometric cutaways; QA-gated SVG with PDF and 600 dpi PNG in full material colour.",
104
+ ),
98
105
  ("lit-pptx", "Create and inspect PowerPoint decks with the installed Office engine and QA gate."),
99
106
  ("lit-docx", "Create, edit, convert, and audit styled Word documents."),
100
- ("lit-typographic-motion", "Direct an original film from a treatment, or a seamless README cover loop, gated before delivery."),
107
+ (
108
+ "lit-hijack",
109
+ "Port published know-how into your skill.",
110
+ ),
111
+ (
112
+ "lit-adhd",
113
+ "Cross-session brief with gauges and one next step.",
114
+ ),
115
+ ("lit-typographic-motion", "Direct an original film or a README cover loop, gated before delivery."),
101
116
  (
102
117
  "readme-studio",
103
118
  "Write factual READMEs and compose local covers with outlined type, honest image capability and verified motion.",
104
119
  ),
105
120
  (
106
121
  "lit-commit",
107
- "Run the LitHermes git-history specialist for commits, rebases, blame, and bisect.",
122
+ "Run the git-history specialist for commits, rebases, blame and bisect.",
108
123
  ),
109
124
  (
110
125
  "lit-brainstorm",
@@ -112,7 +127,7 @@ PORTED_SKILLS = [
112
127
  ),
113
128
  (
114
129
  "lit-crucible",
115
- "Stress-test a plan with Hermes-native delegate_task lanes before handing off to /lit-plan.",
130
+ "Stress-test a plan with delegate_task lanes before handing off to /lit-plan.",
116
131
  ),
117
132
  (
118
133
  "lit-init",
@@ -120,11 +135,11 @@ PORTED_SKILLS = [
120
135
  ),
121
136
  (
122
137
  "lsp",
123
- "Use Hermes' native LSP diagnostics with LitHermes LSP guidance.",
138
+ "Use Hermes' native LSP diagnostics with LSP guidance.",
124
139
  ),
125
140
  (
126
141
  "lit-code",
127
- "Use LitHermes programming references for Python, TypeScript, Go, and Rust.",
142
+ "Use programming references for Python, TypeScript, Go and Rust.",
128
143
  ),
129
144
  (
130
145
  "refactor",
@@ -156,7 +171,7 @@ PORTED_SKILLS = [
156
171
  ),
157
172
  (
158
173
  "litwork",
159
- "Use when a defined code change must be carried to done: build, test, verify the real surface, report. Not for documents or decks.",
174
+ "Use when a defined code change must be carried to done: build, test, verify the real surface, report; not for documents or decks.",
160
175
  ),
161
176
  (
162
177
  "visual-qa",
@@ -172,7 +187,7 @@ PORTED_SKILLS = [
172
187
  ),
173
188
  (
174
189
  "litresearch",
175
- "Use when a question needs broad cited research into prior work or how others solve it, with disputed claims checked at the source.",
190
+ "Use when a question needs broad cited research into prior work or how others solve it; disputed claims checked at the source.",
176
191
  ),
177
192
  (
178
193
  "lit-recap",
@@ -458,7 +473,7 @@ def _setup_lithermes_cli(parser) -> None:
458
473
  "slash commands: /lit /lit-loop /lit-plan /litgoal /review-work /start-work /deep-interview "
459
474
  "/lit-humanizer (legacy aliases: /lit-korean /text-naturalization /text-neutralization "
460
475
  "/korean-ai-slop-remover) /lit-recap "
461
- "/lit-handoff /lit-scientific-visualization /lit-diagram-drawer /lit-pptx /lit-docx /lit-typographic-motion\n"
476
+ "/lit-handoff /lit-scientific-visualization /lit-diagram-drawer /lit-schematic /lit-pptx /lit-docx /lit-hijack /lit-adhd /lit-typographic-motion\n"
462
477
  f"skills: {len(PORTED_SKILLS)} lithermes:* skills — `hermes lithermes status` lists them\n"
463
478
  "contract schema: lithermes_llm_contract/v1 headings include #contract.activation, "
464
479
  "#contract.inputs, #contract.outputs, #contract.evidence, and #contract.hard_stops\n"
@@ -774,6 +789,12 @@ def register(ctx) -> None:
774
789
  description="Create and verify an accessible diagram from a supplied brief",
775
790
  args_hint="<diagram brief>",
776
791
  )
792
+ ctx.register_command(
793
+ "lit-schematic",
794
+ _command_handler(ctx, core.command_lit_schematic, "lit-schematic"),
795
+ description="Draw and QA-check a P&ID, PFD, apparatus figure or isometric cutaway from a supplied brief",
796
+ args_hint="<schematic brief>",
797
+ )
777
798
  for name in ("lit-pptx", "lit-docx"):
778
799
  ctx.register_command(
779
800
  name,
@@ -781,6 +802,24 @@ def register(ctx) -> None:
781
802
  description="Create and verify a PowerPoint deck" if name == "lit-pptx" else "Create and audit a Word document",
782
803
  args_hint="<document brief>",
783
804
  )
805
+ ctx.register_command(
806
+ "lit-hijack",
807
+ _command_handler(ctx, core.command_lit_hijack, "lit-hijack"),
808
+ description="Port published know-how into your own skill, clean-room",
809
+ args_hint="<urls or files> [into <target>]",
810
+ )
811
+ ctx.register_command(
812
+ "lit-adhd",
813
+ _command_handler(ctx, core.command_lit_adhd, "lit-adhd"),
814
+ description="Cross-session brief with gauges and one next step",
815
+ args_hint="[--here] [--since 7d] [--max 12]",
816
+ )
817
+ ctx.register_command(
818
+ "lit-rewind",
819
+ _command_handler(ctx, lambda raw: core.command_lit_adhd(raw, route="/lit-rewind"), "lit-adhd"),
820
+ description="Cross-session brief with gauges and one next step (alias of lit-adhd)",
821
+ args_hint="[--here] [--since 7d] [--max 12]",
822
+ )
784
823
  ctx.register_command(
785
824
  "lit-typographic-motion",
786
825
  _command_handler(ctx, core.command_motion_skill, "lit-typographic-motion"),
@@ -0,0 +1,272 @@
1
+ """Per-session discipline stack and `go` continuation for lit-adhd.
2
+
3
+ The prompt hook records each lit route activation as a frame in one guarded JSON file per
4
+ workspace (``.hermes/lithermes/adhd/stack.json``) so a later brief in another session can see
5
+ which disciplines are active. Hermes has a single chat lane per session and nothing can pause
6
+ a discipline, so a session holds at most one active frame: pushing a new skill finishes the
7
+ previous active frame, and there are no pause/resume transitions. lit-adhd and lit-recap are
8
+ read-only disciplines and never push a frame, so a brief in the middle of a lit-loop leaves
9
+ the loop exactly where it was.
10
+
11
+ A bare ``go`` (also ``lit go``, ``ㄱㄱ``, ``고``, ``진행``, as the whole prompt) continues the
12
+ step the last brief offered: the hook reads the next-step record the collector wrote for this
13
+ session (``.hermes/lithermes/adhd/next-<session>.json``) and returns a short context note for
14
+ the model. A plan saved under ``plans/`` during a lit-plan turn writes the same record
15
+ (``start-plan``), so ``go`` right after planning shows that plan's ``/start-work`` command. A record older than 30 minutes, or no record at all, reads as no next step on
16
+ record. Hermes has no typed ``go`` command; ``go`` is a word the hook consumes.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import os
22
+ import re
23
+ import tempfile
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ STACK_VERSION = 1
28
+ STACK_DIRNAME = "adhd"
29
+ STACK_FILENAME = "stack.json"
30
+ MAX_SESSIONS = 64
31
+ MAX_FRAMES_PER_SESSION = 20
32
+ MAX_STATE_BYTES = 2 * 1024 * 1024
33
+ GO_TTL_SECONDS = 30 * 60
34
+
35
+ # Read-only disciplines never push a stack frame.
36
+ READ_ONLY_DISCIPLINES = frozenset({"lit-adhd", "lit-recap"})
37
+
38
+ _GO_WORD = re.compile(r"^(?:go|lit go|ㄱㄱ|고|진행)$", re.IGNORECASE)
39
+ _SESSION_SAFE = re.compile(r"[^A-Za-z0-9_-]")
40
+
41
+
42
+ def match_go_word(text: str) -> bool:
43
+ """True when the whole prompt is a go word the hook should consume."""
44
+ return _GO_WORD.match(str(text or "").strip()) is not None
45
+
46
+
47
+ def adhd_dir(workspace: Path | str) -> Path:
48
+ return Path(workspace) / ".hermes" / "lithermes" / STACK_DIRNAME
49
+
50
+
51
+ def stack_path(workspace: Path | str) -> Path:
52
+ return adhd_dir(workspace) / STACK_FILENAME
53
+
54
+
55
+ def next_step_path(workspace: Path | str, session_id: Any) -> Path:
56
+ safe = _SESSION_SAFE.sub("_", str(session_id or "session"))[:128] or "session"
57
+ return adhd_dir(workspace) / f"next-{safe}.json"
58
+
59
+
60
+ def read_stack(workspace: Path | str) -> dict[str, Any]:
61
+ """The stack file, or {} when it is missing, oversized or malformed. Never raises."""
62
+ try:
63
+ path = stack_path(workspace)
64
+ if not path.is_file() or path.stat().st_size > MAX_STATE_BYTES:
65
+ return {}
66
+ data = json.loads(path.read_text(encoding="utf-8"))
67
+ except (OSError, ValueError):
68
+ return {}
69
+ return data if isinstance(data, dict) else {}
70
+
71
+
72
+ def _write_stack(workspace: Path | str, data: dict[str, Any]) -> bool:
73
+ """Whole-file write through a temporary file. False when the write failed."""
74
+ try:
75
+ directory = adhd_dir(workspace)
76
+ # Refuse to write through a planted link: neither the adhd directory
77
+ # nor its `.hermes` parent may be a symlink. Missing paths are fine.
78
+ workspace_path = Path(workspace)
79
+ for probe in (directory, workspace_path / ".hermes"):
80
+ try:
81
+ if probe.is_symlink():
82
+ return False
83
+ except OSError:
84
+ return False
85
+ directory.mkdir(parents=True, exist_ok=True)
86
+ fd, staging = tempfile.mkstemp(dir=str(directory), prefix="stack-", suffix=".tmp")
87
+ try:
88
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
89
+ json.dump({"version": STACK_VERSION, "sessions": data.get("sessions", {})}, handle)
90
+ handle.write("\n")
91
+ os.replace(staging, stack_path(workspace))
92
+ finally:
93
+ try:
94
+ os.unlink(staging)
95
+ except OSError:
96
+ pass
97
+ return True
98
+ except (OSError, ValueError):
99
+ return False
100
+
101
+
102
+ def _frames(data: dict[str, Any], session_id: str) -> list[dict[str, Any]]:
103
+ sessions = data.get("sessions")
104
+ if not isinstance(sessions, dict):
105
+ return []
106
+ record = sessions.get(session_id)
107
+ if not isinstance(record, dict):
108
+ return []
109
+ frames = record.get("frames")
110
+ return [frame for frame in frames if isinstance(frame, dict)] if isinstance(frames, list) else []
111
+
112
+
113
+ def push(workspace: Path | str, session_id: Any, skill: str, *, stage: str = "", at: str = "") -> bool:
114
+ """Record an activation: the previous active frame of this session finishes. Never raises."""
115
+ key = str(session_id or "")
116
+ skill = str(skill or "")
117
+ if not key or not skill or skill in READ_ONLY_DISCIPLINES:
118
+ return False
119
+ try:
120
+ data = read_stack(workspace)
121
+ sessions = data.get("sessions")
122
+ if not isinstance(sessions, dict):
123
+ sessions = {}
124
+ data["sessions"] = sessions
125
+ frames = _frames(data, key)
126
+ for frame in frames:
127
+ if frame.get("status") == "active":
128
+ frame["status"] = "finished"
129
+ frames.append({"skill": skill, "stage": stage, "status": "active", "at": at})
130
+ sessions[key] = {"frames": frames[-MAX_FRAMES_PER_SESSION:]}
131
+ while len(sessions) > MAX_SESSIONS:
132
+ oldest = next(iter(sessions))
133
+ if oldest == key:
134
+ break
135
+ del sessions[oldest]
136
+ return _write_stack(workspace, data)
137
+ except (OSError, ValueError):
138
+ return False
139
+
140
+
141
+ def finish(workspace: Path | str, session_id: Any, skill: str = "") -> bool:
142
+ """Mark this session's latest active frame finished. Never raises."""
143
+ key = str(session_id or "")
144
+ if not key:
145
+ return False
146
+ try:
147
+ data = read_stack(workspace)
148
+ sessions = data.get("sessions")
149
+ if not isinstance(sessions, dict) or key not in sessions:
150
+ return False
151
+ frames = _frames(data, key)
152
+ for frame in reversed(frames):
153
+ if frame.get("status") == "active" and (not skill or frame.get("skill") == skill):
154
+ frame["status"] = "finished"
155
+ break
156
+ sessions[key] = {"frames": frames[-MAX_FRAMES_PER_SESSION:]}
157
+ return _write_stack(workspace, data)
158
+ except (OSError, ValueError):
159
+ return False
160
+
161
+
162
+ def active_skill(workspace: Path | str, session_id: Any) -> str:
163
+ """The skill of this session's latest active frame, or "" when none is active."""
164
+ for frame in reversed(_frames(read_stack(workspace), str(session_id or ""))):
165
+ if frame.get("status") == "active":
166
+ return str(frame.get("skill") or "")
167
+ return ""
168
+
169
+
170
+ def write_next_step(
171
+ workspace: Path | str, session_id: Any, kind: str, target: dict[str, Any], *, now: float | None = None
172
+ ) -> bool:
173
+ """Write this session's `{kind, target, at}` record, the shape the collector writes.
174
+
175
+ Refuses a symlinked adhd or ``.hermes`` directory and stages through an exclusive,
176
+ no-follow, pid-unique file, so a planted link is never written through. Never raises.
177
+ """
178
+ import time as _time
179
+ from datetime import datetime, timezone
180
+
181
+ try:
182
+ directory = adhd_dir(workspace)
183
+ for probe in (directory, Path(workspace) / ".hermes"):
184
+ if probe.is_symlink():
185
+ return False
186
+ directory.mkdir(parents=True, exist_ok=True)
187
+ at = datetime.fromtimestamp(now if now is not None else _time.time(), tz=timezone.utc)
188
+ payload = {"kind": kind, "target": target, "at": at.isoformat(timespec="milliseconds").replace("+00:00", "Z")}
189
+ path = next_step_path(workspace, session_id)
190
+ staging = path.with_name(f"{path.name}.{os.getpid()}.tmp")
191
+ flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY | getattr(os, "O_NOFOLLOW", 0)
192
+ fd = os.open(staging, flags, 0o600)
193
+ try:
194
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
195
+ handle.write(json.dumps(payload) + "\n")
196
+ os.replace(staging, path)
197
+ except BaseException:
198
+ staging.unlink(missing_ok=True)
199
+ raise
200
+ return True
201
+ except (OSError, ValueError, TypeError):
202
+ return False
203
+
204
+
205
+ def read_next_step(workspace: Path | str, session_id: Any) -> dict[str, Any] | None:
206
+ """This session's next-step record, or None when it is missing or malformed. Never raises."""
207
+ try:
208
+ path = next_step_path(workspace, session_id)
209
+ if not path.is_file() or path.stat().st_size > MAX_STATE_BYTES:
210
+ return None
211
+ data = json.loads(path.read_text(encoding="utf-8"))
212
+ except (OSError, ValueError):
213
+ return None
214
+ return data if isinstance(data, dict) and isinstance(data.get("kind"), str) else None
215
+
216
+
217
+ def go_note(workspace: Path | str, session_id: Any, *, now: float | None = None) -> str:
218
+ """The context note a consumed `go` injects: what to do next, or that nothing is on record."""
219
+ import time as _time
220
+ from datetime import datetime, timezone
221
+
222
+ def _stamp(raw: str) -> float:
223
+ parsed = datetime.fromisoformat(raw.replace("Z", "+00:00"))
224
+ if parsed.tzinfo is None:
225
+ parsed = parsed.replace(tzinfo=timezone.utc)
226
+ return parsed.timestamp()
227
+
228
+ record = read_next_step(workspace, session_id)
229
+ if record is None:
230
+ return (
231
+ "The user typed `go` with no brief on record: no next-step record exists for this "
232
+ "session. Offer `lit adhd` for a cross-session brief instead of inventing a step."
233
+ )
234
+ try:
235
+ at = _stamp(str(record.get("at", "")))
236
+ now_s = now if now is not None else _time.time()
237
+ if now_s - at > GO_TTL_SECONDS:
238
+ raise ValueError("expired")
239
+ except (ValueError, OverflowError, TypeError):
240
+ return (
241
+ "The user typed `go` but the brief's next-step record is older than 30 minutes: it "
242
+ "has expired. Offer `lit adhd` for a fresh brief instead of continuing the old step."
243
+ )
244
+ kind = str(record.get("kind", "none")).replace("-", " ")
245
+ target = record.get("target") or {}
246
+ if not isinstance(target, dict):
247
+ # A hand-edited or foreign record may store a bare string here; treat
248
+ # it as an empty detail rather than raising on `.get`.
249
+ target = {}
250
+ detail = target.get("plan") or target.get("workId") or target.get("skill") or ""
251
+ if record.get("kind") == "start-plan" and detail:
252
+ return (
253
+ f"The user typed `go` after a brief whose next step is: start plan {detail} with "
254
+ f"`/start-work {detail}`. Present that command for the user to run; do not run it, "
255
+ "and do not start anything else."
256
+ )
257
+ if record.get("kind") == "approve-boundary":
258
+ return (
259
+ "YOUR TURN\n"
260
+ "The user typed `go` after a brief whose next step is a bounded-work boundary "
261
+ f"({detail}): show what it wants and ask the user to approve or deny it in chat. "
262
+ "Never approve it yourself."
263
+ )
264
+ if kind != "none":
265
+ return (
266
+ f"The user typed `go` after a brief whose next step is: {kind}"
267
+ f"{f' ({detail})' if detail else ''}. Continue that step in chat; start nothing else."
268
+ )
269
+ return (
270
+ "The user typed `go` but the brief recorded no next step: say there is none instead "
271
+ "of inventing one."
272
+ )
@@ -801,6 +801,45 @@ def _terminal_requirement(args: dict[str, Any]) -> tuple[str, list[Path]] | None
801
801
  return action, [_canonical_tool_path(args.get("workdir") or Path.cwd())]
802
802
 
803
803
 
804
+ _OBSERVER_HERDR_READS = {
805
+ "pane": {"list", "read", "get", "layout"},
806
+ "agent": {"list", "get", "read"},
807
+ "tab": {"list"},
808
+ "workspace": {"list"},
809
+ }
810
+ _OBSERVER_FILTERS = {"grep", "rg", "head", "tail", "wc"}
811
+
812
+
813
+ def classify_observation(command: Any) -> bool:
814
+ """True when a terminal command only reads: a herdr read or a grep/rg pipeline.
815
+
816
+ Pipes are the only shell control admitted; redirects, chaining, command
817
+ substitution and newlines still refuse, as in ``_terminal_requirement``.
818
+ """
819
+ text = str(command or "").strip()
820
+ if not text or _SHELL_CONTROL_RE.search(text.replace("|", "")):
821
+ return False
822
+ segments = text.split("|")
823
+ for index, segment in enumerate(segments):
824
+ try:
825
+ words = shlex.split(segment)
826
+ except ValueError:
827
+ return False
828
+ if not words:
829
+ return False
830
+ executable = Path(words[0]).name
831
+ if executable == "rg" and any(word.startswith("--pre") for word in words):
832
+ return False
833
+ if index == 0 and executable == "herdr":
834
+ if len(words) < 3 or words[2] not in _OBSERVER_HERDR_READS.get(words[1], set()):
835
+ return False
836
+ elif index == 0 and executable not in {"grep", "rg"}:
837
+ return False
838
+ elif index > 0 and executable not in _OBSERVER_FILTERS:
839
+ return False
840
+ return True
841
+
842
+
804
843
  def _tool_requirement(tool_name: str, args: dict[str, Any]) -> tuple[str, list[Path]] | None:
805
844
  if tool_name in _READ_ONLY_TOOLS:
806
845
  return None
@@ -866,6 +905,8 @@ def enforce_tool_authority(tool_name: Any, args: Any, session_id: Any) -> dict[s
866
905
  exact_session = str(session_id or "")
867
906
  bound = [state for state in states if state.get("active_session_id") == exact_session]
868
907
  if not exact_session or not bound:
908
+ if name == "terminal" and classify_observation(payload.get("command")):
909
+ return None
869
910
  return _block_tool("Bounded work mutation blocked: the exact Hermes session is not bound.")
870
911
  if any(state.get("status") == "paused" for state in bound):
871
912
  return _block_tool("Bounded work mutation blocked: this Hermes session has paused work.")