@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.
- package/README.md +12 -9
- package/README_Ko-KR.md +12 -9
- package/assets/lithermes-plugin/README.md +15 -2
- package/assets/lithermes-plugin/__init__.py +49 -10
- package/assets/lithermes-plugin/adhd_stack.py +272 -0
- package/assets/lithermes-plugin/bounded_work.py +41 -0
- package/assets/lithermes-plugin/core.py +53 -4
- package/assets/lithermes-plugin/core_commands.py +99 -0
- package/assets/lithermes-plugin/core_contexts.py +54 -4
- package/assets/lithermes-plugin/core_contract.py +2 -2
- package/assets/lithermes-plugin/core_plans.py +80 -0
- package/assets/lithermes-plugin/core_routing.py +126 -0
- package/assets/lithermes-plugin/core_workflow_commands.py +11 -5
- package/assets/lithermes-plugin/diagnostics.py +3 -2
- package/assets/lithermes-plugin/lit_mark.py +75 -0
- package/assets/lithermes-plugin/payload-version.json +473 -61
- package/assets/lithermes-plugin/plugin.yaml +1 -1
- package/assets/lithermes-plugin/scientific_visualization.py +23 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/SKILL.md +7 -7
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/components.css +414 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/fonts.json +42 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/kit/ui.js +226 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/README.md +2 -4
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +4 -4
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/craft-floor.md +10 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe-rules.json +6 -1
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe.mjs +76 -4
- package/assets/lithermes-plugin/skills/lit-adhd/SKILL.md +202 -0
- package/assets/lithermes-plugin/skills/lit-adhd/references/brief-contract.md +78 -0
- package/assets/lithermes-plugin/skills/lit-adhd/references/card-examples.md +97 -0
- package/assets/lithermes-plugin/skills/lit-adhd/references/status-taxonomy.md +56 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/adhd.mjs +11 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/collect.mjs +650 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/gauge.mjs +89 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/hermes-sessions.mjs +219 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/next-step.mjs +101 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/plan-task-rows.mjs +32 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/redact.mjs +42 -0
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/secret-shapes.mjs +59 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/SKILL.md +7 -4
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/style-guide.md +20 -11
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-architecture.md +3 -3
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dependency.md +1 -1
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-tree.md +3 -3
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-wardley.md +1 -1
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/verifier-guide.md +5 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_quality.py +4 -4
- package/assets/lithermes-plugin/skills/lit-docx/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/lit-hijack/SKILL.md +239 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/capture-and-provenance.md +123 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/clean-room-build.md +128 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/distil-and-licence.md +128 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/gap-and-port-design.md +152 -0
- package/assets/lithermes-plugin/skills/lit-hijack/references/parity-and-report.md +131 -0
- package/assets/lithermes-plugin/skills/lit-hijack/scripts/hijack.mjs +995 -0
- package/assets/lithermes-plugin/skills/lit-plan/SKILL.md +34 -1
- package/assets/lithermes-plugin/skills/lit-pptx/SKILL.md +11 -6
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/provenance.json +1 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/anti-slop-checklist.md +3 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/authoring-guide.md +1 -1
- package/assets/lithermes-plugin/skills/lit-pptx/references/consensus-protocol.md +1 -1
- package/assets/lithermes-plugin/skills/lit-pptx/references/direction-step.md +8 -2
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +175 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +156 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +141 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +160 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +183 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/layout-families.md +1 -1
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/bauhaus.md +105 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/computational-brutalism.md +111 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/neo-brutalism.md +114 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/newsroom.md +106 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/tonalities/riso.md +101 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/craft_extras.py +49 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/deck_output.py +4 -2
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/render-pack.js +96 -10
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/template-registry.js +50 -6
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +103 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
- package/assets/lithermes-plugin/skills/lit-recap/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/lit-schematic/SKILL.md +144 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +38 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +133 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +87 -0
- package/assets/lithermes-plugin/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +85 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/cutaway-outputs.md +70 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/isometric-craft.md +101 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/journal-figures.md +72 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/kit-api.md +134 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/pfd-and-layout.md +53 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/pid-drafting.md +103 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/pid-symbols.md +163 -0
- package/assets/lithermes-plugin/skills/lit-schematic/references/verification.md +100 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/doctor.mjs +57 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/figure.mjs +81 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/fonts.mjs +27 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/geom.mjs +113 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/interactive.mjs +292 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/isa.mjs +115 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/iso.mjs +719 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/legend-sheet.mjs +162 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/pid.mjs +937 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/png.mjs +132 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/qa-core.mjs +658 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/route.mjs +218 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/sheet.mjs +890 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/svg.mjs +82 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/lib/theme.mjs +115 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/motion.mjs +129 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/qa.mjs +92 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/render.mjs +233 -0
- package/assets/lithermes-plugin/skills/lit-schematic/scripts/scaffold.mjs +60 -0
- package/assets/lithermes-plugin/skills/lit-scientific-visualization/SKILL.md +19 -1
- package/assets/lithermes-plugin/skills/lit-typographic-motion/SKILL.md +12 -11
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/motion.mjs +56 -2
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/runtime.mjs +129 -4
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +9 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/blender/runner.py +218 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/blender.mjs +154 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/board.mjs +132 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/completion.mjs +48 -5
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/gate.mjs +13 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/house.mjs +44 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/scaffold.mjs +238 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/score.mjs +643 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound-dsp.mjs +21 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound.mjs +171 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-qa.mjs +23 -3
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage.mjs +124 -17
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/styles.mjs +240 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/treatment.mjs +152 -10
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/hero-3d/expected.json +67 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/hero-3d/index.html +139 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/depth.md +74 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/studio.md +87 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/style-bibles.md +9 -2
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/treatment.md +24 -8
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/paper.json +41 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/swiss-signal.json +41 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/terminalcore.json +42 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/styles/tidal.json +41 -0
- package/assets/lithermes-plugin/skills/litresearch/SKILL.md +4 -0
- package/assets/lithermes-plugin/skills/readme-studio/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/review-work/SKILL.md +2 -0
- package/assets/lithermes-plugin/skills/start-work/SKILL.md +2 -1
- package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +3 -3
- package/package.json +1 -1
- package/readme-assets/badge-version.svg +1 -1
- 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.
|
|
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.
|
|
5
|
-
<a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.
|
|
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.
|
|
10
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.
|
|
11
|
-
<a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.
|
|
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>
|
|
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>
|
|
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.
|
|
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-
|
|
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
|
|
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.
|
|
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.
|
|
5
|
-
<a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.
|
|
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.
|
|
10
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.
|
|
11
|
-
<a href="https://github.com/wjgoarxiv/lithermes/blob/main/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/lithermes@1.0.
|
|
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>
|
|
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>
|
|
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.
|
|
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-
|
|
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`처럼 씁니다. 스킬
|
|
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`,
|
|
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
|
|
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
|
|
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
|
-
(
|
|
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
|
|
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
|
|
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
|
|
138
|
+
"Use Hermes' native LSP diagnostics with LSP guidance.",
|
|
124
139
|
),
|
|
125
140
|
(
|
|
126
141
|
"lit-code",
|
|
127
|
-
"Use
|
|
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
|
|
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
|
|
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.")
|