@codyswann/lisa 4.87.2 → 4.87.4

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.
Files changed (164) hide show
  1. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +36 -21
  2. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  3. package/dist/core/lisa-owned-hash-ledger.js +10 -0
  4. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  5. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  6. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  7. package/dist/core/upstream-evidence-manifest.js +20 -14
  8. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  9. package/dist/opencode/plugin-templates/parity-safety-net.sh +36 -21
  10. package/package.json +4 -4
  11. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  13. package/plugins/lisa/.codex-plugin/skills/lisa-parity-code-review/SKILL.md +3 -2
  14. package/plugins/lisa/.codex-plugin/skills/lisa-parity-code-simplifier/SKILL.md +1 -1
  15. package/plugins/lisa/.codex-plugin/skills/lisa-parity-coderabbit/SKILL.md +1 -1
  16. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +25 -6
  17. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-human.mjs +37 -10
  18. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-setup.mjs +22 -6
  19. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +22 -12
  20. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-workers.mjs +21 -0
  21. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-answer.mjs +76 -21
  22. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +6 -2
  23. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-migrate.mjs +15 -3
  24. package/plugins/lisa/hooks/parity-safety-net.sh +36 -21
  25. package/plugins/lisa/skills/lisa-parity-code-review/SKILL.md +3 -2
  26. package/plugins/lisa/skills/lisa-parity-code-simplifier/SKILL.md +1 -1
  27. package/plugins/lisa/skills/lisa-parity-coderabbit/SKILL.md +1 -1
  28. package/plugins/lisa/skills/lisa-session-manager/SKILL.md +25 -6
  29. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-human.mjs +37 -10
  30. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-setup.mjs +22 -6
  31. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +22 -12
  32. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-workers.mjs +21 -0
  33. package/plugins/lisa/skills/lisa-session-manager/scripts/human-answer.mjs +76 -21
  34. package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +6 -2
  35. package/plugins/lisa/skills/lisa-session-manager/scripts/token-migrate.mjs +15 -3
  36. package/plugins/lisa-agy/hooks/parity-safety-net.sh +36 -21
  37. package/plugins/lisa-agy/plugin.json +1 -1
  38. package/plugins/lisa-agy/skills/lisa-parity-code-review/SKILL.md +3 -2
  39. package/plugins/lisa-agy/skills/lisa-parity-code-simplifier/SKILL.md +1 -1
  40. package/plugins/lisa-agy/skills/lisa-parity-coderabbit/SKILL.md +1 -1
  41. package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +25 -6
  42. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-human.mjs +37 -10
  43. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-setup.mjs +22 -6
  44. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +22 -12
  45. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-workers.mjs +21 -0
  46. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-answer.mjs +76 -21
  47. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +6 -2
  48. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-migrate.mjs +15 -3
  49. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  52. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  56. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +36 -21
  58. package/plugins/lisa-copilot/skills/lisa-parity-code-review/SKILL.md +3 -2
  59. package/plugins/lisa-copilot/skills/lisa-parity-code-simplifier/SKILL.md +1 -1
  60. package/plugins/lisa-copilot/skills/lisa-parity-coderabbit/SKILL.md +1 -1
  61. package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +25 -6
  62. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-human.mjs +37 -10
  63. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-setup.mjs +22 -6
  64. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +22 -12
  65. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-workers.mjs +21 -0
  66. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-answer.mjs +76 -21
  67. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +6 -2
  68. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-migrate.mjs +15 -3
  69. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +36 -21
  71. package/plugins/lisa-cursor/skills/lisa-parity-code-review/SKILL.md +3 -2
  72. package/plugins/lisa-cursor/skills/lisa-parity-code-simplifier/SKILL.md +1 -1
  73. package/plugins/lisa-cursor/skills/lisa-parity-coderabbit/SKILL.md +1 -1
  74. package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +25 -6
  75. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-human.mjs +37 -10
  76. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-setup.mjs +22 -6
  77. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +22 -12
  78. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-workers.mjs +21 -0
  79. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-answer.mjs +76 -21
  80. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +6 -2
  81. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-migrate.mjs +15 -3
  82. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  84. package/plugins/lisa-devin/hooks/parity-safety-net.sh +36 -21
  85. package/plugins/lisa-devin/skills/lisa-parity-code-review/SKILL.md +3 -2
  86. package/plugins/lisa-devin/skills/lisa-parity-code-simplifier/SKILL.md +1 -1
  87. package/plugins/lisa-devin/skills/lisa-parity-coderabbit/SKILL.md +1 -1
  88. package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +25 -6
  89. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-human.mjs +37 -10
  90. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-setup.mjs +22 -6
  91. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +22 -12
  92. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-workers.mjs +21 -0
  93. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-answer.mjs +76 -21
  94. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +6 -2
  95. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-migrate.mjs +15 -3
  96. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  98. package/plugins/lisa-expo-agy/plugin.json +1 -1
  99. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  103. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  105. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  106. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  110. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  112. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  113. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  114. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  116. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  117. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  119. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  120. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  123. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  124. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  126. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  127. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  129. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  130. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  131. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  133. package/plugins/lisa-rails-agy/plugin.json +1 -1
  134. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  135. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  136. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  137. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  138. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  139. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  140. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  141. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  143. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  144. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  145. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  146. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  147. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  148. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  149. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  151. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  152. package/plugins/src/base/hooks/parity-safety-net.sh +36 -21
  153. package/plugins/src/base/skills/lisa-parity-code-review/SKILL.md +3 -2
  154. package/plugins/src/base/skills/lisa-parity-code-simplifier/SKILL.md +1 -1
  155. package/plugins/src/base/skills/lisa-parity-coderabbit/SKILL.md +1 -1
  156. package/plugins/src/base/skills/lisa-session-manager/SKILL.md +25 -6
  157. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-human.mjs +37 -10
  158. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-setup.mjs +22 -6
  159. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +22 -12
  160. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-workers.mjs +21 -0
  161. package/plugins/src/base/skills/lisa-session-manager/scripts/human-answer.mjs +76 -21
  162. package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +6 -2
  163. package/plugins/src/base/skills/lisa-session-manager/scripts/token-migrate.mjs +15 -3
  164. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +62 -4
