@litfamily/lithermes 1.0.32 → 1.0.33
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 +7 -7
- package/README_Ko-KR.md +7 -7
- package/assets/lithermes-plugin/README.md +0 -12
- package/assets/lithermes-plugin/adhd_stack.py +43 -1
- package/assets/lithermes-plugin/auto_handoff.py +13 -2
- package/assets/lithermes-plugin/bounded_work.py +33 -3
- package/assets/lithermes-plugin/core.py +59 -18
- package/assets/lithermes-plugin/core_review.py +2 -2
- package/assets/lithermes-plugin/lit_mark.py +2 -7
- package/assets/lithermes-plugin/payload-version.json +43 -47
- package/assets/lithermes-plugin/plugin.yaml +1 -1
- package/assets/lithermes-plugin/rules/bundled-rules/baseline-discipline.md +7 -13
- package/assets/lithermes-plugin/rules/globmatch.py +1 -1
- package/assets/lithermes-plugin/skills/debugging/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/debugging/references/methodology/00-setup.md +1 -1
- package/assets/lithermes-plugin/skills/debugging/references/methodology/02-investigate.md +2 -2
- package/assets/lithermes-plugin/skills/debugging/references/methodology/09-cleanup.md +1 -1
- package/assets/lithermes-plugin/skills/debugging/references/runtimes/native-binary.md +1 -1
- package/assets/lithermes-plugin/skills/debugging/references/runtimes/rust.md +4 -4
- package/assets/lithermes-plugin/skills/debugging/references/tools/ghidra.md +1 -1
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/nuxtjs.csv +1 -1
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/manifest.json +2 -2
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +2 -2
- package/assets/lithermes-plugin/skills/lit-adhd/SKILL.md +10 -10
- package/assets/lithermes-plugin/skills/lit-adhd/references/brief-contract.md +12 -7
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/collect.mjs +81 -27
- package/assets/lithermes-plugin/skills/lit-adhd/scripts/lib/next-step.mjs +34 -6
- package/assets/lithermes-plugin/skills/lit-burnoff/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/lit-code/SKILL.md +4 -4
- package/assets/lithermes-plugin/skills/lit-code/references/rust/README.md +2 -2
- package/assets/lithermes-plugin/skills/lit-code/references/rust/async-tokio.md +4 -4
- package/assets/lithermes-plugin/skills/lit-code/references/rust/axum-stack.md +1 -1
- package/assets/lithermes-plugin/skills/lit-code/references/rust/clap-stack.md +1 -1
- package/assets/lithermes-plugin/skills/lit-code/references/rust/libraries.md +3 -3
- package/assets/lithermes-plugin/skills/lit-code/references/rust/proptest-insta.md +10 -10
- package/assets/lithermes-plugin/skills/lit-code/references/rust/zero-cost-safety.md +1 -1
- package/assets/lithermes-plugin/skills/lit-code/references/typescript/README.md +2 -2
- package/assets/lithermes-plugin/skills/lit-code/references/typescript/backend-hono.md +1 -1
- package/assets/lithermes-plugin/skills/lit-code/references/typescript/tsconfig-strict.md +1 -1
- package/assets/lithermes-plugin/skills/lit-code/scripts/typescript/check-no-excuse-rules.ts +2 -2
- package/assets/lithermes-plugin/skills/readme-studio/templates/remotion/package-lock.json +3 -3
- package/assets/lithermes-plugin/skills/readme-studio/templates/remotion/package.json +2 -1
- package/assets/lithermes-plugin/skills/rules/SKILL.md +1 -1
- package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +2 -2
- package/assets/{lithermes-plugin → npm-channel}/auto_update.py +43 -9
- package/package.json +1 -1
- package/readme-assets/badge-version.svg +1 -1
- package/src/lib/canonicalCorpus.js +1 -1
- package/src/lib/install.js +22 -0
- package/src/lib/skillPayload.js +10 -5
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {"render": "remotion render", "still": "remotion still"},
|
|
6
|
-
"dependencies": {"@remotion/cli":"4.0.526", "remotion":"4.0.526", "react":"19.3.0", "react-dom":"19.3.0"}
|
|
6
|
+
"dependencies": {"@remotion/cli":"4.0.526", "remotion":"4.0.526", "react":"19.3.0", "react-dom":"19.3.0"},
|
|
7
|
+
"overrides": {"zod": "4.6.6"}
|
|
7
8
|
}
|
|
@@ -169,7 +169,7 @@ priority rules.
|
|
|
169
169
|
`paths` and Copilot `applyTo` aliases. A Cursor-style comma-separated scalar
|
|
170
170
|
(`globs: *.ts,*.tsx`) is split into separate patterns.
|
|
171
171
|
- **Glob support is a documented SUBSET**, not picomatch. `**`, `*`, `?`,
|
|
172
|
-
`[seq]`, `{a,b}` and a leading
|
|
172
|
+
`[seq]`, `{a,b}` and a leading (!) work. Extglob (`@(a|b)`) and POSIX classes
|
|
173
173
|
(`[[:alpha:]]`) do NOT — a rule using them silently matches nothing. The full
|
|
174
174
|
divergence list lives in `rules/globmatch.py` `DIVERGENCES`; read it before
|
|
175
175
|
claiming a glob "should have" matched.
|
|
@@ -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.33
|
|
489
489
|
plugin: $ISOLATED/plugins/lithermes
|
|
490
490
|
model config: updated
|
|
491
491
|
|
|
@@ -502,7 +502,7 @@ installed payload: PASS
|
|
|
502
502
|
enabled config: PASS
|
|
503
503
|
|
|
504
504
|
$ hermes lithermes doctor
|
|
505
|
-
[OK] plugin.yaml readable (version 1.0.
|
|
505
|
+
[OK] plugin.yaml readable (version 1.0.33)
|
|
506
506
|
[OK] skills bundled: 40
|
|
507
507
|
[OK] litgoal durable runtime importable
|
|
508
508
|
```
|
|
@@ -42,6 +42,8 @@ INSTALL_TIMEOUT_SECONDS = 90.0
|
|
|
42
42
|
MAX_RESPONSE_BYTES = 64 * 1024
|
|
43
43
|
AUTO_UPDATE_GUARD_ENV = "LITHERMES_AUTO_UPDATE_RUNNING"
|
|
44
44
|
INSTALLER_DISTRIBUTION = "npm"
|
|
45
|
+
NPM_CHANNEL_MARKER_NAME = ".lithermes-npm-channel"
|
|
46
|
+
NPM_CHANNEL_MARKER_TEXT = "lithermes npm channel\n"
|
|
45
47
|
ATTEMPT_RECORD_NAME = "auto-update-attempt.json"
|
|
46
48
|
# A broken npm fetch blocked each new session's first reply for about 70 s, so
|
|
47
49
|
# after a failed install the bridge waits a day before it tries the same
|
|
@@ -244,19 +246,51 @@ def _read_install_receipt(home: Path) -> dict[str, Any] | None:
|
|
|
244
246
|
return _read_state_json(home, "auto-update-receipt.json")
|
|
245
247
|
|
|
246
248
|
|
|
249
|
+
def _plugin_dir(home: Path, manifest: dict[str, Any] | None) -> Path:
|
|
250
|
+
"""Prefer the receipt's plugin path when it is a real directory, not a link."""
|
|
251
|
+
|
|
252
|
+
raw = manifest.get("pluginPath") if isinstance(manifest, dict) else None
|
|
253
|
+
if isinstance(raw, str) and raw.strip():
|
|
254
|
+
candidate = Path(raw).expanduser()
|
|
255
|
+
try:
|
|
256
|
+
info = candidate.lstat()
|
|
257
|
+
except OSError:
|
|
258
|
+
info = None
|
|
259
|
+
if info is not None and stat.S_ISDIR(info.st_mode) and not stat.S_ISLNK(info.st_mode):
|
|
260
|
+
return candidate
|
|
261
|
+
return home / "plugins" / "lithermes"
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _channel_marker_matches(root: Path) -> bool:
|
|
265
|
+
marker = root / NPM_CHANNEL_MARKER_NAME
|
|
266
|
+
expected = NPM_CHANNEL_MARKER_TEXT.encode("utf-8")
|
|
267
|
+
try:
|
|
268
|
+
info = marker.lstat()
|
|
269
|
+
if not stat.S_ISREG(info.st_mode) or stat.S_ISLNK(info.st_mode):
|
|
270
|
+
return False
|
|
271
|
+
fd = os.open(marker, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0))
|
|
272
|
+
try:
|
|
273
|
+
data = os.read(fd, len(expected) + 1)
|
|
274
|
+
finally:
|
|
275
|
+
os.close(fd)
|
|
276
|
+
except OSError:
|
|
277
|
+
return False
|
|
278
|
+
return data == expected
|
|
279
|
+
|
|
280
|
+
|
|
247
281
|
def _is_npm_channel(home: Path) -> bool:
|
|
248
|
-
"""Return true only for
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
treated as non-npm until the user runs the installer once, which preserves the
|
|
255
|
-
catalog safety boundary over an ambiguous channel.
|
|
282
|
+
"""Return true only for an npm receipt that also carries the channel marker.
|
|
283
|
+
|
|
284
|
+
The receipt lives outside the plugin directory and survives a catalog replace,
|
|
285
|
+
so ``distribution: npm`` alone is not permission to fetch and replace the
|
|
286
|
+
tree. The marker is written only by the npm installer, into the plugin
|
|
287
|
+
directory, and a catalog copy never contains it. A symlink is not the marker.
|
|
256
288
|
"""
|
|
257
289
|
|
|
258
290
|
manifest = _read_state_json(home, "install-manifest.json")
|
|
259
|
-
|
|
291
|
+
if not (isinstance(manifest, dict) and manifest.get("distribution") == INSTALLER_DISTRIBUTION):
|
|
292
|
+
return False
|
|
293
|
+
return _channel_marker_matches(_plugin_dir(home, manifest))
|
|
260
294
|
|
|
261
295
|
|
|
262
296
|
def _utc_stamp(seconds: float | None = None) -> str:
|
package/package.json
CHANGED
|
@@ -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.33"><title>release: 1.0.33</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.33</text></g></svg>
|
|
@@ -14,7 +14,7 @@ const EXPECTED = Object.freeze({
|
|
|
14
14
|
tree: "9188410be0af35f2421ba300d91a0d7a7341caf0",
|
|
15
15
|
fileCount: 167,
|
|
16
16
|
totalBytes: 2596360,
|
|
17
|
-
digest: "
|
|
17
|
+
digest: "c921ed87f41b5aaef14ef480c791f1c36bbd3ec616eaccc32f5e1246fcc547e6",
|
|
18
18
|
directories: ["design", "designpowers", "perfection", "ui-ux-db"],
|
|
19
19
|
attributes: "corpus/** -text\nlegal/** -text\n",
|
|
20
20
|
legal: [
|
package/src/lib/install.js
CHANGED
|
@@ -32,6 +32,9 @@ const { installSkins } = require("./skins");
|
|
|
32
32
|
|
|
33
33
|
const packageRoot = path.resolve(__dirname, "..", "..");
|
|
34
34
|
const assetRoot = path.join(packageRoot, "assets", "lithermes-plugin");
|
|
35
|
+
const channelRoot = path.join(packageRoot, "assets", "npm-channel");
|
|
36
|
+
const NPM_CHANNEL_MARKER_NAME = ".lithermes-npm-channel";
|
|
37
|
+
const NPM_CHANNEL_MARKER_TEXT = "lithermes npm channel\n";
|
|
35
38
|
const LEGACY_VENDOR_DIRS = new Map([
|
|
36
39
|
["vendor/022_handoff", "vendor/handoff"],
|
|
37
40
|
["vendor/045_scientific-visualization", "vendor/scientific-visualization"],
|
|
@@ -547,6 +550,24 @@ function previewModelRoute(flags = {}) {
|
|
|
547
550
|
};
|
|
548
551
|
}
|
|
549
552
|
|
|
553
|
+
function stageNpmChannel(dest) {
|
|
554
|
+
const source = path.join(channelRoot, "auto_update.py");
|
|
555
|
+
const sourceStat = fs.lstatSync(source);
|
|
556
|
+
if (!sourceStat.isFile()) {
|
|
557
|
+
throw new LitHermesError("npm channel updater is missing.", 5);
|
|
558
|
+
}
|
|
559
|
+
const target = path.join(dest, "auto_update.py");
|
|
560
|
+
fs.copyFileSync(source, target);
|
|
561
|
+
fs.chmodSync(target, 0o644);
|
|
562
|
+
const marker = path.join(dest, NPM_CHANNEL_MARKER_NAME);
|
|
563
|
+
fs.writeFileSync(marker, NPM_CHANNEL_MARKER_TEXT, { encoding: "utf8", mode: 0o644 });
|
|
564
|
+
fs.chmodSync(marker, 0o644);
|
|
565
|
+
return [
|
|
566
|
+
{ path: "auto_update.py", sha256: sha256(target) },
|
|
567
|
+
{ path: NPM_CHANNEL_MARKER_NAME, sha256: sha256(marker) },
|
|
568
|
+
];
|
|
569
|
+
}
|
|
570
|
+
|
|
550
571
|
function installLitHermes(flags = {}) {
|
|
551
572
|
const dryRun = Boolean(flags["dry-run"]);
|
|
552
573
|
const homeInfo = dryRun
|
|
@@ -655,6 +676,7 @@ function installLitHermes(flags = {}) {
|
|
|
655
676
|
assertPluginParent(hermesHome);
|
|
656
677
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
657
678
|
const files = copyTree(assetRoot, dest);
|
|
679
|
+
files.push(...stageNpmChannel(dest));
|
|
658
680
|
onProgress("Writing Hermes config");
|
|
659
681
|
checkState();
|
|
660
682
|
let afterConfig = beforeConfig;
|
package/src/lib/skillPayload.js
CHANGED
|
@@ -221,7 +221,7 @@ function inspectSkillPayload(pluginPath, manifest, verifySourceHash = false, opt
|
|
|
221
221
|
const failure = checkEntry(pluginPath, entryPath, expectedHash);
|
|
222
222
|
if (failure) failures.push(failure);
|
|
223
223
|
}
|
|
224
|
-
const allowedUnlisted = new Set(["payload-version.json"]);
|
|
224
|
+
const allowedUnlisted = new Set(["payload-version.json", "auto_update.py", ".lithermes-npm-channel"]);
|
|
225
225
|
const allowedDirectories = new Set();
|
|
226
226
|
for (const entryPath of inspected.entries.keys()) {
|
|
227
227
|
const segments = entryPath.split("/");
|
|
@@ -237,20 +237,25 @@ function inspectSkillPayload(pluginPath, manifest, verifySourceHash = false, opt
|
|
|
237
237
|
const directory = path.posix.dirname(entryPath);
|
|
238
238
|
hostCacheDirectories.add(directory === "." ? "__pycache__/" : `${directory}/__pycache__/`);
|
|
239
239
|
}
|
|
240
|
-
const
|
|
241
|
-
const source = hostBytecodeSource(entryPath);
|
|
242
|
-
if (!source || !inspected.entries.has(source)) return false;
|
|
240
|
+
const isRegularFile = (entryPath) => {
|
|
243
241
|
try {
|
|
244
242
|
return fs.lstatSync(path.join(pluginPath, ...entryPath.split("/"))).isFile();
|
|
245
243
|
} catch {
|
|
246
244
|
return false;
|
|
247
245
|
}
|
|
248
246
|
};
|
|
247
|
+
const isHostBytecode = (entryPath) => {
|
|
248
|
+
const source = hostBytecodeSource(entryPath);
|
|
249
|
+
if (!source || !(inspected.entries.has(source) || source === "auto_update.py")) return false;
|
|
250
|
+
return isRegularFile(entryPath);
|
|
251
|
+
};
|
|
252
|
+
const isNpmChannelFile = (entryPath) =>
|
|
253
|
+
(entryPath === "auto_update.py" || entryPath === ".lithermes-npm-channel") && isRegularFile(entryPath);
|
|
249
254
|
const traversal = listPayloadEntries(pluginPath, options.readDirectory);
|
|
250
255
|
for (const entryPath of traversal.entries) {
|
|
251
256
|
if (
|
|
252
257
|
inspected.entries.has(entryPath)
|
|
253
|
-
|| allowedUnlisted.has(entryPath)
|
|
258
|
+
|| (allowedUnlisted.has(entryPath) && (entryPath === "payload-version.json" || isNpmChannelFile(entryPath)))
|
|
254
259
|
|| allowedDirectories.has(entryPath)
|
|
255
260
|
|| hostCacheDirectories.has(entryPath)
|
|
256
261
|
|| isHostBytecode(entryPath)
|