@codyswann/lisa 3.45.9 → 3.45.10
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/all/copy-overwrite/scripts/lisa-floor-collisions.mjs +17 -1
- package/all/copy-overwrite/scripts/lisa-gates.mjs +2 -2
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +54 -12
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +15 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +11 -10
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/expo/copy-overwrite/scripts/bdd/discover.mjs +20 -4
- package/expo/copy-overwrite/scripts/bdd/markdown-cell.mjs +7 -1
- package/expo/copy-overwrite/scripts/bdd/parse.mjs +8 -2
- package/expo/copy-overwrite/scripts/check-e2e-coverage.mjs +8 -2
- package/expo/copy-overwrite/scripts/classify-maestro-failures.mjs +91 -10
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +22 -6
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +10 -1
|
@@ -26,6 +26,22 @@ import * as path from "node:path";
|
|
|
26
26
|
import { byCodeUnit } from "./contract.mjs";
|
|
27
27
|
import { listFiles, posix, resolveInsideRepo } from "./parse.mjs";
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* A path without its trailing slashes.
|
|
31
|
+
*
|
|
32
|
+
* An index scan rather than `.replace(/\/+$/, "")`. A quantified class pinned
|
|
33
|
+
* to `$` is re-attempted from every position before it can fail, which is
|
|
34
|
+
* super-linear in the path's length — S5852. Walking back from the end is one
|
|
35
|
+
* pass and cannot backtrack.
|
|
36
|
+
* @param {string} value - A repo-relative path or prefix.
|
|
37
|
+
* @returns {string} The same path with every trailing `/` removed.
|
|
38
|
+
*/
|
|
39
|
+
const withoutTrailingSlashes = value => {
|
|
40
|
+
let end = value.length;
|
|
41
|
+
while (end > 0 && value[end - 1] === "/") end -= 1;
|
|
42
|
+
return value.slice(0, end);
|
|
43
|
+
};
|
|
44
|
+
|
|
29
45
|
/**
|
|
30
46
|
* Normalize a repo-relative path or path prefix to one comparable spelling.
|
|
31
47
|
*
|
|
@@ -36,10 +52,10 @@ import { listFiles, posix, resolveInsideRepo } from "./parse.mjs";
|
|
|
36
52
|
* @returns {string} Its canonical form; the empty string means the repo root.
|
|
37
53
|
*/
|
|
38
54
|
const canonicalPath = value =>
|
|
39
|
-
posix(String(value))
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
withoutTrailingSlashes(posix(String(value)).replace(/^\.\/+/, "")).replace(
|
|
56
|
+
/^\.$/,
|
|
57
|
+
""
|
|
58
|
+
);
|
|
43
59
|
|
|
44
60
|
/**
|
|
45
61
|
* Whether a repo-relative path lies at or under a path prefix, SEGMENT-WISE.
|
|
@@ -68,6 +68,12 @@ const escapePipeRun = (_match, backslashes) =>
|
|
|
68
68
|
* failures are invisible in review:
|
|
69
69
|
*
|
|
70
70
|
* - Pipes are escaped by RUN, not one at a time — see {@link escapePipeRun}.
|
|
71
|
+
* - `(?<!\\)` in front of that run. Without it the engine may start a run
|
|
72
|
+
* part-way into a longer one, so every position in a backslash run is a
|
|
73
|
+
* fresh attempt and the scan is super-linear in the cell's length — S5852,
|
|
74
|
+
* on author-supplied text. The lookbehind removes no match: a run's
|
|
75
|
+
* leftmost backslash always wins under leftmost-match anyway, so the
|
|
76
|
+
* later starts it forbids were never the ones chosen.
|
|
71
77
|
* - `/\r\n?|\n/` and not `/\r?\n/`. The latter only matches a CR that is
|
|
72
78
|
* FOLLOWED by an LF, so a lone `\r` survives — and CommonMark treats a lone
|
|
73
79
|
* CR as a line ending, which ends the row rather than the cell.
|
|
@@ -93,7 +99,7 @@ const escapePipeRun = (_match, backslashes) =>
|
|
|
93
99
|
*/
|
|
94
100
|
export const cell = text =>
|
|
95
101
|
String(text ?? "")
|
|
96
|
-
.replace(/(\\*)\|/g, escapePipeRun)
|
|
102
|
+
.replace(/(?<!\\)(\\*)\|/g, escapePipeRun)
|
|
97
103
|
.replace(/\r\n?|\n/g, " ");
|
|
98
104
|
|
|
99
105
|
/**
|
|
@@ -163,7 +163,13 @@ export function parseFeatureSource(source, file, platforms) {
|
|
|
163
163
|
pending.push(...tagsOf(trimmed));
|
|
164
164
|
continue;
|
|
165
165
|
}
|
|
166
|
-
|
|
166
|
+
// `(\S.*)` and not `(.+)`, here and on the Scenario line below. `\s*(.+)`
|
|
167
|
+
// lets both halves claim the same run of spaces, so the engine has a
|
|
168
|
+
// choice at every one of them and the match is super-linear in the line's
|
|
169
|
+
// length — S5852, on feature files. `\S` makes the halves disjoint. The
|
|
170
|
+
// subject is already trimmed and the capture is trimmed again, so no line
|
|
171
|
+
// that used to match stops matching.
|
|
172
|
+
const featureMatch = /^Feature:\s*(\S.*)$/.exec(trimmed);
|
|
167
173
|
if (featureMatch) {
|
|
168
174
|
feature = featureMatch[1].trim();
|
|
169
175
|
// Gherkin inherits Feature-level tags down to every scenario in the
|
|
@@ -189,7 +195,7 @@ export function parseFeatureSource(source, file, platforms) {
|
|
|
189
195
|
current = null;
|
|
190
196
|
continue;
|
|
191
197
|
}
|
|
192
|
-
const scenarioMatch = /^Scenario(?: Outline| Template)?:\s*(
|
|
198
|
+
const scenarioMatch = /^Scenario(?: Outline| Template)?:\s*(\S.*)$/.exec(
|
|
193
199
|
trimmed
|
|
194
200
|
);
|
|
195
201
|
if (scenarioMatch) {
|
|
@@ -397,11 +397,17 @@ function flowTags(source) {
|
|
|
397
397
|
for (const line of lines.slice(start + 1)) {
|
|
398
398
|
// The block ends at the document separator, at a non-indented line, or at
|
|
399
399
|
// anything that is not a list item. Each of those is what the regex missed.
|
|
400
|
-
|
|
400
|
+
// The whitespace either side of the item is trimmed in JS rather than by
|
|
401
|
+
// `\s*(.+?)\s*$`. There, three quantifiers compete for the same spaces, so
|
|
402
|
+
// the engine has a choice to make at each one and the match is
|
|
403
|
+
// super-linear in the line's length — S5852, on a workflow file. `(.+)`
|
|
404
|
+
// holds the same "at least one character after the dash" condition the
|
|
405
|
+
// lazy form did, so no line changes its verdict.
|
|
406
|
+
const item = line.match(/^\s+-(.+)$/u);
|
|
401
407
|
if (!item || line.startsWith("---")) {
|
|
402
408
|
break;
|
|
403
409
|
}
|
|
404
|
-
tags.push(item[1].replace(/^["']|["']$/gu, ""));
|
|
410
|
+
tags.push(item[1].trim().replace(/^["']|["']$/gu, ""));
|
|
405
411
|
}
|
|
406
412
|
return tags;
|
|
407
413
|
}
|
|
@@ -149,6 +149,57 @@ const PLACEHOLDER_METHODS = new Set([
|
|
|
149
149
|
"?",
|
|
150
150
|
]);
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* An `id:` line, with everything after the colon captured verbatim.
|
|
154
|
+
*
|
|
155
|
+
* Two deliberate spellings, both there to keep the match linear (S5852):
|
|
156
|
+
* `[ \t]` rather than `\s`, because this is applied to one already-split line
|
|
157
|
+
* and `\s`'s newline class only lets the indentation run overlap the next
|
|
158
|
+
* line's; and `(?:\S.*)?` rather than `.*`, because `[ \t]*(.*)` lets both
|
|
159
|
+
* halves claim the same spaces, which is a choice the engine has to make at
|
|
160
|
+
* every one of them.
|
|
161
|
+
*/
|
|
162
|
+
const ID_LINE = /^[ \t]*id:[ \t]*((?:\S.*)?)$/;
|
|
163
|
+
|
|
164
|
+
/** A `text:` line, read the same way. */
|
|
165
|
+
const TEXT_LINE = /^[ \t]*text:[ \t]*((?:\S.*)?)$/;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* A scalar value with one layer of matching quotes removed.
|
|
169
|
+
*
|
|
170
|
+
* Character comparison rather than `['"]?(...)['"]?`, because the regex form
|
|
171
|
+
* cannot express "the same quote at both ends" without a backreference, and
|
|
172
|
+
* the shape it settled for — a lazy capture between two optional quotes and a
|
|
173
|
+
* trailing `\s*$` — has three quantifiers competing for the same characters.
|
|
174
|
+
* That is super-linear in the line's length (S5852) on flow files.
|
|
175
|
+
* @param {string} value - An already-trimmed scalar.
|
|
176
|
+
* @returns {string} The value without its surrounding quote pair.
|
|
177
|
+
*/
|
|
178
|
+
function unquote(value) {
|
|
179
|
+
const first = value.at(0);
|
|
180
|
+
const isQuote = first === "'" || first === '"';
|
|
181
|
+
return isQuote && value.length > 1 && value.at(-1) === first
|
|
182
|
+
? value.slice(1, -1)
|
|
183
|
+
: value;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The selector a `key: value` line declares, or null when the line is not one.
|
|
188
|
+
*
|
|
189
|
+
* An empty value reads as "no selector here" rather than as the empty
|
|
190
|
+
* selector, which is what the previous `[^'"\n]+?` (one character minimum)
|
|
191
|
+
* also did.
|
|
192
|
+
* @param {string} line - One line of flow YAML.
|
|
193
|
+
* @param {RegExp} pattern - The key's line pattern.
|
|
194
|
+
* @returns {string|null} The selector, or null.
|
|
195
|
+
*/
|
|
196
|
+
function scalarSelector(line, pattern) {
|
|
197
|
+
const match = pattern.exec(line);
|
|
198
|
+
if (!match) return null;
|
|
199
|
+
const value = unquote(match[1].trim());
|
|
200
|
+
return value === "" ? null : value;
|
|
201
|
+
}
|
|
202
|
+
|
|
152
203
|
/**
|
|
153
204
|
* Pull `id:`/`text:` selectors and their ceilings out of a flow's YAML source.
|
|
154
205
|
*
|
|
@@ -178,7 +229,12 @@ export function extractGates(source) {
|
|
|
178
229
|
|
|
179
230
|
for (const line of lines) {
|
|
180
231
|
if (/^\s*#/.test(line) || line.trim() === "") continue;
|
|
181
|
-
|
|
232
|
+
// `(?:-\s*)?` rather than `-?\s*`: with two `\s*` runs either side of an
|
|
233
|
+
// optional dash, both can claim the same spaces and the engine has a
|
|
234
|
+
// choice at every one — super-linear in the line's length (S5852). Tying
|
|
235
|
+
// the dash to the whitespace that follows it removes the choice and
|
|
236
|
+
// recognises exactly the same lines.
|
|
237
|
+
const command = line.match(/^(\s*)(?:-\s*)?(\w+):/);
|
|
182
238
|
if (command && GATE_COMMANDS.includes(command[2])) {
|
|
183
239
|
if (inGate) flush();
|
|
184
240
|
inGate = true;
|
|
@@ -198,18 +254,35 @@ export function extractGates(source) {
|
|
|
198
254
|
timeout = Number(timeoutMatch[1]);
|
|
199
255
|
continue;
|
|
200
256
|
}
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
203
|
-
pending.push({ kind: "id", selector:
|
|
257
|
+
const idSelector = scalarSelector(line, ID_LINE);
|
|
258
|
+
if (idSelector !== null) {
|
|
259
|
+
pending.push({ kind: "id", selector: idSelector });
|
|
204
260
|
continue;
|
|
205
261
|
}
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
262
|
+
const textSelector = scalarSelector(line, TEXT_LINE);
|
|
263
|
+
if (textSelector !== null)
|
|
264
|
+
pending.push({ kind: "text", selector: textSelector });
|
|
208
265
|
}
|
|
209
266
|
if (inGate) flush();
|
|
210
267
|
return gates;
|
|
211
268
|
}
|
|
212
269
|
|
|
270
|
+
/** The extension a referenced flow must carry to be followed. */
|
|
271
|
+
const YAML_SUFFIX = ".yaml";
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* A `runFlow: <path>` line, in the shorthand list-item form.
|
|
275
|
+
*
|
|
276
|
+
* `[ \t]` rather than `\s` throughout, and the dash tied to the whitespace
|
|
277
|
+
* that follows it. Under `m`, a `\s*` run can cross a newline into the next
|
|
278
|
+
* line's own `^`, so the scan re-attempts the same characters from many start
|
|
279
|
+
* positions — super-linear in the file's length (S5852).
|
|
280
|
+
*/
|
|
281
|
+
const INLINE_RUN_FLOW = /^[ \t]*(?:-[ \t]*)?runFlow:[ \t]*['"]?([^'"\s]+)/gm;
|
|
282
|
+
|
|
283
|
+
/** The `file: <path>` line of the expanded `runFlow:` form. */
|
|
284
|
+
const NESTED_RUN_FLOW = /^[ \t]*file:[ \t]*['"]?([^'"\s]+)/gm;
|
|
285
|
+
|
|
213
286
|
/**
|
|
214
287
|
* Resolve the `runFlow:` targets a flow references, as absolute paths.
|
|
215
288
|
* @param {string} source - Flow YAML source
|
|
@@ -219,10 +292,18 @@ export function extractGates(source) {
|
|
|
219
292
|
export function extractRunFlowTargets(source, flowPath) {
|
|
220
293
|
const dir = path.dirname(flowPath);
|
|
221
294
|
const targets = [];
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
295
|
+
// The `.yaml` suffix is checked in JS rather than written as `[^'"\s]+\.yaml`.
|
|
296
|
+
// A greedy class that already contains `.` has to give characters back one at
|
|
297
|
+
// a time before the literal suffix can match, at every start position a `g`
|
|
298
|
+
// scan tries — super-linear in the source's length (S5852), on flow files.
|
|
299
|
+
// The class stops at whitespace or a quote either way, so the token it
|
|
300
|
+
// captures is the same one; only the suffix test moved.
|
|
301
|
+
for (const match of source.matchAll(INLINE_RUN_FLOW))
|
|
302
|
+
if (match[1].endsWith(YAML_SUFFIX))
|
|
303
|
+
targets.push(path.resolve(dir, match[1]));
|
|
304
|
+
for (const match of source.matchAll(NESTED_RUN_FLOW))
|
|
305
|
+
if (match[1].endsWith(YAML_SUFFIX))
|
|
306
|
+
targets.push(path.resolve(dir, match[1]));
|
|
226
307
|
return targets;
|
|
227
308
|
}
|
|
228
309
|
|
package/package.json
CHANGED
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"ws": ">=8.21.0"
|
|
133
133
|
},
|
|
134
134
|
"name": "@codyswann/lisa",
|
|
135
|
-
"version": "3.45.
|
|
135
|
+
"version": "3.45.10",
|
|
136
136
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
137
137
|
"main": "dist/index.js",
|
|
138
138
|
"exports": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.45.
|
|
3
|
+
"version": "3.45.10",
|
|
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": "3.45.
|
|
3
|
+
"version": "3.45.10",
|
|
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": "3.45.
|
|
3
|
+
"version": "3.45.10",
|
|
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": "3.45.
|
|
3
|
+
"version": "3.45.10",
|
|
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": "3.45.
|
|
3
|
+
"version": "3.45.10",
|
|
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"
|