@@ -137,6 +137,16 @@ export async function spawn(ctx) {
137
137
  }
138
138
 
139
139
  const ledger = await readLedger(ctx.root);
140
+ // A spawn no longer clears a pending question (lisa#4571), and a worker
141
+ // under one classifies as waiting, never stalled or dead. So refuse here:
142
+ // the question must be answered and cleared first ("Clearing").
143
+ if (ledger.items[ref]?.pendingQuestion) {
144
+ throw new CliError(
145
+ `${ref} has a pending question in the ledger; nothing was spawned. ` +
146
+ `Answer it and run "Clearing" (ledger clear-question ${ref}) first.`,
147
+ EXIT.SETUP
148
+ );
149
+ }
140
150
  const spec = asSetupError(() => {
141
151
  const name = buildWorkerName(ref, ctx.now(), knownNames(agents, ledger));
142
152
  return buildWorkerSpawn({
@@ -191,6 +201,17 @@ export async function resume(ctx) {
191
201
  const text = stdinText(ctx, "message for the worker");
192
202
  const config = requireConfig(ctx);
193
203
  const item = ledgerItem(await readLedger(ctx.root), ref);
204
+ // A finish keeps a pending question (lisa#4571), so an answer can still
205
+ // arrive for a worker that reported DONE or FAILED. Resuming it would
206
+ // restart a worker the ledger no longer tracks, so refuse: "Clearing"
207
+ // delivers nothing to a finished worker.
208
+ if (item.status === "DONE" || item.status === "FAILED") {
209
+ throw new CliError(
210
+ `${ref} finished (${item.status}); a finished worker is never ` +
211
+ `resumed. Clear its question without delivering it ("Clearing").`,
212
+ EXIT.SETUP
213
+ );
214
+ }
194
215
  if (!item.sessionId && !item.worker) {
195
216
  throw new CliError(`${ref} has no recorded session id to resume.`, 2);
196
217
  }
@@ -16,7 +16,10 @@
16
16
  * answer, and an ordinary reply such as `[x] option 1` is not a machine tag
17
17
  * (#4561). Only then is the author matched, on stable ids only: a GitHub
18
18
  * login or numeric id, a Jira accountId, a Linear user id, never a display
19
- * name or email, which anyone can set. The text is carried as data.
19
+ * name or email, which anyone can set. An all-digit allowed entry is a
20
+ * numeric id and matches only the `id` or `accountId` field, never a login,
21
+ * which its owner chooses and may make all digits (#4571). The text is
22
+ * carried as data.
20
23
  *
21
24
  * The question the answer must follow is the comment whose id the manager
22
25
  * recorded last; with no recorded id, the latest comment that STARTS with
@@ -46,6 +49,17 @@ const COMMENT_ID_KEYS = ["id", "node_id"];
46
49
  /** The author fields that are stable ids, never settable display text. */
47
50
  const STABLE_ID_KEYS = ["login", "id", "accountId"];
48
51
 
52
+ /**
53
+ * The author fields an all-digit allowed entry may match. A GitHub login can
54
+ * be all digits and is chosen by its owner, so a numeric operator id must
55
+ * never be matched against a login: user `583231` would otherwise pass as the
56
+ * operator whose numeric id is 583231 (CodySwannGT/lisa#4571).
57
+ */
58
+ const NUMERIC_ID_KEYS = ["id", "accountId"];
59
+
60
+ /** An allowed entry that is a numeric account id, never a login. */
61
+ const ALL_DIGITS = /^\d+$/;
62
+
49
63
  /**
50
64
  * The string or number values of `keys` on `value`, as strings.
51
65
  * @param {unknown} value - Object to read
@@ -126,37 +140,78 @@ function questionIndex(rows, managerCommentIds, authorOf) {
126
140
  * the manager's latest question comment ({@link questionIndex}) that is not
127
141
  * a machine's ({@link isMachineAuthoredComment}), has text of its own
128
142
  * outside quoted lines (a comment made only of `>` lines quotes someone
129
- * else and answers nothing), and whose author's stable id is allowed. Comments are taken in the order given (chronological).
143
+ * else and answers nothing), whose author's stable id is allowed, and that
144
+ * `refuses` does not refuse. Comments are taken in the order given
145
+ * (chronological).
146
+ *
147
+ * A refused reply (one that looks like it holds a credential) is skipped and
148
+ * reported in `refused`, never returned: returning it made every later cycle
149
+ * pick the same reply and fail on it again, so a corrected reply posted after
150
+ * it was never read (CodySwannGT/lisa#4571).
130
151
  * @param {{comments: unknown, allowed: readonly string[],
131
- * managerCommentIds?: readonly (string|number)[]}} input - The item's
132
- * comments, the answerer ids, and the ids the manager posted
152
+ * managerCommentIds?: readonly (string|number)[],
153
+ * refuses?: (text: string) => string | null | undefined}} input - The
154
+ * item's comments, the answerer ids, the ids the manager posted, and why a
155
+ * reply's text may not be queued (null when it may)
133
156
  * @returns {{answer: {text: string, author: string, at: string|null} | null,
134
- * reason: string}} The answer, or null with why there is none
157
+ * reason: string, refused?: {id: string|null, author: string, why: string}[]}}
158
+ * The answer, or null with why there is none, and (only when any were)
159
+ * the replies refused
135
160
  */
136
- export function pickItemAnswer({ comments, allowed, managerCommentIds = [] }) {
161
+ export function pickItemAnswer({
162
+ comments,
163
+ allowed,
164
+ managerCommentIds = [],
165
+ refuses = () => null,
166
+ }) {
137
167
  if (!Array.isArray(comments)) {
138
168
  throw new Error('stdin must be JSON {"comments":[...]}');
139
169
  }
140
170
  const rows = comments.filter(row => row && typeof row === "object");
141
- const allow = new Set(allowed.map(String));
142
- const authorOf = row =>
143
- idsOf(row.author ?? row.user, STABLE_ID_KEYS).find(id => allow.has(id));
171
+ const allow = allowed.map(String);
172
+ const numeric = new Set(allow.filter(id => ALL_DIGITS.test(id)));
173
+ const named = new Set(allow.filter(id => !ALL_DIGITS.test(id)));
174
+ const authorOf = row => {
175
+ const author = row.author ?? row.user;
176
+ return (
177
+ idsOf(author, NUMERIC_ID_KEYS).find(id => numeric.has(id)) ??
178
+ idsOf(author, STABLE_ID_KEYS).find(id => named.has(id))
179
+ );
180
+ };
144
181
  const asked = questionIndex(rows, managerCommentIds, authorOf);
182
+ const refused = [];
145
183
  if (asked === -1) return { answer: null, reason: "no-question-comment" };
146
- const reply = rows
184
+ const withRefused = result =>
185
+ refused.length > 0 ? { ...result, refused } : result;
186
+ const replies = rows
147
187
  .slice(asked + 1)
148
188
  .filter(row => !isMachineAuthoredComment(row, { managerCommentIds }))
149
- .find(
189
+ .filter(
150
190
  row => authorOf(row) !== undefined && unquoted(bodyOf(row)).trim() !== ""
151
191
  );
152
- if (!reply) return { answer: null, reason: "no-answer-yet" };
153
- const at = reply.createdAt ?? reply.created ?? reply.created_at ?? null;
154
- return {
155
- answer: {
156
- text: bodyOf(reply).trim(),
157
- author: authorOf(reply),
158
- at: typeof at === "string" ? at : null,
159
- },
160
- reason: "answered",
161
- };
192
+ for (const reply of replies) {
193
+ const text = bodyOf(reply).trim();
194
+ const why = refuses(text);
195
+ if (why) {
196
+ refused.push({
197
+ id: idsOf(reply, COMMENT_ID_KEYS)[0] ?? null,
198
+ author: authorOf(reply),
199
+ why,
200
+ });
201
+ continue;
202
+ }
203
+ const at = reply.createdAt ?? reply.created ?? reply.created_at ?? null;
204
+ return withRefused({
205
+ answer: {
206
+ text,
207
+ author: authorOf(reply),
208
+ at: typeof at === "string" ? at : null,
209
+ },
210
+ reason: "answered",
211
+ });
212
+ }
213
+ return withRefused({
214
+ answer: null,
215
+ reason: refused.length > 0 ? "credential-refused" : "no-answer-yet",
216
+ });
162
217
  }
@@ -297,7 +297,9 @@ export function recordSpawn(
297
297
  startedAt: at,
298
298
  lastProgressAt: at,
299
299
  logsHash: null,
300
- pendingQuestion: null,
300
+ // Kept, never reset: only clearPendingQuestion clears a question, once
301
+ // its answer is delivered and the marker removed (lisa#4571).
302
+ pendingQuestion: previous.pendingQuestion ?? null,
301
303
  reason: null,
302
304
  finishedAt: null,
303
305
  attempts,
@@ -354,7 +356,9 @@ export function recordFinish(projectRoot, { ref, status, reason }, seams) {
354
356
  status,
355
357
  reason,
356
358
  finishedAt: at,
357
- pendingQuestion: null,
359
+ // Kept: a question recorded before the finish still holds its answer
360
+ // and the tracker marker the manager must remove (lisa#4571).
361
+ pendingQuestion: previous.pendingQuestion ?? null,
358
362
  history: [
359
363
  ...(previous.history ?? []),
360
364
  { event: "finish", at, status, reason },
@@ -62,8 +62,9 @@ export function digest(value) {
62
62
  * of anything else in it (values are never kept or reported).
63
63
  * @param {string} stateDir - Resolved state dir
64
64
  * @param {Pick<import("node:fs"), "existsSync" | "readFileSync">} fs - Seam
65
- * @returns {{ path: string, token: string | undefined, others: string[] } |
66
- * null} The file, or null when absent
65
+ * @returns {{ path: string, token: string | undefined, tokenLines: number,
66
+ * others: string[] } | null} The file, or null when absent; `token` is the
67
+ * first `TELEGRAM_BOT_TOKEN` line's value and `tokenLines` counts them all
67
68
  */
68
69
  export function readEnvFile(stateDir, fs) {
69
70
  const file = path.join(stateDir, TOKEN_FILE);
@@ -74,10 +75,12 @@ export function readEnvFile(stateDir, fs) {
74
75
  .map((line, at) => ({ line: line.trim(), at }))
75
76
  .filter(({ line }) => line !== "" && !line.startsWith("#"))
76
77
  .map(({ line, at }) => ({ match: ENV_LINE.exec(line), at }));
77
- const token = entries.find(({ match }) => match?.[1] === TOKEN_VAR)?.match;
78
+ const tokenLines = entries.filter(({ match }) => match?.[1] === TOKEN_VAR);
79
+ const token = tokenLines[0]?.match;
78
80
  return {
79
81
  path: file,
80
82
  token: token ? token[2].replace(/^(['"])(.*)\1$/, "$2") : undefined,
83
+ tokenLines: tokenLines.length,
81
84
  others: entries
82
85
  .filter(({ match }) => match?.[1] !== TOKEN_VAR)
83
86
  .map(({ match, at }) => match?.[1] ?? `an unrecognised line ${at + 1}`),
@@ -109,6 +112,15 @@ export function removable(file) {
109
112
  */
110
113
  export function assertMigratable(file) {
111
114
  if (file === null) return;
115
+ // Only the first line's token is verified, and the whole file is deleted
116
+ // after, so a later line would be lost unverified (CodySwannGT/lisa#4571).
117
+ if ((file.tokenLines ?? 0) > 1) {
118
+ throw setupError(
119
+ `${file.path} has ${file.tokenLines} ${TOKEN_VAR} lines, so which ` +
120
+ `token is meant is ambiguous. Keep exactly one, then run store-token ` +
121
+ `again. Nothing was changed.`
122
+ );
123
+ }
112
124
  if (file.others.length > 0) {
113
125
  throw setupError(
114
126
  `${file.path} holds ${file.others.join(", ")} besides ${TOKEN_VAR}, so ` +
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.87.2",
3
+ "version": "4.87.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"