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-14T03:56:58.019Z",
2
+ "syncedAt": "2026-06-14T07:32:26.488Z",
3
3
  "source": "source-reference",
4
- "sourceHash": "23886fd1b52dc30433f8886ba15ccedbc3d134ba8f0b99b7a90a83d07c0351fe",
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": "14951813219a77f7074ff8ca36e26cc46b66b0987f28dbcf1827c9bcfa98d65c"
16
+ "sha256": "9a0b19060eb12799bf76b15b729fcbab4388d74aac9e888b40369b809fd54669"
17
17
  },
18
18
  {
19
19
  "path": "core.py",
20
- "sha256": "da2888e70b560697eaf81fe29d0b92a36e8e0563ab3bcab95c34472533faa937"
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": "4b8a31a093d883cf1714250564955c3d8ca87889887780515a7df15ed317e732"
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.0
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lithermes-ai",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "npx/bunx installer for the LitHermes Hermes plugin",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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 wasUpToDate = fs.existsSync(dest) && installedMatchesManifest(hermesHome, existingManifest);
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: require(path.join(packageRoot, "package.json")).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
- wasUpToDate ? "LitHermes already up to date" : "Installed LitHermes",
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,