@litfamily/lithermes 1.0.26 → 1.0.28
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 +10 -9
- package/README_Ko-KR.md +10 -9
- package/assets/lithermes-plugin/README.md +28 -9
- package/assets/lithermes-plugin/__init__.py +8 -3
- package/assets/lithermes-plugin/auto_update.py +17 -3
- package/assets/lithermes-plugin/core.py +41 -2
- package/assets/lithermes-plugin/core_contexts.py +27 -9
- package/assets/lithermes-plugin/core_contract.py +7 -2
- package/assets/lithermes-plugin/core_review.py +144 -3
- package/assets/lithermes-plugin/core_routing.py +10 -0
- package/assets/lithermes-plugin/core_workflow_commands.py +3 -2
- package/assets/lithermes-plugin/deliverable_hedge_guard.py +171 -48
- package/assets/lithermes-plugin/diagnostics.py +5 -3
- package/assets/lithermes-plugin/humanizer_detector.py +117 -5
- package/assets/lithermes-plugin/lit_reentry.py +233 -0
- package/assets/lithermes-plugin/payload-version.json +184 -52
- package/assets/lithermes-plugin/plugin.yaml +1 -1
- package/assets/lithermes-plugin/rules/__init__.py +2 -0
- package/assets/lithermes-plugin/rules/bundled-rules/lit-humanizer.md +2 -0
- package/assets/lithermes-plugin/rules/engine.py +26 -11
- package/assets/lithermes-plugin/skills/deep-interview/SKILL.md +2 -1
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +3 -3
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/hostexec.mjs +118 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe.mjs +6 -5
- package/assets/lithermes-plugin/skills/lit-brainstorm/SKILL.md +408 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/doctor.mjs +3 -3
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/export.mjs +5 -5
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/hostexec.mjs +118 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/convert_md_to_pdf.py +38 -4
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/acs/template.tex +3 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/elsevier/template.tex +3 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/ieee/template.tex +3 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/korean-generic/template.tex +3 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/nature/template.tex +3 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/SKILL.md +2 -0
- package/assets/lithermes-plugin/skills/lit-pptx/bin/hostexec.mjs +118 -0
- package/assets/lithermes-plugin/skills/lit-pptx/bin/office.mjs +16 -9
- package/assets/lithermes-plugin/skills/lit-typographic-motion/SKILL.md +13 -12
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/motion.mjs +19 -6
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/runtime.mjs +25 -19
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +2 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/browser.mjs +11 -4
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/constants.mjs +29 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-assets.mjs +87 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-capture.mjs +189 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-gate.mjs +263 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-image.mjs +103 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-init.js +36 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-png.mjs +77 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-sheet.mjs +70 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-treatment.mjs +104 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-webp.mjs +112 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover-worker.mjs +19 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/cover.mjs +338 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/encode.mjs +1 -1
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/hostexec.mjs +118 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/look.mjs +103 -7
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/loop-kit.js +80 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound.mjs +1 -1
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage.mjs +8 -2
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/treatment.mjs +1 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/card-scenes/expected.json +49 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/card-scenes/index.html +37 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/reset-sweep/expected.json +49 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/reset-sweep/index.html +27 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/still-glow/expected.json +51 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/still-glow/index.html +29 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/title-slam/expected.json +51 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/negatives/title-slam/index.html +37 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/orbit-loop/expected.json +68 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/orbit-loop/index.html +112 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/painted-light/expected.json +96 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/painted-light/index.html +208 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/weight-tide/expected.json +68 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/recipes/weight-tide/index.html +81 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/cover.md +458 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/craft-loop.md +4 -1
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/runtime.md +13 -5
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/stage.md +4 -2
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/treatment.md +6 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/fonts.json +11 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/requirements.lock +72 -0
- package/assets/lithermes-plugin/skills/litgoal/SKILL.md +19 -1
- package/assets/lithermes-plugin/skills/lsp-setup/scripts/verify-lsp.ts +16 -3
- package/assets/lithermes-plugin/skills/review-work/SKILL.md +38 -16
- package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +3 -3
- package/package.json +2 -1
- package/readme-assets/badge-version.svg +1 -1
- package/readme-assets/cover-motion-still.webp +0 -0
- package/readme-assets/cover-motion.webp +0 -0
- package/src/cli.js +2 -2
- package/src/lib/check.js +9 -6
- package/src/lib/hermesDiscovery.js +2 -2
- package/src/lib/hostExec.js +81 -0
- package/src/lib/skillPayload.js +1 -1
- package/src/lib/updateNotifier.js +14 -4
|
@@ -26,7 +26,7 @@ Hermes session runs npm, pip or a download. Run the pre-warm in a normal shell,
|
|
|
26
26
|
outside any sandboxed session, with the same `HOME`, `HERMES_HOME` and
|
|
27
27
|
`XDG_CACHE_HOME` the session will use.
|
|
28
28
|
|
|
29
|
-
## Status (
|
|
29
|
+
## Status (six probes)
|
|
30
30
|
|
|
31
31
|
`lithermes motion-runtime status` and `lithermes doctor` report, every time:
|
|
32
32
|
|
|
@@ -41,6 +41,9 @@ outside any sandboxed session, with the same `HOME`, `HERMES_HOME` and
|
|
|
41
41
|
5. **Pre-warm**: READY with the cache path, or exactly which dependencies and
|
|
42
42
|
fonts are missing or hash-mismatched and the command that fixes them; plus
|
|
43
43
|
the state of the optional audio venv.
|
|
44
|
+
6. **Cover encoder**: img2webp and dwebp (libwebp) with their version, the
|
|
45
|
+
gifski fallback, or not found (cover renders then stop with exit 21). These
|
|
46
|
+
are host tools, never bundled or installed by this product.
|
|
44
47
|
|
|
45
48
|
## Chrome launch
|
|
46
49
|
|
|
@@ -71,6 +74,8 @@ the render with exit 10 and that line. The engine never adds `--no-sandbox`.
|
|
|
71
74
|
| 13 | GATE_FAIL_QA | the gate or the pre-flight failed; the report names the rules; fix and rerun the next round |
|
|
72
75
|
| 14 | BLOCKED_DEPS_NOT_PREWARMED | the cache is not warm (or word-timing models are absent): run `lithermes motion-runtime install` outside this session |
|
|
73
76
|
| 15 | BLOCKED_FONT_FETCH | a pinned font or licence is missing or its sha256 differs: rerun `lithermes motion-runtime install`; never accept or re-fetch it inside a session |
|
|
77
|
+
| 21 | BLOCKED_NO_COVER_ENCODER | cover mode found neither img2webp with dwebp nor gifski; install libwebp (or gifski) on the host |
|
|
78
|
+
| 22 | BLOCKED_ASSET_MISSING | cover mode: an asset listed in `assets.json` is missing; supply or generate it, never a stand-in |
|
|
74
79
|
| 2 | usage | a missing or invalid brief or flag |
|
|
75
80
|
|
|
76
81
|
Pre-flight order: brief, word-timing request, dependencies (14), fonts (15),
|
|
@@ -82,10 +87,13 @@ glyph coverage and timeline floors (13, nothing rendered), ffmpeg for `video`
|
|
|
82
87
|
`lithermes motion-runtime install --audio` creates a Python venv in the cache
|
|
83
88
|
from `runtime/requirements.lock` with
|
|
84
89
|
`pip install --require-hashes --only-binary=:all:` (librosa and its pinned
|
|
85
|
-
dependencies; Python 3.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
dependencies; the newest Python from 3.14 down to 3.11 on PATH, picked the way the
|
|
91
|
+
Office runtime picks, so a python3 that is 3.15 is passed over; the lock pins
|
|
92
|
+
numpy 2.4, which starts at 3.11, and carries audioread's extra dependencies
|
|
93
|
+
for 3.13 and newer; set `LITHERMES_MOTION_PYTHON` to choose
|
|
94
|
+
the interpreter). When a brief names an
|
|
95
|
+
`audio` file and the venv is ready and still matches its pins, `bin/audio.py`
|
|
96
|
+
analyses that file once into `<out>/audio-grid.json` and the beats replace the fixed grid. If the venv is
|
|
89
97
|
missing, drifted, or the analysis fails, the cuts use text timing and the
|
|
90
98
|
manifest and report carry the warning. Either way the audio itself is always
|
|
91
99
|
muxed into the MP4 (AAC 256k), padded or trimmed to the film with a 50 ms
|
|
@@ -69,8 +69,10 @@ or prefetch link, anywhere in the stage files.
|
|
|
69
69
|
`font-display: block`, served from the pre-warmed cache and the reused lit-pptx
|
|
70
70
|
Pretendard pair: `Pretendard` (400, 700), `Archivo` (500, 700, 900 at
|
|
71
71
|
`font-stretch` 75 %, 100 %, 125 %), `Galmuri9`, `VT323`, `Silkscreen` (400,
|
|
72
|
-
700) and `MesloLGS NF
|
|
73
|
-
to a
|
|
72
|
+
700) and `MesloLGS NF`, plus `JetBrains Mono` (a variable `wght` axis from 100
|
|
73
|
+
to 800 with a fixed advance, pinned in the same pre-warmed cache, so weight
|
|
74
|
+
motion never reflows a line). Use Pretendard for Hangul. Any other family falls
|
|
75
|
+
back to a system face; the QA reports it and fails it on copy.
|
|
74
76
|
|
|
75
77
|
### Text the QA can read
|
|
76
78
|
|
|
@@ -10,6 +10,12 @@ where, how it is shaped over time, what is drawn on screen, how it sounds, and
|
|
|
10
10
|
what would make it excellent. Write it from the request, not from the example
|
|
11
11
|
below.
|
|
12
12
|
|
|
13
|
+
A README, hero, banner or cover loop is not a film. Its treatment sets
|
|
14
|
+
`"mode": "cover"` and follows `cover.md` instead of this file: no beats, no arc,
|
|
15
|
+
no sound, no copy lines, and none of the film rules below (the 1.2 s beat, the
|
|
16
|
+
10 s floor, the 16:9 and 9:16 formats, the genre arcs). `stage` and `run` refuse
|
|
17
|
+
a cover treatment, and `cover` refuses a film treatment, each with exit 16.
|
|
18
|
+
|
|
13
19
|
## Load order
|
|
14
20
|
|
|
15
21
|
1. This file, always first.
|
|
@@ -115,6 +115,12 @@
|
|
|
115
115
|
"sha256": "7b5a420d4e16285213e7e2b1d187d086835c2dd7a7f64c5b8057434f082fc46f",
|
|
116
116
|
"license": "licenses/EMS-OFL.txt"
|
|
117
117
|
},
|
|
118
|
+
{
|
|
119
|
+
"path": "fonts/JetBrainsMono-wght-latin.woff2",
|
|
120
|
+
"url": "https://cdn.jsdelivr.net/npm/@fontsource-variable/jetbrains-mono@5.3.0/files/jetbrains-mono-latin-wght-normal.woff2",
|
|
121
|
+
"sha256": "18be452724bfdc236c074ca94a249a7f41a86752c7d04ab258ce9ed5651f6a7e",
|
|
122
|
+
"license": "licenses/JetBrainsMono-OFL.txt"
|
|
123
|
+
},
|
|
118
124
|
{
|
|
119
125
|
"path": "licenses/Galmuri-OFL.md",
|
|
120
126
|
"url": "https://raw.githubusercontent.com/quiple/galmuri/v2.40.4/ofl.md",
|
|
@@ -149,6 +155,11 @@
|
|
|
149
155
|
"path": "licenses/Archivo-OFL.txt",
|
|
150
156
|
"url": "https://raw.githubusercontent.com/mexicat/pdoom-video/ca251e3dddda422b364385eb484b5a3593a0990d/app/public/fonts/src/OFL.txt",
|
|
151
157
|
"sha256": "108b4e57c9c796d3d38d0428ca7ee39de47ad93187302718d9b2d8864b9b716b"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"path": "licenses/JetBrainsMono-OFL.txt",
|
|
161
|
+
"url": "https://cdn.jsdelivr.net/npm/@fontsource-variable/jetbrains-mono@5.3.0/LICENSE",
|
|
162
|
+
"sha256": "403581b69dac5cff4079205e01c6b467e56af449ecbd7247693ddb1baafa005b"
|
|
152
163
|
}
|
|
153
164
|
]
|
|
154
165
|
}
|
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# This file was autogenerated by uv via the following command:
|
|
2
2
|
# uv pip compile --python-version 3.11 --generate-hashes --only-binary :all: packages/lithermes-installer/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/requirements.in --output-file packages/lithermes-installer/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/requirements.lock
|
|
3
|
+
# Hand-merged after the uv header above: audioop-lts, standard-aifc, standard-chunk and standard-sunau, with their hashes and
|
|
4
|
+
# the python_full_version >= '3.13' marker, taken from `uv pip compile --universal --python-version 3.11` of requirements.in.
|
|
5
|
+
# audioread needs them from Python 3.13; every other pin is the original 3.11 resolution.
|
|
6
|
+
audioop-lts==0.2.2 ; python_full_version >= '3.13' \
|
|
7
|
+
--hash=sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a \
|
|
8
|
+
--hash=sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449 \
|
|
9
|
+
--hash=sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3 \
|
|
10
|
+
--hash=sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0 \
|
|
11
|
+
--hash=sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7 \
|
|
12
|
+
--hash=sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58 \
|
|
13
|
+
--hash=sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911 \
|
|
14
|
+
--hash=sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7 \
|
|
15
|
+
--hash=sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e \
|
|
16
|
+
--hash=sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd \
|
|
17
|
+
--hash=sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636 \
|
|
18
|
+
--hash=sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623 \
|
|
19
|
+
--hash=sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b \
|
|
20
|
+
--hash=sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5 \
|
|
21
|
+
--hash=sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a \
|
|
22
|
+
--hash=sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a \
|
|
23
|
+
--hash=sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6 \
|
|
24
|
+
--hash=sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d \
|
|
25
|
+
--hash=sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303 \
|
|
26
|
+
--hash=sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602 \
|
|
27
|
+
--hash=sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b \
|
|
28
|
+
--hash=sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f \
|
|
29
|
+
--hash=sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0 \
|
|
30
|
+
--hash=sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09 \
|
|
31
|
+
--hash=sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132 \
|
|
32
|
+
--hash=sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753 \
|
|
33
|
+
--hash=sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9 \
|
|
34
|
+
--hash=sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547 \
|
|
35
|
+
--hash=sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c \
|
|
36
|
+
--hash=sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75 \
|
|
37
|
+
--hash=sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e \
|
|
38
|
+
--hash=sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093 \
|
|
39
|
+
--hash=sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b \
|
|
40
|
+
--hash=sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6 \
|
|
41
|
+
--hash=sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd \
|
|
42
|
+
--hash=sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8 \
|
|
43
|
+
--hash=sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb \
|
|
44
|
+
--hash=sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969 \
|
|
45
|
+
--hash=sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7 \
|
|
46
|
+
--hash=sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe \
|
|
47
|
+
--hash=sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917 \
|
|
48
|
+
--hash=sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc \
|
|
49
|
+
--hash=sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9 \
|
|
50
|
+
--hash=sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3 \
|
|
51
|
+
--hash=sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5 \
|
|
52
|
+
--hash=sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf \
|
|
53
|
+
--hash=sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6 \
|
|
54
|
+
--hash=sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19 \
|
|
55
|
+
--hash=sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800
|
|
56
|
+
# via
|
|
57
|
+
# standard-aifc
|
|
58
|
+
# standard-sunau
|
|
3
59
|
audioread==3.1.0 \
|
|
4
60
|
--hash=sha256:1c4ab2f2972764c896a8ac61ac53e261c8d29f0c6ccd652f84e18f08a4cab190 \
|
|
5
61
|
--hash=sha256:b30d1df6c5d3de5dcef0fb0e256f6ea17bdcf5f979408df0297d8a408e2971b4
|
|
@@ -711,6 +767,22 @@ soxr==1.1.0 \
|
|
|
711
767
|
--hash=sha256:f4977323ef9c3aa3c2a26ff5fe0191c84b8fd759daf7afb1f25a91a55ad8b730 \
|
|
712
768
|
--hash=sha256:feebcba99ac99adb8009d46c8f4c1956b8c167576b0ae8a6fb47502e9a6f78e7
|
|
713
769
|
# via librosa
|
|
770
|
+
standard-aifc==3.13.0 ; python_full_version >= '3.13' \
|
|
771
|
+
--hash=sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43 \
|
|
772
|
+
--hash=sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66
|
|
773
|
+
# via
|
|
774
|
+
# audioread
|
|
775
|
+
# librosa
|
|
776
|
+
standard-chunk==3.13.0 ; python_full_version >= '3.13' \
|
|
777
|
+
--hash=sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c \
|
|
778
|
+
--hash=sha256:4ac345d37d7e686d2755e01836b8d98eda0d1a3ee90375e597ae43aaf064d654
|
|
779
|
+
# via standard-aifc
|
|
780
|
+
standard-sunau==3.13.0 ; python_full_version >= '3.13' \
|
|
781
|
+
--hash=sha256:53af624a9529c41062f4c2fd33837f297f3baa196b0cfceffea6555654602622 \
|
|
782
|
+
--hash=sha256:b319a1ac95a09a2378a8442f403c66f4fd4b36616d6df6ae82b8e536ee790908
|
|
783
|
+
# via
|
|
784
|
+
# audioread
|
|
785
|
+
# librosa
|
|
714
786
|
threadpoolctl==3.7.0 \
|
|
715
787
|
--hash=sha256:61348cfb77d53b9242e0017029244b559b810c142ced65b4e21eeca1843959a7 \
|
|
716
788
|
--hash=sha256:cd8b60b5641b45c67bbf73c64c843235fc2d8a480c87389f52f5dbee893b86be
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: litgoal
|
|
3
|
-
description:
|
|
3
|
+
description: Sets and keeps the finish line: a durable repo-native goal runtime with embedded success criteria, evidence ledger, checkpoints, steering, and an evidence-gated completion contract that lit-loop and start-work run toward.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## #contract.activation
|
|
@@ -115,6 +115,24 @@ limitations_channel: inline
|
|
|
115
115
|
| broaden scope while editing | keep changes tied to the user request and preserve unrelated worktree state |
|
|
116
116
|
| skip payload sync | run `sync-plugin -- --in-place` and report the hash-manifest change |
|
|
117
117
|
|
|
118
|
+
## What litgoal is for
|
|
119
|
+
|
|
120
|
+
litgoal sets and keeps the finish line; lit-loop and start-work run toward it.
|
|
121
|
+
|
|
122
|
+
| Question | Owner |
|
|
123
|
+
|---|---|
|
|
124
|
+
| What counts as done, and what proves it? | `litgoal`: objective, success criteria, evidence, checkpoints, the completion gate |
|
|
125
|
+
| Do the work toward that finish line in one go | `lit-loop` (bare `lit <task>` or `/lit-loop`) |
|
|
126
|
+
| Execute an approved plan row by row toward it | `start-work` (`/start-work <approved-plan>`) |
|
|
127
|
+
| Is the plan, or the finished work, any good? | `review-work` |
|
|
128
|
+
|
|
129
|
+
litgoal never does the work itself. It records the finish line, keeps it across
|
|
130
|
+
turns and sessions, injects the unmet criteria into every turn, and refuses
|
|
131
|
+
`goal_complete` until the evidence is there. The route name is the skill name:
|
|
132
|
+
`litgoal <objective>`, `lit goal <objective>`, `/litgoal`, and
|
|
133
|
+
`lithermes:litgoal` all open this skill. Hermes' own native `/goal` stays
|
|
134
|
+
user-managed and unobserved; litgoal never mutates it.
|
|
135
|
+
|
|
118
136
|
## LitHermes durable runtime (authoritative — overrides any legacy examples below)
|
|
119
137
|
|
|
120
138
|
LitHermes ships litgoal as a **real durable runtime**, not prose. State persists under
|
|
@@ -118,6 +118,19 @@ function makeReader(onMessage: (message: JsonRpcMessage) => void): (chunk: Buffe
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// Windows installs npm language servers as .cmd shims, which Node refuses to spawn without a
|
|
122
|
+
// shell (EINVAL). A shell line is built only from the configured command, each part quoted;
|
|
123
|
+
// a quote, percent sign or line break in it is refused rather than escaped.
|
|
124
|
+
function launch(bin: string, args: string[]) {
|
|
125
|
+
const stdio: ["pipe", "pipe", "pipe"] = ["pipe", "pipe", "pipe"]
|
|
126
|
+
if (process.platform !== "win32") return spawn(bin, args, { stdio })
|
|
127
|
+
const quote = (part: string): string => {
|
|
128
|
+
if (/["%\r\n\0]/.test(part)) throw new Error(`refusing language server argument with a quote, percent sign or line break: ${part.slice(0, 40)}`)
|
|
129
|
+
return /^[\w@+=:,./\\-]+$/.test(part) ? part : `"${part.replace(/\\+$/, (run) => run + run)}"`
|
|
130
|
+
}
|
|
131
|
+
return spawn([bin, ...args].map(quote).join(" "), { stdio, shell: true })
|
|
132
|
+
}
|
|
133
|
+
|
|
121
134
|
function languageId(ext: string): string {
|
|
122
135
|
const server = LANGUAGES.find((entry) => entry.extensions.includes(ext))
|
|
123
136
|
return server?.language ?? ext.replace(/^\./, "")
|
|
@@ -145,9 +158,9 @@ async function run(filePath: string, timeoutMs: number, configPath: string): Pro
|
|
|
145
158
|
return await new Promise<number>((resolveResult) => {
|
|
146
159
|
let child
|
|
147
160
|
try {
|
|
148
|
-
child =
|
|
149
|
-
} catch {
|
|
150
|
-
process.stdout.write(`FAIL ${absolute}: language server not
|
|
161
|
+
child = launch(bin, binArgs)
|
|
162
|
+
} catch (error) {
|
|
163
|
+
process.stdout.write(`FAIL ${absolute}: language server could not start (${bin}): ${error instanceof Error ? error.message : String(error)}\n`)
|
|
151
164
|
resolveResult(1)
|
|
152
165
|
return
|
|
153
166
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review-work
|
|
3
|
-
description: Hermes-native
|
|
3
|
+
description: Hermes-native plan review (after lit-plan, before start-work) and work review (after lit-loop or start-work), auto-picked from what exists or forced with lit review plan|work: an objective-achievability audit for plans, or five delegate_task lanes with DoneClaim skepticism for work.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## #contract.activation
|
|
@@ -117,28 +117,48 @@ limitations_channel: methodology_paragraph
|
|
|
117
117
|
|
|
118
118
|
# Review Work
|
|
119
119
|
|
|
120
|
-
`review-work` has two read-only modes
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
`review-work` has two read-only modes, named for what is under review.
|
|
121
|
+
|
|
122
|
+
- **Plan review** runs after `lit-plan` and before `start-work`: is the plan
|
|
123
|
+
complete, ordered, and verifiable, so that execution never has to invent scope,
|
|
124
|
+
evidence, or a success path?
|
|
125
|
+
- **Work review** runs after `lit-loop` or `start-work`: do the diff, the tests, and
|
|
126
|
+
the evidence back the claim that the work is done?
|
|
125
127
|
|
|
126
128
|
In both modes, reviewers **never implement** production work. A parent may revise
|
|
127
129
|
only plan text after an `ITERATE` verdict; it must **revise only when needed**, keep
|
|
128
|
-
the approved objective unchanged, and re-run the affected plan
|
|
130
|
+
the approved objective unchanged, and re-run the affected plan review lanes.
|
|
129
131
|
|
|
130
132
|
## Mode Selection
|
|
131
133
|
|
|
132
|
-
|
|
134
|
+
The mode is forced by the user or auto-picked from what exists. Say which mode was
|
|
135
|
+
picked on the first line of the report, before any finding:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
Review mode: plan (forced by the user)
|
|
139
|
+
Review mode: work (auto-picked: changed files outside plans/)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Forcing a mode.** `lit review plan <target>` and `/review-work plan <target>` run
|
|
143
|
+
plan review; `lit review work <target>` and `/review-work work <target>` run work
|
|
144
|
+
review. A forced mode is never overridden by what exists.
|
|
145
|
+
|
|
146
|
+
**Auto-pick.** When no mode is forced, decide in this order and stop at the first
|
|
147
|
+
rule that matches:
|
|
148
|
+
|
|
149
|
+
| Evidence present | Mode | Output |
|
|
133
150
|
|---|---|---|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
151
|
+
| a named PR, branch or diff; changed files outside `plans/`; or a plan with ticked rows (work signals) | work | five-lane `REVIEW PASSED|FAILED|BLOCKED` receipt |
|
|
152
|
+
| a plan with no ticked rows and no changed files outside `plans/` | plan | `PLAN REVIEW PASS|ITERATE|NEEDS-CONTEXT`; findings and, only for `ITERATE`, bounded replacement plan text |
|
|
153
|
+
| neither a plan nor a diff | ask | ask the user one short question: review a plan (which one) or work (which diff, branch or PR)? Do not guess a target and do not dispatch a lane |
|
|
136
154
|
|
|
137
|
-
If both a plan and
|
|
138
|
-
the approved plan in
|
|
139
|
-
|
|
155
|
+
If both a plan and finished work are supplied, audit the implementation against
|
|
156
|
+
the approved plan in work review. The `/review-work` command applies this same
|
|
157
|
+
rule over the real worktree; the natural `lit review` route states it in its
|
|
158
|
+
wrapper and the model applies it. Never force a draft plan through the
|
|
159
|
+
implementation lanes, and never treat a finished diff as a plan.
|
|
140
160
|
|
|
141
|
-
## Plan
|
|
161
|
+
## Plan Review Mode
|
|
142
162
|
|
|
143
163
|
Plan review asks whether execution can begin and end without the implementer
|
|
144
164
|
inventing scope, evidence, or a success path. It does not reward checklist length.
|
|
@@ -186,6 +206,7 @@ aggregate. Otherwise any `ITERATE` controls it; all lanes must pass for `PASS`.
|
|
|
186
206
|
### Plan Verdict Contract
|
|
187
207
|
|
|
188
208
|
```text
|
|
209
|
+
Review mode: plan (<forced by the user | auto-picked: reason>)
|
|
189
210
|
PLAN REVIEW PASS|ITERATE|NEEDS-CONTEXT
|
|
190
211
|
Objective achievable: yes|no|unknown
|
|
191
212
|
Lane verdicts: scope-objective=<...>; dependencies-gates=<...>; atomic-checklist=<...>; acceptance-evidence=<...>; failure-decision-cleanup=<...>
|
|
@@ -200,9 +221,9 @@ the objective is sound but one or more bounded plan defects need correction.
|
|
|
200
221
|
Never mutate production files, run execution tasks, publish, bump a version,
|
|
201
222
|
commit, push, tag, or start a gateway while reviewing a plan.
|
|
202
223
|
|
|
203
|
-
##
|
|
224
|
+
## Work Review Mode
|
|
204
225
|
|
|
205
|
-
|
|
226
|
+
Work review is the LitHermes completion gate, used after `lit-loop` or `start-work`. It is intentionally skeptical: a
|
|
206
227
|
green test suite is useful evidence, not a delivery claim.
|
|
207
228
|
|
|
208
229
|
## Contract
|
|
@@ -375,6 +396,7 @@ Before saying work is done, try to falsify the DoneClaim:
|
|
|
375
396
|
Required final review wording:
|
|
376
397
|
|
|
377
398
|
```text
|
|
399
|
+
Review mode: work (<forced by the user | auto-picked: reason>)
|
|
378
400
|
REVIEW PASSED|FAILED|BLOCKED
|
|
379
401
|
DoneClaim: <one sentence>
|
|
380
402
|
Lane verdicts: scope-diff=<...>; tests-evidence=<...>; package-payload=<...>; security-provenance=<...>; real-surface-docs=<...>
|
|
@@ -485,7 +485,7 @@ isolated Hermes home. Use `--hermes-home`; never `--home`.
|
|
|
485
485
|
|
|
486
486
|
```text
|
|
487
487
|
$ node packages/lithermes-installer/bin/lithermes.js install --yes --offline --no-hud --hermes-home "$ISOLATED"
|
|
488
|
-
Installed LitHermes 1.0.
|
|
488
|
+
Installed LitHermes 1.0.28
|
|
489
489
|
plugin: $ISOLATED/plugins/lithermes
|
|
490
490
|
model config: updated
|
|
491
491
|
|
|
@@ -502,8 +502,8 @@ installed payload: PASS
|
|
|
502
502
|
enabled config: PASS
|
|
503
503
|
|
|
504
504
|
$ hermes lithermes doctor
|
|
505
|
-
[OK] plugin.yaml readable (version 1.0.
|
|
506
|
-
[OK] skills bundled:
|
|
505
|
+
[OK] plugin.yaml readable (version 1.0.28)
|
|
506
|
+
[OK] skills bundled: 37
|
|
507
507
|
[OK] litgoal durable runtime importable
|
|
508
508
|
```
|
|
509
509
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litfamily/lithermes",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
4
|
"description": "Hermes Agent plugin that plans, builds, checks and hands off your work when you add one word, lit, to a request.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hermes-agent",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"qa:negative-control": "node qa/negative-gate-matrix.js --self-check",
|
|
57
57
|
"qa:behaviors": "node qa/behavior-replacement-probe.js",
|
|
58
58
|
"qa:real-surface": "npm run qa:negative-matrix && npm run qa:behaviors && npm run qa:negative-control",
|
|
59
|
+
"qa:cover-recipes": "node qa/cover-recipes.js",
|
|
59
60
|
"sync-plugin": "node scripts/sync-plugin.js",
|
|
60
61
|
"pack:dry": "npm run clean:payload && npm pack --dry-run --ignore-scripts --json",
|
|
61
62
|
"clean:payload": "find assets -name __pycache__ -type d -prune -exec rm -rf {} + && find assets -name '*.py[co]' -delete",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.28"><title>release: 1.0.28</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.28</text></g></svg>
|
|
Binary file
|
|
Binary file
|
package/src/cli.js
CHANGED
|
@@ -538,8 +538,8 @@ async function main(argv, runtime = {}) {
|
|
|
538
538
|
else {
|
|
539
539
|
const warm = motionRuntime('install', flags);
|
|
540
540
|
receipt = warm.status === 0
|
|
541
|
-
? `Motion runtime: ${warm.output.split(
|
|
542
|
-
: `Motion runtime: pre-warm incomplete (${warm.output.split(
|
|
541
|
+
? `Motion runtime: ${warm.output.split(/\r?\n/)[0]}`
|
|
542
|
+
: `Motion runtime: pre-warm incomplete (${warm.output.split(/\r?\n/).filter(Boolean).at(-1)}); run \`lithermes motion-runtime install\` before rendering a film.`;
|
|
543
543
|
}
|
|
544
544
|
(flags.json ? console.error : console.log)(receipt);
|
|
545
545
|
}
|
package/src/lib/check.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require("node:fs");
|
|
2
2
|
const path = require("node:path");
|
|
3
3
|
const { spawnSync } = require("node:child_process");
|
|
4
|
+
const { spawnTool } = require("./hostExec");
|
|
4
5
|
const { configHasLitHermes, formatConcurrencyCapability, inspectHermesCapabilities, readConfig, readExternalSkillDirsConfig, readStatusBarFields, suggestStatusBarFields } = require("./config");
|
|
5
6
|
const { detectHermesRuntimeRepo, detectHermesVersion, ensureHermesHome, expandHome, inspectHermesHostCapabilities, LitHermesError } = require("./hermesDiscovery");
|
|
6
7
|
const { formatManagedRoute } = require("./modelRoutePolicy");
|
|
@@ -205,7 +206,8 @@ function checkLitHermes(flags = {}) {
|
|
|
205
206
|
}
|
|
206
207
|
|
|
207
208
|
// MO-A-44: the motion runtime's five probes (Chrome, ffmpeg and preview rung,
|
|
208
|
-
// WebGL2 renderer, software-GL warning, pre-warm state)
|
|
209
|
+
// WebGL2 renderer, software-GL warning, pre-warm state) and the cover encoder,
|
|
210
|
+
// on every doctor run.
|
|
209
211
|
// They report; they never change the doctor's pass/fail.
|
|
210
212
|
function motionProbeLines(pluginPath, hermesHome) {
|
|
211
213
|
const installed = path.join(pluginPath, "skills", "lit-typographic-motion", "bin", "runtime.mjs");
|
|
@@ -214,13 +216,14 @@ function motionProbeLines(pluginPath, hermesHome) {
|
|
|
214
216
|
const result = spawnSync(process.execPath, [script, "status"], {
|
|
215
217
|
encoding: "utf8", timeout: 60000, env: { ...process.env, HERMES_HOME: hermesHome },
|
|
216
218
|
});
|
|
217
|
-
const lines = String(result.stdout || "").split(
|
|
218
|
-
if (result.status === 0 && lines.length ===
|
|
219
|
-
const reason = (result.stderr || result.error?.message || "no output").split(
|
|
219
|
+
const lines = String(result.stdout || "").split(/\r?\n/).filter(Boolean);
|
|
220
|
+
if (result.status === 0 && lines.length === 6) return lines.map((line) => `motion ${line}`);
|
|
221
|
+
const reason = (result.stderr || result.error?.message || "no output").split(/\r?\n/)[0];
|
|
220
222
|
return [
|
|
221
223
|
"motion Chrome: unknown (status failed)", "motion ffmpeg: unknown (status failed)",
|
|
222
224
|
"motion WebGL2 renderer: unknown (status failed)", "motion software GL: unknown (status failed)",
|
|
223
225
|
`motion pre-warm: status failed (${reason}); run \`lithermes motion-runtime status\``,
|
|
226
|
+
"motion cover encoder: unknown (status failed)",
|
|
224
227
|
];
|
|
225
228
|
}
|
|
226
229
|
|
|
@@ -257,7 +260,7 @@ function doctorLitHermes(flags = {}) {
|
|
|
257
260
|
let loaded = { status: "PARTIAL", reason: "offline mode did not query hermes plugins list" };
|
|
258
261
|
if (!flags.offline) {
|
|
259
262
|
const env = { ...process.env, HERMES_HOME: hermesHome };
|
|
260
|
-
const result =
|
|
263
|
+
const result = spawnTool("hermes", ["plugins", "list", "--json", "--enabled", "--user"], {
|
|
261
264
|
encoding: "utf8",
|
|
262
265
|
env,
|
|
263
266
|
timeout: 15000,
|
|
@@ -266,7 +269,7 @@ function doctorLitHermes(flags = {}) {
|
|
|
266
269
|
&& !String(result.stderr || "").trim()
|
|
267
270
|
&& enabledUserJsonIncludesPlugin(result.stdout, "lithermes");
|
|
268
271
|
const command = enabledUser
|
|
269
|
-
?
|
|
272
|
+
? spawnTool("hermes", ["lithermes", "status"], { encoding: "utf8", env, timeout: 15000 })
|
|
270
273
|
: null;
|
|
271
274
|
const provenLoaded = enabledUser
|
|
272
275
|
&& command.status === 0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require("node:fs");
|
|
2
2
|
const os = require("node:os");
|
|
3
3
|
const path = require("node:path");
|
|
4
|
-
const {
|
|
4
|
+
const { spawnTool } = require("./hostExec");
|
|
5
5
|
|
|
6
6
|
class LitHermesError extends Error {
|
|
7
7
|
constructor(message, exitCode = 1) {
|
|
@@ -124,7 +124,7 @@ function ensureHermesHome(flags = {}, { forInstall = false } = {}) {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
function detectHermesVersion() {
|
|
127
|
-
const result =
|
|
127
|
+
const result = spawnTool("hermes", ["--version"], { encoding: "utf8", timeout: 15000 });
|
|
128
128
|
return result.status === 0 ? (result.stdout || result.stderr || "") : "";
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Child processes that survive Windows. Since the CVE-2024-27980 fix, Node refuses to spawn a
|
|
4
|
+
// .cmd or .bat file without a shell (spawnSync npm.cmd EINVAL), and PATH lookups there depend on
|
|
5
|
+
// PATHEXT. Everything takes its platform, env and fs probes as arguments so tests can simulate
|
|
6
|
+
// win32 without touching globals. The payload skills carry the same logic as hostexec.mjs.
|
|
7
|
+
|
|
8
|
+
const childProcess = require("node:child_process");
|
|
9
|
+
const fs = require("node:fs");
|
|
10
|
+
const path = require("node:path");
|
|
11
|
+
|
|
12
|
+
const SHELL_SCRIPT = /\.(?:cmd|bat)$/i;
|
|
13
|
+
const SAFE_ARG = /^[\w@+=:,./\\-]+$/;
|
|
14
|
+
|
|
15
|
+
function whichSync(name, { platform = process.platform, env = process.env, exists = fs.existsSync } = {}) {
|
|
16
|
+
const win = platform === "win32";
|
|
17
|
+
const p = win ? path.win32 : path.posix;
|
|
18
|
+
const exts = win ? String(env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim().toLowerCase()).filter(Boolean) : [""];
|
|
19
|
+
const hasExt = win && exts.some((e) => name.toLowerCase().endsWith(e.toLowerCase()));
|
|
20
|
+
// A bare name never matches on Windows: npm leaves an extensionless sh script beside npm.cmd.
|
|
21
|
+
const names = win ? (hasExt ? [name] : exts.map((e) => name + e)) : [name];
|
|
22
|
+
const dirs = p.isAbsolute(name) || /[\\/]/.test(name) ? [""] : String(env.PATH ?? env.Path ?? "").split(win ? ";" : ":").map((d) => d.trim().replace(/^"|"$/g, "")).filter(Boolean);
|
|
23
|
+
for (const dir of dirs) {
|
|
24
|
+
for (const candidate of names) {
|
|
25
|
+
const full = dir ? p.join(dir, candidate) : candidate;
|
|
26
|
+
if (exists(full)) return full;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Quoted for cmd.exe, which still expands %VAR% inside quotes, so those are refused, never escaped.
|
|
33
|
+
function quoteCmdArg(arg) {
|
|
34
|
+
const text = String(arg);
|
|
35
|
+
if (/["%\r\n\0]/.test(text)) throw new Error(`refusing an argument with a quote, percent sign or line break on a Windows shell: ${JSON.stringify(text.slice(0, 40))}`);
|
|
36
|
+
if (text !== "" && SAFE_ARG.test(text)) return text;
|
|
37
|
+
return `"${text.replace(/\\+$/, (run) => run + run)}"`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Where an npm or npx shim's JavaScript entry lives, so Windows can run it with node directly.
|
|
41
|
+
function npmCli(name, resolved, { env, execPath, exists }) {
|
|
42
|
+
const cli = `${name}-cli.js`;
|
|
43
|
+
const dirs = [];
|
|
44
|
+
if (typeof env.npm_execpath === "string" && /\.js$/i.test(env.npm_execpath)) dirs.push(path.win32.dirname(env.npm_execpath));
|
|
45
|
+
dirs.push(path.win32.join(path.win32.dirname(resolved), "node_modules", "npm", "bin"));
|
|
46
|
+
dirs.push(path.win32.join(path.win32.dirname(execPath), "node_modules", "npm", "bin"));
|
|
47
|
+
return dirs.map((dir) => path.win32.join(dir, cli)).find((file) => exists(file)) || null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function prepareSpawn(command, args = [], options = {}, ctx = {}) {
|
|
51
|
+
const { platform = process.platform, execPath = process.execPath, exists = fs.existsSync } = ctx;
|
|
52
|
+
if (platform !== "win32") return { command, args, options };
|
|
53
|
+
const env = ctx.env || options.env || process.env;
|
|
54
|
+
const resolved = whichSync(command, { platform, env, exists });
|
|
55
|
+
if (!resolved || !SHELL_SCRIPT.test(resolved)) return { command: resolved || command, args, options };
|
|
56
|
+
const base = path.win32.basename(resolved).replace(SHELL_SCRIPT, "").toLowerCase();
|
|
57
|
+
if (base === "npm" || base === "npx") {
|
|
58
|
+
const cli = npmCli(base, resolved, { env, execPath, exists });
|
|
59
|
+
if (cli) return { command: execPath, args: [cli, ...args], options };
|
|
60
|
+
}
|
|
61
|
+
const line = [resolved, ...args].map(quoteCmdArg).join(" ");
|
|
62
|
+
return { command: line, args: [], options: { ...options, shell: true } };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function spawnTool(command, args, options, ctx = {}) {
|
|
66
|
+
const run = ctx.spawnSync || childProcess.spawnSync;
|
|
67
|
+
const prepared = prepareSpawn(command, args, options, ctx);
|
|
68
|
+
return run(prepared.command, prepared.args, prepared.options);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// One line naming the real cause: the spawn error, the exit status and the last stderr line.
|
|
72
|
+
function describeSpawnFailure(label, result, { limit = 200 } = {}) {
|
|
73
|
+
if (!result) return `${label} returned no result`;
|
|
74
|
+
const tail = String(result.stderr || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).at(-1) || "";
|
|
75
|
+
const clean = tail.replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, limit);
|
|
76
|
+
if (result.error) return `${label} could not run: ${result.error.code || "error"} ${result.error.message}${clean ? ` (${clean})` : ""}`;
|
|
77
|
+
if (result.signal) return `${label} stopped by ${result.signal}${clean ? ` (${clean})` : ""}`;
|
|
78
|
+
return `${label} exited ${result.status}${clean ? `: ${clean}` : ""}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { describeSpawnFailure, prepareSpawn, quoteCmdArg, spawnTool, whichSync };
|
package/src/lib/skillPayload.js
CHANGED
|
@@ -16,7 +16,7 @@ const MAX_PAYLOAD_TOTAL_BYTES = 24 * 1024 * 1024;
|
|
|
16
16
|
const requiredSkills = [
|
|
17
17
|
"lit-burnoff-file", "autoconference", "autoresearch", "browser-drive", "comment-checker", "debugging",
|
|
18
18
|
"deep-interview",
|
|
19
|
-
"frontend-ui-ux", "readme-studio", "lit-commit", "lit-crucible", "lit-init",
|
|
19
|
+
"frontend-ui-ux", "readme-studio", "lit-commit", "lit-brainstorm", "lit-crucible", "lit-init",
|
|
20
20
|
"lit-humanizer", "lit-comprehend", "lit-handoff", "lit-plan", "lit-recap",
|
|
21
21
|
"lit-scientific-visualization", "lit-diagram-drawer", "lit-pptx", "lit-docx", "lit-typographic-motion", "litgoal", "litresearch", "litwork", "lsp",
|
|
22
22
|
"lsp-setup", "lit-code", "refactor", "lit-burnoff", "review-work",
|