lithermes-ai 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -133,6 +133,9 @@ def register(ctx) -> None:
|
|
|
133
133
|
ctx.register_hook("pre_llm_call", _pre_llm_call)
|
|
134
134
|
# Record each delegate_task child (review lanes, reviewer gate) to the ledger.
|
|
135
135
|
ctx.register_hook("subagent_stop", core.subagent_stop)
|
|
136
|
+
# Force the LITBURN banner onto a bare-`lit` keyword turn's response (that
|
|
137
|
+
# path has no deterministic display channel; slash commands print it directly).
|
|
138
|
+
ctx.register_hook("transform_llm_output", core.transform_llm_output)
|
|
136
139
|
|
|
137
140
|
# Model-facing litgoal tools (durable criteria/evidence/checkpoint/steering/gate).
|
|
138
141
|
litgoal_tools.register_tools(ctx)
|
|
@@ -38,6 +38,12 @@ _SLUG_PATTERN = re.compile(r"[^a-z0-9]+")
|
|
|
38
38
|
# for the bare-`lit` keyword path (best-effort — that path only reaches the model).
|
|
39
39
|
LITBURN_BANNER = "🔥 LITBURN IGNITED 🔥"
|
|
40
40
|
|
|
41
|
+
# Session ids whose CURRENT turn triggered the litwork keyword path. The bare
|
|
42
|
+
# `lit` keyword has no deterministic display channel (pre_llm_call only reaches
|
|
43
|
+
# the model), so pre_llm_call flags the turn here and transform_llm_output forces
|
|
44
|
+
# the banner onto that turn's response before the user sees it. Consumed once.
|
|
45
|
+
_PENDING_IGNITE: set[str] = set()
|
|
46
|
+
|
|
41
47
|
|
|
42
48
|
LIT_CONTEXT = "\n".join(
|
|
43
49
|
[
|
|
@@ -258,6 +264,10 @@ def ignite(result: str | dict[str, str]) -> str | dict[str, str]:
|
|
|
258
264
|
def pre_llm_call(**kwargs: Any) -> dict[str, str] | None:
|
|
259
265
|
user_message = str(kwargs.get("user_message") or "")
|
|
260
266
|
session_id = str(kwargs.get("session_id") or "")
|
|
267
|
+
# Clear any stale ignite flag from an interrupted previous turn so it can't
|
|
268
|
+
# leak the banner onto this turn's response. Re-added below only if THIS turn
|
|
269
|
+
# is a keyword-lit turn — keeping the flag scoped to the current turn.
|
|
270
|
+
_PENDING_IGNITE.discard(session_id)
|
|
261
271
|
# /litgoal & /lit-plan declare an objective via the bind-goal marker. Bind it
|
|
262
272
|
# (we have session_id here) and stop — those messages are self-contained.
|
|
263
273
|
bind_obj = _extract_bind_goal(user_message)
|
|
@@ -291,9 +301,31 @@ def pre_llm_call(**kwargs: Any) -> dict[str, str] | None:
|
|
|
291
301
|
session_id=session_id,
|
|
292
302
|
platform=str(kwargs.get("platform") or ""),
|
|
293
303
|
)
|
|
304
|
+
# Flag this turn so transform_llm_output forces the banner onto the response
|
|
305
|
+
# (the keyword path has no deterministic display channel like slash commands).
|
|
306
|
+
if session_id:
|
|
307
|
+
_PENDING_IGNITE.add(session_id)
|
|
294
308
|
return {"context": LIT_CONTEXT + run_context}
|
|
295
309
|
|
|
296
310
|
|
|
311
|
+
def transform_llm_output(**kwargs: Any) -> str | None:
|
|
312
|
+
"""Force the LITBURN banner onto a keyword-`lit` turn's response.
|
|
313
|
+
|
|
314
|
+
Hermes replaces the user-visible response with the first non-empty string a
|
|
315
|
+
transform_llm_output hook returns. Returning None leaves the text unchanged.
|
|
316
|
+
We only act on turns pre_llm_call flagged, consume the flag once, and dedup so
|
|
317
|
+
a model that already opened with the banner is not double-bannered.
|
|
318
|
+
"""
|
|
319
|
+
session_id = str(kwargs.get("session_id") or "")
|
|
320
|
+
if session_id not in _PENDING_IGNITE:
|
|
321
|
+
return None
|
|
322
|
+
_PENDING_IGNITE.discard(session_id)
|
|
323
|
+
response_text = str(kwargs.get("response_text") or "")
|
|
324
|
+
if response_text.lstrip().startswith(LITBURN_BANNER):
|
|
325
|
+
return None # model already emitted it — leave unchanged
|
|
326
|
+
return f"{LITBURN_BANNER}\n\n{response_text}"
|
|
327
|
+
|
|
328
|
+
|
|
297
329
|
def subagent_stop(**kwargs: Any) -> None:
|
|
298
330
|
"""Record each delegate_task child (e.g. a review lane) to the LitHermes ledger.
|
|
299
331
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"syncedAt": "2026-06-
|
|
2
|
+
"syncedAt": "2026-06-14T07:32:26.488Z",
|
|
3
3
|
"source": "source-reference",
|
|
4
|
-
"sourceHash": "
|
|
4
|
+
"sourceHash": "4ec00032263690e3fea1b618c961ae32aaa950db46d5320f4fcc79a844652ab6",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": "NOTICE.md",
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "__init__.py",
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "9a0b19060eb12799bf76b15b729fcbab4388d74aac9e888b40369b809fd54669"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "core.py",
|
|
20
|
-
"sha256": "
|
|
20
|
+
"sha256": "6b9d8b488d5905232d170f78943d2b5f9b9a4bde564e01a816c4fcc45098a7df"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "litgoal/__init__.py",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"path": "plugin.yaml",
|
|
52
|
-
"sha256": "
|
|
52
|
+
"sha256": "d356b26af023599d32bde33f8c51053fdd4970d8ff710d4d1243d87a438f0f38"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"path": "skills/ai-slop-remover/SKILL.md",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: lithermes
|
|
2
|
-
version: 0.8.
|
|
2
|
+
version: 0.8.2
|
|
3
3
|
description: "Hermes-native workflow toolkit: litgoal durable runtime, 5-lane review orchestrator, Litwork commands, skills, and prompt steering."
|
|
4
4
|
author: "Hermes Agent"
|
|
5
5
|
kind: standalone
|
|
@@ -7,3 +7,4 @@ auto_load: true
|
|
|
7
7
|
hooks:
|
|
8
8
|
- pre_llm_call
|
|
9
9
|
- subagent_stop
|
|
10
|
+
- transform_llm_output
|
package/package.json
CHANGED
package/src/lib/install.js
CHANGED
|
@@ -50,6 +50,15 @@ function installedMatchesManifest(hermesHome, manifest) {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// Version-aware install status (NOT a manifest-integrity check): distinguish a
|
|
54
|
+
// fresh install, a same-version reinstall, and a real upgrade so the message
|
|
55
|
+
// never claims "already up to date" while actually replacing an older version.
|
|
56
|
+
function installStatusLine(installedVersion, packageVersion) {
|
|
57
|
+
if (!installedVersion) return `Installed LitHermes ${packageVersion}`;
|
|
58
|
+
if (installedVersion === packageVersion) return `LitHermes already up to date (${packageVersion})`;
|
|
59
|
+
return `Upgraded LitHermes ${installedVersion} → ${packageVersion}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
53
62
|
function installLitHermes(flags = {}) {
|
|
54
63
|
const { hermesHome, hermesRepo } = ensureHermesHome(flags, { forInstall: true });
|
|
55
64
|
const dest = pluginDest(hermesHome);
|
|
@@ -72,7 +81,8 @@ function installLitHermes(flags = {}) {
|
|
|
72
81
|
return withLock(hermesHome, () => {
|
|
73
82
|
onProgress("Inspecting existing plugin");
|
|
74
83
|
const existingManifest = loadManifest(hermesHome);
|
|
75
|
-
const
|
|
84
|
+
const packageVersion = require(path.join(packageRoot, "package.json")).version;
|
|
85
|
+
const installedVersion = fs.existsSync(dest) && existingManifest ? existingManifest.version : null;
|
|
76
86
|
if (fs.existsSync(dest) && !installedMatchesManifest(hermesHome, existingManifest) && !flags.force) {
|
|
77
87
|
throw new LitHermesError(`Existing LitHermes plugin at ${dest} is not manifest-owned. Use --force after backing it up.`, 5);
|
|
78
88
|
}
|
|
@@ -92,7 +102,7 @@ function installLitHermes(flags = {}) {
|
|
|
92
102
|
}
|
|
93
103
|
onProgress("Recording install manifest");
|
|
94
104
|
const manifest = {
|
|
95
|
-
version:
|
|
105
|
+
version: packageVersion,
|
|
96
106
|
installedAt: new Date().toISOString(),
|
|
97
107
|
pluginPath: dest,
|
|
98
108
|
configPath: path.join(hermesHome, "config.yaml"),
|
|
@@ -114,7 +124,7 @@ function installLitHermes(flags = {}) {
|
|
|
114
124
|
}
|
|
115
125
|
}
|
|
116
126
|
const lines = [
|
|
117
|
-
|
|
127
|
+
installStatusLine(installedVersion, packageVersion),
|
|
118
128
|
`plugin: ${dest}`,
|
|
119
129
|
];
|
|
120
130
|
if (patchResult) lines.push(`patches: ${patchResult.changed.length ? patchResult.changed.join(", ") : "none needed"}`);
|
|
@@ -152,6 +162,7 @@ function uninstallLitHermes(flags = {}) {
|
|
|
152
162
|
module.exports = {
|
|
153
163
|
assetRoot,
|
|
154
164
|
installLitHermes,
|
|
165
|
+
installStatusLine,
|
|
155
166
|
loadManifest,
|
|
156
167
|
manifestPath,
|
|
157
168
|
pluginDest,
|