@codyswann/lisa 3.33.5 → 3.33.7
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/check-state-classification.mjs +18 -4
- package/all/copy-overwrite/scripts/lisa-gates.mjs +47 -46
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +3 -2
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +284 -2
- package/dist/configs/eslint/base.d.ts +21 -0
- package/dist/configs/eslint/base.d.ts.map +1 -1
- package/dist/configs/eslint/base.js +78 -2
- package/dist/configs/eslint/base.js.map +1 -1
- package/dist/configs/eslint/cdk.d.ts.map +1 -1
- package/dist/configs/eslint/cdk.js +5 -1
- package/dist/configs/eslint/cdk.js.map +1 -1
- package/dist/configs/eslint/expo.d.ts.map +1 -1
- package/dist/configs/eslint/expo.js +5 -1
- package/dist/configs/eslint/expo.js.map +1 -1
- package/dist/configs/eslint/harper-fabric.d.ts.map +1 -1
- package/dist/configs/eslint/harper-fabric.js +6 -1
- package/dist/configs/eslint/harper-fabric.js.map +1 -1
- package/dist/configs/eslint/nestjs.d.ts.map +1 -1
- package/dist/configs/eslint/nestjs.js +5 -1
- package/dist/configs/eslint/nestjs.js.map +1 -1
- package/dist/configs/eslint/phaser.d.ts.map +1 -1
- package/dist/configs/eslint/phaser.js +6 -1
- package/dist/configs/eslint/phaser.js.map +1 -1
- package/dist/configs/eslint/typescript.d.ts +2 -2
- package/dist/configs/eslint/typescript.d.ts.map +1 -1
- package/dist/configs/eslint/typescript.js +6 -2
- package/dist/configs/eslint/typescript.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +12 -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 +36 -35
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/expo/copy-overwrite/scripts/bdd/baseline.mjs +11 -4
- package/expo/copy-overwrite/scripts/bdd/discover.mjs +12 -6
- package/expo/copy-overwrite/scripts/bdd/waivers.mjs +7 -4
- package/expo/copy-overwrite/scripts/bdd-matrix.mjs +1 -0
- package/expo/copy-overwrite/scripts/check-bdd-coverage.mjs +26 -17
- package/expo/copy-overwrite/scripts/check-e2e-coverage.mjs +12 -8
- 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/.codex-plugin/skills/lisa-git-submit-pr/SKILL.md +11 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-github-sync/SKILL.md +7 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-sync/SKILL.md +7 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-sync/SKILL.md +7 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-sync/SKILL.md +7 -2
- package/plugins/lisa/skills/lisa-git-submit-pr/SKILL.md +11 -4
- package/plugins/lisa/skills/lisa-github-sync/SKILL.md +7 -2
- package/plugins/lisa/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jira-sync/SKILL.md +7 -2
- package/plugins/lisa/skills/lisa-linear-sync/SKILL.md +7 -2
- package/plugins/lisa/skills/lisa-tracker-sync/SKILL.md +7 -2
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-git-submit-pr/SKILL.md +11 -4
- package/plugins/lisa-agy/skills/lisa-github-sync/SKILL.md +7 -2
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jira-sync/SKILL.md +7 -2
- package/plugins/lisa-agy/skills/lisa-linear-sync/SKILL.md +7 -2
- package/plugins/lisa-agy/skills/lisa-tracker-sync/SKILL.md +7 -2
- 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-copilot/skills/lisa-git-submit-pr/SKILL.md +11 -4
- package/plugins/lisa-copilot/skills/lisa-github-sync/SKILL.md +7 -2
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jira-sync/SKILL.md +7 -2
- package/plugins/lisa-copilot/skills/lisa-linear-sync/SKILL.md +7 -2
- package/plugins/lisa-copilot/skills/lisa-tracker-sync/SKILL.md +7 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-git-submit-pr/SKILL.md +11 -4
- package/plugins/lisa-cursor/skills/lisa-github-sync/SKILL.md +7 -2
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jira-sync/SKILL.md +7 -2
- package/plugins/lisa-cursor/skills/lisa-linear-sync/SKILL.md +7 -2
- package/plugins/lisa-cursor/skills/lisa-tracker-sync/SKILL.md +7 -2
- 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/plugins/src/base/skills/lisa-git-submit-pr/SKILL.md +11 -4
- package/plugins/src/base/skills/lisa-github-sync/SKILL.md +7 -2
- package/plugins/src/base/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jira-sync/SKILL.md +7 -2
- package/plugins/src/base/skills/lisa-linear-sync/SKILL.md +7 -2
- package/plugins/src/base/skills/lisa-tracker-sync/SKILL.md +7 -2
- package/scripts/check-conflict-markers.mjs +1 -1
- package/scripts/check-required-check-promotions.mjs +8 -4
- package/scripts/detect-stale-workflow-inputs.mjs +1 -3
- package/scripts/fix-namespace-test-assertions.mjs +1 -1
- package/scripts/generate-agy-plugin-artifacts.mjs +12 -7
- package/scripts/generate-codex-plugin-artifacts.mjs +14 -9
- package/scripts/generate-copilot-plugin-artifacts.mjs +1 -1
- package/scripts/generate-cursor-plugin-artifacts.mjs +2 -2
- package/scripts/generate-upstream-evidence-manifest.mjs +1 -1
- package/scripts/lisa-assert-eas-profile.mjs +3 -3
- package/scripts/plugin-parity-drift.mjs +29 -13
- package/scripts/plugin-routing-validate.mjs +8 -2
- package/scripts/update-node-version.ts +7 -3
- package/scripts/verify-health-agentic-built.mjs +27 -20
- package/scripts/verify-health-consumer-built.mjs +7 -3
- package/scripts/verify-health-deterministic-built.mjs +24 -19
- package/typescript/copy-overwrite/eslint.ignore.config.json +0 -2
- package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +9 -4
- package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +1 -3
|
@@ -113,9 +113,25 @@ const PERSISTENCE_SIGNALS = Object.freeze([
|
|
|
113
113
|
{ kind: "schema-definition", pattern: /(^|\/)db\/schema\.[cm]?[jt]s$/iu },
|
|
114
114
|
]);
|
|
115
115
|
|
|
116
|
+
// The `CREATE TABLE` grammar, one clause per name. Written as fragments rather
|
|
117
|
+
// than one literal because the literal was unreadable — the alternations are
|
|
118
|
+
// SQL's, and loosening any of them into a wildcard would make the subtraction
|
|
119
|
+
// detector match statements that are not entity declarations.
|
|
120
|
+
/** `CREATE [UNLOGGED|TEMPORARY|TEMP|GLOBAL|LOCAL] ...` modifier run. */
|
|
121
|
+
const CREATE_MODIFIERS =
|
|
122
|
+
"(?:unlogged\\s+|temporary\\s+|temp\\s+|global\\s+|local\\s+)*";
|
|
123
|
+
/** The object kinds that declare durable state. */
|
|
124
|
+
const CREATE_OBJECT_KIND = "(?:materialized\\s+view|table|view)";
|
|
125
|
+
/** Optional `IF NOT EXISTS` guard. */
|
|
126
|
+
const OPTIONAL_IF_NOT_EXISTS = "(?:if\\s+not\\s+exists\\s+)?";
|
|
127
|
+
/** A possibly schema-qualified, possibly quoted identifier. */
|
|
128
|
+
const ENTITY_NAME = '([\\w."`[\\]]+)';
|
|
129
|
+
|
|
116
130
|
/** `CREATE TABLE`-shaped declarations the subtraction detector recognizes. */
|
|
117
|
-
const CREATE_ENTITY_PATTERN =
|
|
118
|
-
|
|
131
|
+
const CREATE_ENTITY_PATTERN = new RegExp(
|
|
132
|
+
`create\\s+${CREATE_MODIFIERS}${CREATE_OBJECT_KIND}\\s+${OPTIONAL_IF_NOT_EXISTS}${ENTITY_NAME}`,
|
|
133
|
+
"giu"
|
|
134
|
+
);
|
|
119
135
|
|
|
120
136
|
/**
|
|
121
137
|
* Read a JSON file, returning a discriminated result rather than throwing.
|
|
@@ -277,11 +293,9 @@ export function compareInventory({
|
|
|
277
293
|
}) {
|
|
278
294
|
const findings = [];
|
|
279
295
|
const entities = contract.entities ?? [];
|
|
280
|
-
const byBareName = new Map();
|
|
281
296
|
const classified = new Map();
|
|
282
297
|
for (const entity of entities) {
|
|
283
298
|
classified.set(entity.id, entity);
|
|
284
|
-
byBareName.set(bareName(entity.id), entity);
|
|
285
299
|
}
|
|
286
300
|
const waived = new Map(
|
|
287
301
|
(contract.waivers ?? []).map(waiver => [waiver.id, waiver])
|
|
@@ -71,17 +71,24 @@ export const LEVELS = ["required", "optional", "off"];
|
|
|
71
71
|
/** What to do when a gate reports success without evidence of work. */
|
|
72
72
|
export const HOLLOW_RESPONSES = ["report", "wait", "block"];
|
|
73
73
|
|
|
74
|
+
// Moment names, named once. They appear in a dozen moment lists below; a
|
|
75
|
+
// literal repeated that many times is one typo away from a gate that silently
|
|
76
|
+
// never runs, and there is no test that can tell "gate not configured for this
|
|
77
|
+
// moment" from "moment misspelled".
|
|
78
|
+
const SESSION_START = "session-start";
|
|
79
|
+
const PRE_TOOL = "pre-tool";
|
|
80
|
+
const COMMIT = "commit";
|
|
81
|
+
const PUSH = "push";
|
|
82
|
+
const PULL_REQUEST = "pull-request";
|
|
83
|
+
const PRE_DEPLOY = "pre-deploy";
|
|
84
|
+
const POST_DEPLOY = "post-deploy";
|
|
85
|
+
const CONTINUOUS = "continuous";
|
|
86
|
+
|
|
74
87
|
/** Fixed moments. Two more families take an environment suffix. */
|
|
75
|
-
export const MOMENTS = [
|
|
76
|
-
"session-start",
|
|
77
|
-
"pre-tool",
|
|
78
|
-
"commit",
|
|
79
|
-
"push",
|
|
80
|
-
"pull-request",
|
|
81
|
-
];
|
|
88
|
+
export const MOMENTS = [SESSION_START, PRE_TOOL, COMMIT, PUSH, PULL_REQUEST];
|
|
82
89
|
|
|
83
90
|
/** Moment families that take an `:<environment>` suffix. */
|
|
84
|
-
export const MOMENT_FAMILIES = [
|
|
91
|
+
export const MOMENT_FAMILIES = [PRE_DEPLOY, POST_DEPLOY, CONTINUOUS];
|
|
85
92
|
|
|
86
93
|
/**
|
|
87
94
|
* Moments that gate a *state* rather than a change.
|
|
@@ -99,7 +106,7 @@ export const MOMENT_FAMILIES = ["pre-deploy", "post-deploy", "continuous"];
|
|
|
99
106
|
* new cases against a stable one. It applies equally to a CVE published today,
|
|
100
107
|
* which makes yesterday's dependency scan wrong with no change at all.
|
|
101
108
|
*/
|
|
102
|
-
export const STATE_FAMILIES = [
|
|
109
|
+
export const STATE_FAMILIES = [CONTINUOUS];
|
|
103
110
|
|
|
104
111
|
/** Keys on a gate entry that are settings rather than moments. */
|
|
105
112
|
export const GATE_FIELDS = new Set(["run", "needs", "task"]);
|
|
@@ -130,19 +137,12 @@ export const GATE_FIELDS = new Set(["run", "needs", "task"]);
|
|
|
130
137
|
/** Prefix marking a gate, or a config key, that this project invented. */
|
|
131
138
|
export const CUSTOM_PREFIX = "x-";
|
|
132
139
|
|
|
133
|
-
const COMMIT_ONWARD = [
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
];
|
|
140
|
-
const PUSH_ONWARD = ["push", "pull-request", "pre-deploy", "post-deploy"];
|
|
141
|
-
const PR_ONWARD = ["pull-request", "pre-deploy", "post-deploy"];
|
|
142
|
-
const PR_ONLY = ["pull-request"];
|
|
143
|
-
const DEPLOY_ONLY = ["pre-deploy", "post-deploy", "continuous"];
|
|
144
|
-
const CONTINUOUS_ONLY = ["continuous"];
|
|
145
|
-
const SESSION_ONWARD = ["session-start", ...COMMIT_ONWARD];
|
|
140
|
+
const COMMIT_ONWARD = [COMMIT, PUSH, PULL_REQUEST, PRE_DEPLOY, POST_DEPLOY];
|
|
141
|
+
const PUSH_ONWARD = [PUSH, PULL_REQUEST, PRE_DEPLOY, POST_DEPLOY];
|
|
142
|
+
const PR_ONWARD = [PULL_REQUEST, PRE_DEPLOY, POST_DEPLOY];
|
|
143
|
+
const PR_ONLY = [PULL_REQUEST];
|
|
144
|
+
const DEPLOY_ONLY = [PRE_DEPLOY, POST_DEPLOY, CONTINUOUS];
|
|
145
|
+
const SESSION_ONWARD = [SESSION_START, ...COMMIT_ONWARD];
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Lisa's canonical gates.
|
|
@@ -231,14 +231,14 @@ export const REGISTRY = Object.freeze({
|
|
|
231
231
|
label: "🎭 Playwright E2E Tests",
|
|
232
232
|
summary: "Browser journeys pass end to end.",
|
|
233
233
|
task: "test:e2e",
|
|
234
|
-
moments: [...PR_ONWARD,
|
|
234
|
+
moments: [...PR_ONWARD, CONTINUOUS],
|
|
235
235
|
work: "specs run",
|
|
236
236
|
},
|
|
237
237
|
"e2e-native": {
|
|
238
238
|
label: "📱 Maestro Native E2E",
|
|
239
239
|
summary: "Native device journeys pass end to end.",
|
|
240
240
|
task: "test:e2e:native",
|
|
241
|
-
moments: [...PR_ONWARD,
|
|
241
|
+
moments: [...PR_ONWARD, CONTINUOUS],
|
|
242
242
|
work: "flows run",
|
|
243
243
|
},
|
|
244
244
|
// ---------------------------------------------------------------------
|
|
@@ -268,7 +268,7 @@ export const REGISTRY = Object.freeze({
|
|
|
268
268
|
summary:
|
|
269
269
|
"The environment reset exists, and its guard cannot be bypassed by calling it directly.",
|
|
270
270
|
task: "environment:reset:verify",
|
|
271
|
-
moments: [...PR_ONWARD,
|
|
271
|
+
moments: [...PR_ONWARD, CONTINUOUS],
|
|
272
272
|
work: "refusals proved",
|
|
273
273
|
},
|
|
274
274
|
"environment-reseed": {
|
|
@@ -276,7 +276,7 @@ export const REGISTRY = Object.freeze({
|
|
|
276
276
|
summary:
|
|
277
277
|
"The environment reseed exists, and its guard cannot be bypassed by calling it directly.",
|
|
278
278
|
task: "environment:reseed:verify",
|
|
279
|
-
moments: [...PR_ONWARD,
|
|
279
|
+
moments: [...PR_ONWARD, CONTINUOUS],
|
|
280
280
|
work: "refusals proved",
|
|
281
281
|
},
|
|
282
282
|
"generative-testing": {
|
|
@@ -288,7 +288,7 @@ export const REGISTRY = Object.freeze({
|
|
|
288
288
|
// re-running one suite against every change explores far less of the input
|
|
289
289
|
// space than generating new cases against a stable one." A property suite
|
|
290
290
|
// pinned to the per-change gate re-walks the same ground forever.
|
|
291
|
-
moments: [...PR_ONWARD,
|
|
291
|
+
moments: [...PR_ONWARD, CONTINUOUS],
|
|
292
292
|
work: "cases generated",
|
|
293
293
|
},
|
|
294
294
|
"structural-rules": {
|
|
@@ -322,14 +322,14 @@ export const REGISTRY = Object.freeze({
|
|
|
322
322
|
task: "security:audit",
|
|
323
323
|
// Continuous matters most here: a CVE published today makes yesterday's
|
|
324
324
|
// green wrong with no change to trigger a re-scan.
|
|
325
|
-
moments: [...PUSH_ONWARD,
|
|
325
|
+
moments: [...PUSH_ONWARD, CONTINUOUS],
|
|
326
326
|
work: "manifests scanned",
|
|
327
327
|
},
|
|
328
328
|
"static-security": {
|
|
329
329
|
label: "🔍 Static Security Analysis",
|
|
330
330
|
summary: "Static analysis finds no security defect.",
|
|
331
331
|
task: "security:sast",
|
|
332
|
-
moments: [...PR_ONWARD,
|
|
332
|
+
moments: [...PR_ONWARD, CONTINUOUS],
|
|
333
333
|
work: "files analysed",
|
|
334
334
|
},
|
|
335
335
|
"runtime-web-vulnerability": {
|
|
@@ -349,7 +349,7 @@ export const REGISTRY = Object.freeze({
|
|
|
349
349
|
label: "👁️ Code Review",
|
|
350
350
|
summary: "The change was reviewed by something that read it.",
|
|
351
351
|
task: "review:local",
|
|
352
|
-
moments: [
|
|
352
|
+
moments: [PUSH, PULL_REQUEST],
|
|
353
353
|
},
|
|
354
354
|
"performance-budget": {
|
|
355
355
|
label: "⚡ Performance Budget",
|
|
@@ -768,9 +768,9 @@ function validateGate(id, gate) {
|
|
|
768
768
|
...Object.keys(INTERCEPTORS),
|
|
769
769
|
]);
|
|
770
770
|
return [
|
|
771
|
-
`gates."${id}" is not a gate Lisa knows
|
|
772
|
-
|
|
773
|
-
|
|
771
|
+
`gates."${id}" is not a gate Lisa knows${
|
|
772
|
+
near ? `. Did you mean "${near}"?` : ""
|
|
773
|
+
} Prefix a gate of your own with "${CUSTOM_PREFIX}" — Lisa will run ` +
|
|
774
774
|
`it without pretending to understand it.`,
|
|
775
775
|
];
|
|
776
776
|
}
|
|
@@ -834,7 +834,7 @@ function validateMoment(id, moment, value, known, interceptor, gateRun) {
|
|
|
834
834
|
);
|
|
835
835
|
}
|
|
836
836
|
if (entry.await) {
|
|
837
|
-
if ([
|
|
837
|
+
if ([COMMIT, PUSH, SESSION_START, PRE_TOOL].includes(moment)) {
|
|
838
838
|
problems.push(
|
|
839
839
|
`gates."${id}"."${moment}" awaits "${entry.await}", but there is no ` +
|
|
840
840
|
`pull request yet for a signal to post against. An awaited check ` +
|
|
@@ -953,8 +953,9 @@ export function validatePolicy(policy) {
|
|
|
953
953
|
if (!Object.hasOwn(POLICY_SCHEMA, section)) {
|
|
954
954
|
const near = nearest(section, Object.keys(POLICY_SCHEMA));
|
|
955
955
|
problems.push(
|
|
956
|
-
`policy."${section}" is not a policy section Lisa knows
|
|
957
|
-
|
|
956
|
+
`policy."${section}" is not a policy section Lisa knows${
|
|
957
|
+
near ? `. Did you mean "${near}"?` : ""
|
|
958
|
+
}`
|
|
958
959
|
);
|
|
959
960
|
continue;
|
|
960
961
|
}
|
|
@@ -967,8 +968,9 @@ export function validatePolicy(policy) {
|
|
|
967
968
|
if (!expected) {
|
|
968
969
|
const near = nearest(field, Object.keys(POLICY_SCHEMA[section]));
|
|
969
970
|
problems.push(
|
|
970
|
-
`policy.${section}."${field}" is not a setting Lisa manages
|
|
971
|
-
|
|
971
|
+
`policy.${section}."${field}" is not a setting Lisa manages${
|
|
972
|
+
near ? `. Did you mean "${near}"?` : ""
|
|
973
|
+
}`
|
|
972
974
|
);
|
|
973
975
|
continue;
|
|
974
976
|
}
|
|
@@ -1012,10 +1014,9 @@ export function auditConfigKeys(config) {
|
|
|
1012
1014
|
findings.push({
|
|
1013
1015
|
key,
|
|
1014
1016
|
level: "warn",
|
|
1015
|
-
message:
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
` Nothing consumes it, so whatever it was meant to configure is unset.`,
|
|
1017
|
+
message: `${key} is not a key Lisa reads${
|
|
1018
|
+
near ? `. Did you mean "${near}"?` : ""
|
|
1019
|
+
} Nothing consumes it, so whatever it was meant to configure is unset.`,
|
|
1019
1020
|
});
|
|
1020
1021
|
}
|
|
1021
1022
|
return findings;
|
|
@@ -1050,7 +1051,7 @@ export function resolveMoment({
|
|
|
1050
1051
|
// for a real moment; it is refused only for one that cannot exist.
|
|
1051
1052
|
if (!isMoment(moment)) {
|
|
1052
1053
|
throw new Error(
|
|
1053
|
-
`"${moment}" ${unknownMomentMessage(moment)}
|
|
1054
|
+
`"${moment}" ${unknownMomentMessage(moment)} ${knownMomentsMessage()}`
|
|
1054
1055
|
);
|
|
1055
1056
|
}
|
|
1056
1057
|
assertValidMomentKeys(gates);
|
|
@@ -1184,7 +1185,7 @@ export function needsAt({ gates, moment }) {
|
|
|
1184
1185
|
*/
|
|
1185
1186
|
export function contextsFor(gates, options = {}) {
|
|
1186
1187
|
const {
|
|
1187
|
-
moment =
|
|
1188
|
+
moment = PULL_REQUEST,
|
|
1188
1189
|
workflowName = "🔍 Quality Checks",
|
|
1189
1190
|
previousLabels = [],
|
|
1190
1191
|
} = options;
|
|
@@ -1347,7 +1348,7 @@ function main() {
|
|
|
1347
1348
|
}
|
|
1348
1349
|
|
|
1349
1350
|
if (command === "needs") {
|
|
1350
|
-
const moment = flag("moment") ??
|
|
1351
|
+
const moment = flag("moment") ?? SESSION_START;
|
|
1351
1352
|
console.log(JSON.stringify(needsAt({ gates, moment }), null, 2));
|
|
1352
1353
|
return;
|
|
1353
1354
|
}
|
|
@@ -1360,7 +1361,7 @@ function main() {
|
|
|
1360
1361
|
console.log(
|
|
1361
1362
|
JSON.stringify(
|
|
1362
1363
|
contextsFor(gates, {
|
|
1363
|
-
moment: flag("moment") ??
|
|
1364
|
+
moment: flag("moment") ?? PULL_REQUEST,
|
|
1364
1365
|
workflowName: flag("workflow") ?? "🔍 Quality Checks",
|
|
1365
1366
|
previousLabels,
|
|
1366
1367
|
}),
|
|
@@ -305,8 +305,9 @@ function summarise(result) {
|
|
|
305
305
|
`(optional), ${result.skipped.length} not provable here`;
|
|
306
306
|
lines.push(
|
|
307
307
|
`${failedOptional.length ? "⚠️ " : "✅"} ${result.moment}: ${counts}, ` +
|
|
308
|
-
`of ${result.total} gate(s) declared
|
|
309
|
-
|
|
308
|
+
`of ${result.total} gate(s) declared.${
|
|
309
|
+
failedOptional.length ? " See the optional failure(s) above." : ""
|
|
310
|
+
}`
|
|
310
311
|
);
|
|
311
312
|
return lines;
|
|
312
313
|
}
|
|
@@ -25,6 +25,14 @@ const RELEASE_SUBJECT =
|
|
|
25
25
|
/^chore\(release\): \d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)? \[skip ci\]$/;
|
|
26
26
|
const ZERO_OID = /^0+$/;
|
|
27
27
|
const MARKER = "[lisa-pr-link]";
|
|
28
|
+
/**
|
|
29
|
+
* The command that establishes the ticket-side backlink.
|
|
30
|
+
*
|
|
31
|
+
* Named in every refusal that the backlink is missing. A validator that
|
|
32
|
+
* reports "no verified backlink" without the remedy sends the reader looking
|
|
33
|
+
* for a producer that, until this command existed, was prose in a SKILL.md.
|
|
34
|
+
*/
|
|
35
|
+
const BACKLINK_COMMAND = "node scripts/lisa-work-item.mjs backlink";
|
|
28
36
|
/**
|
|
29
37
|
* The prefix only — an anchored, fixed-length literal with no quantifier, so
|
|
30
38
|
* it cannot backtrack. The ReDoS was never here; it was in the `\s*(.+?)\s*$`
|
|
@@ -1217,6 +1225,247 @@ export function textContainsBacklink(value, prUrl) {
|
|
|
1217
1225
|
return false;
|
|
1218
1226
|
}
|
|
1219
1227
|
|
|
1228
|
+
/**
|
|
1229
|
+
* The managed backlink comment's body.
|
|
1230
|
+
*
|
|
1231
|
+
* One line, and the whole body — so establishing the backlink is a whole-body
|
|
1232
|
+
* replace rather than an append, which is what makes a rerun converge instead
|
|
1233
|
+
* of accumulating. Nothing else is carried here on purpose: the milestone and
|
|
1234
|
+
* the merge SHA belong to the vendor sync skills' progress notes, and putting
|
|
1235
|
+
* them in this body would make every rerun a content change.
|
|
1236
|
+
* @param {string} prUrl Pull request URL.
|
|
1237
|
+
* @returns {string} The comment body.
|
|
1238
|
+
*/
|
|
1239
|
+
function backlinkBody(prUrl) {
|
|
1240
|
+
return `${MARKER} ${prUrl}`;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/**
|
|
1244
|
+
* Whether a comment payload is Lisa's managed backlink comment.
|
|
1245
|
+
*
|
|
1246
|
+
* Shape-agnostic deliberately: GitHub and Linear return a plain string body,
|
|
1247
|
+
* Jira returns an Atlassian Document Format tree. Serialising covers all three
|
|
1248
|
+
* without a per-provider walker, and the marker is distinctive enough that a
|
|
1249
|
+
* false positive would have to be a comment quoting it verbatim — which is
|
|
1250
|
+
* still Lisa's comment to reuse rather than a second one to add.
|
|
1251
|
+
* @param {unknown} body Comment body in whatever shape the provider returned.
|
|
1252
|
+
* @returns {boolean} True when this is the managed comment.
|
|
1253
|
+
*/
|
|
1254
|
+
function carriesMarker(body) {
|
|
1255
|
+
return JSON.stringify(body ?? "").includes(MARKER);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Establish the backlink on a GitHub issue.
|
|
1260
|
+
* @param {string} ref Canonical `owner/repo#number` reference.
|
|
1261
|
+
* @param {string} prUrl Pull request URL.
|
|
1262
|
+
* @returns {string} What changed: created, updated, or unchanged.
|
|
1263
|
+
*/
|
|
1264
|
+
function githubBacklink(ref, prUrl) {
|
|
1265
|
+
const [repository, number] = ref.split("#");
|
|
1266
|
+
const listing = run(
|
|
1267
|
+
"gh",
|
|
1268
|
+
[
|
|
1269
|
+
"api",
|
|
1270
|
+
"--paginate",
|
|
1271
|
+
`repos/${repository}/issues/${number}/comments?per_page=100`,
|
|
1272
|
+
],
|
|
1273
|
+
{ allowFailure: true }
|
|
1274
|
+
);
|
|
1275
|
+
if (listing.status !== 0) throw githubFailure(listing, ref);
|
|
1276
|
+
const comments = safeJson(listing.stdout, `GitHub comments on ${ref}`);
|
|
1277
|
+
const managed = (Array.isArray(comments) ? comments : []).find(comment =>
|
|
1278
|
+
carriesMarker(comment?.body)
|
|
1279
|
+
);
|
|
1280
|
+
const body = backlinkBody(prUrl);
|
|
1281
|
+
if (managed && managed.body === body) return "unchanged";
|
|
1282
|
+
const [method, endpoint] = managed
|
|
1283
|
+
? ["PATCH", `repos/${repository}/issues/comments/${managed.id}`]
|
|
1284
|
+
: ["POST", `repos/${repository}/issues/${number}/comments`];
|
|
1285
|
+
run("gh", ["api", "--method", method, endpoint, "--field", `body=${body}`], {
|
|
1286
|
+
error: `could not write the backlink comment on ${ref}`,
|
|
1287
|
+
});
|
|
1288
|
+
return managed ? "updated" : "created";
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
/**
|
|
1292
|
+
* Send one Linear GraphQL document, refusing on either failure surface.
|
|
1293
|
+
*
|
|
1294
|
+
* GraphQL answers an invalid mutation with HTTP 200 and an `errors` array, so
|
|
1295
|
+
* a status-only check reports success for a comment that was never written.
|
|
1296
|
+
* @param {string} token Linear API key.
|
|
1297
|
+
* @param {string} query The document.
|
|
1298
|
+
* @param {object} variables Its variables.
|
|
1299
|
+
* @param {string} context What the call was for, for the message.
|
|
1300
|
+
* @returns {object} The `data` payload.
|
|
1301
|
+
*/
|
|
1302
|
+
function linearGraphql(token, query, variables, context) {
|
|
1303
|
+
const result = secureCurl(
|
|
1304
|
+
["https://api.linear.app/graphql"],
|
|
1305
|
+
[
|
|
1306
|
+
["request", "POST"],
|
|
1307
|
+
["header", "Content-Type: application/json"],
|
|
1308
|
+
["header", `Authorization: ${token}`],
|
|
1309
|
+
["data-binary", JSON.stringify({ query, variables })],
|
|
1310
|
+
],
|
|
1311
|
+
{ allowFailure: true }
|
|
1312
|
+
);
|
|
1313
|
+
if (result.status !== 0) throw new TrackingError(`${context} failed`);
|
|
1314
|
+
const response = safeJson(result.stdout, context);
|
|
1315
|
+
if (Array.isArray(response.errors) && response.errors.length > 0)
|
|
1316
|
+
throw new TrackingError(
|
|
1317
|
+
`${context} failed: ${response.errors[0]?.message}`
|
|
1318
|
+
);
|
|
1319
|
+
return response.data ?? {};
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* Establish the backlink on a Linear issue.
|
|
1324
|
+
* @param {string} ref Canonical `KEY-123` identifier.
|
|
1325
|
+
* @param {string} prUrl Pull request URL.
|
|
1326
|
+
* @param {object} contract Resolved tracker contract.
|
|
1327
|
+
* @returns {string} What changed: created, updated, or unchanged.
|
|
1328
|
+
*/
|
|
1329
|
+
function linearBacklink(ref, prUrl, contract) {
|
|
1330
|
+
const token = readLinearKey(contract.workspace);
|
|
1331
|
+
if (!token)
|
|
1332
|
+
throw new TrackingError(
|
|
1333
|
+
"writing a Linear backlink requires LINEAR_API_KEY or a lisa-linear keychain entry"
|
|
1334
|
+
);
|
|
1335
|
+
const issue = linearGraphql(
|
|
1336
|
+
token,
|
|
1337
|
+
"query($id:String!){issue(id:$id){id comments{nodes{id body}}}}",
|
|
1338
|
+
{ id: ref },
|
|
1339
|
+
`Linear issue ${ref} lookup`
|
|
1340
|
+
).issue;
|
|
1341
|
+
if (!issue?.id)
|
|
1342
|
+
throw new TrackingError(
|
|
1343
|
+
`Linear issue ${ref} does not exist or is inaccessible`
|
|
1344
|
+
);
|
|
1345
|
+
const managed = (issue.comments?.nodes ?? []).find(comment =>
|
|
1346
|
+
carriesMarker(comment?.body)
|
|
1347
|
+
);
|
|
1348
|
+
const body = backlinkBody(prUrl);
|
|
1349
|
+
if (managed && managed.body === body) return "unchanged";
|
|
1350
|
+
if (managed) {
|
|
1351
|
+
linearGraphql(
|
|
1352
|
+
token,
|
|
1353
|
+
"mutation($id:String!,$body:String!){commentUpdate(id:$id,input:{body:$body}){success}}",
|
|
1354
|
+
{ body, id: managed.id },
|
|
1355
|
+
`Linear backlink update on ${ref}`
|
|
1356
|
+
);
|
|
1357
|
+
return "updated";
|
|
1358
|
+
}
|
|
1359
|
+
linearGraphql(
|
|
1360
|
+
token,
|
|
1361
|
+
"mutation($id:String!,$body:String!){commentCreate(input:{issueId:$id,body:$body}){success}}",
|
|
1362
|
+
{ body, id: issue.id },
|
|
1363
|
+
`Linear backlink comment on ${ref}`
|
|
1364
|
+
);
|
|
1365
|
+
return "created";
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* The managed comment as an Atlassian Document Format tree.
|
|
1370
|
+
*
|
|
1371
|
+
* A single text node, so the marker and the URL land in one string — which is
|
|
1372
|
+
* what `textContainsBacklink` walks the tree looking for. Splitting them across
|
|
1373
|
+
* nodes would write a comment the reader accepts visually and the check
|
|
1374
|
+
* rejects.
|
|
1375
|
+
* @param {string} prUrl Pull request URL.
|
|
1376
|
+
* @returns {object} The ADF document.
|
|
1377
|
+
*/
|
|
1378
|
+
function jiraCommentDocument(prUrl) {
|
|
1379
|
+
return {
|
|
1380
|
+
content: [
|
|
1381
|
+
{
|
|
1382
|
+
content: [{ text: backlinkBody(prUrl), type: "text" }],
|
|
1383
|
+
type: "paragraph",
|
|
1384
|
+
},
|
|
1385
|
+
],
|
|
1386
|
+
type: "doc",
|
|
1387
|
+
version: 1,
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* Establish the backlink on a Jira ticket.
|
|
1393
|
+
*
|
|
1394
|
+
* Requires API credentials rather than falling back to `acli`: the read path
|
|
1395
|
+
* may degrade to whatever can answer, but a write that silently does not
|
|
1396
|
+
* happen is exactly the failure this command exists to remove.
|
|
1397
|
+
* @param {string} ref Canonical `KEY-123` reference.
|
|
1398
|
+
* @param {string} prUrl Pull request URL.
|
|
1399
|
+
* @param {object} contract Resolved tracker contract.
|
|
1400
|
+
* @returns {string} What changed: created, updated, or unchanged.
|
|
1401
|
+
*/
|
|
1402
|
+
function jiraBacklink(ref, prUrl, contract) {
|
|
1403
|
+
const credentials = jiraCredentials(contract);
|
|
1404
|
+
if (!credentials)
|
|
1405
|
+
throw new TrackingError(
|
|
1406
|
+
"writing a Jira backlink requires ATLASSIAN_API_TOKEN/JIRA_API_TOKEN with JIRA_LOGIN and atlassian.cloudId/site"
|
|
1407
|
+
);
|
|
1408
|
+
const issueUrl = `${credentials.baseUrl}/rest/api/3/issue/${encodeURIComponent(ref)}/comment`;
|
|
1409
|
+
const auth = [
|
|
1410
|
+
["user", `${credentials.login}:${credentials.token}`],
|
|
1411
|
+
["header", "Accept: application/json"],
|
|
1412
|
+
];
|
|
1413
|
+
const listing = secureCurl([`${issueUrl}?maxResults=100`], auth, {
|
|
1414
|
+
allowFailure: true,
|
|
1415
|
+
});
|
|
1416
|
+
if (listing.status !== 0)
|
|
1417
|
+
throw new TrackingError(`Jira ticket ${ref} comments are inaccessible`);
|
|
1418
|
+
const existing = safeJson(listing.stdout, `Jira comments on ${ref}`);
|
|
1419
|
+
const managed = (existing.comments ?? []).find(comment =>
|
|
1420
|
+
carriesMarker(comment?.body)
|
|
1421
|
+
);
|
|
1422
|
+
const document = jiraCommentDocument(prUrl);
|
|
1423
|
+
const payload = JSON.stringify({ body: document });
|
|
1424
|
+
if (managed && JSON.stringify(managed.body) === JSON.stringify(document))
|
|
1425
|
+
return "unchanged";
|
|
1426
|
+
secureCurl(
|
|
1427
|
+
[managed ? `${issueUrl}/${encodeURIComponent(managed.id)}` : issueUrl],
|
|
1428
|
+
[
|
|
1429
|
+
...auth,
|
|
1430
|
+
["request", managed ? "PUT" : "POST"],
|
|
1431
|
+
["header", "Content-Type: application/json"],
|
|
1432
|
+
["data-binary", payload],
|
|
1433
|
+
],
|
|
1434
|
+
{ error: `could not write the backlink comment on ${ref}` }
|
|
1435
|
+
);
|
|
1436
|
+
return managed ? "updated" : "created";
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Establish the ticket-side backlink, whatever the tracker.
|
|
1441
|
+
*
|
|
1442
|
+
* This is the producer for the comment `assertBacklink` consumes, and it lives
|
|
1443
|
+
* in this file for that reason: the requirement was documented in a SKILL.md
|
|
1444
|
+
* and enforced here, so nothing executable ever wrote it and a required check
|
|
1445
|
+
* failed on a step no command performed.
|
|
1446
|
+
*
|
|
1447
|
+
* Every provider `assertBacklink` reads is written here. A provider that is
|
|
1448
|
+
* not refuses loudly rather than returning quietly, because a silent no-op
|
|
1449
|
+
* here reproduces the original defect one layer down: the command reports
|
|
1450
|
+
* success and the check still fails.
|
|
1451
|
+
* @param {string} ref Canonical work-item reference.
|
|
1452
|
+
* @param {string} prUrl Pull request URL.
|
|
1453
|
+
* @param {object} contract Resolved tracker contract.
|
|
1454
|
+
* @returns {string} What changed: created, updated, or unchanged.
|
|
1455
|
+
*/
|
|
1456
|
+
function postBacklink(ref, prUrl, contract) {
|
|
1457
|
+
if (contract.provider === "github") return githubBacklink(ref, prUrl);
|
|
1458
|
+
if (contract.provider === "linear")
|
|
1459
|
+
return linearBacklink(ref, prUrl, contract);
|
|
1460
|
+
if (contract.provider === "jira") return jiraBacklink(ref, prUrl, contract);
|
|
1461
|
+
throw new TrackingError(
|
|
1462
|
+
`no backlink writer for tracker '${contract.provider}'; ` +
|
|
1463
|
+
`github, jira and linear are supported.\nThe Work-Item Traceability check ` +
|
|
1464
|
+
`reads a managed ${MARKER} comment for this provider, so it cannot pass ` +
|
|
1465
|
+
`until a writer exists here — add one rather than posting by hand.`
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1220
1469
|
function assertBacklink(
|
|
1221
1470
|
ref,
|
|
1222
1471
|
prUrl,
|
|
@@ -1502,7 +1751,12 @@ function backlinkAdvice(ref, prUrl, contract) {
|
|
|
1502
1751
|
contract.provider === "linear"
|
|
1503
1752
|
? `The tracker builds that link from the BRANCH NAME, so a branch carrying no ticket id can never acquire one: no edit to this pull request, its body, or its commits will do it. Fixing it that way needs a NEW branch named for ${ref} and a new pull request from it. `
|
|
1504
1753
|
: "";
|
|
1505
|
-
return
|
|
1754
|
+
return (
|
|
1755
|
+
`${branchDerived}The one remedy that needs no new branch is the managed comment ` +
|
|
1756
|
+
`\`${MARKER} ${prUrl}\` on ${ref}. Do not post it by hand — run:\n\n` +
|
|
1757
|
+
` ${BACKLINK_COMMAND} --ref ${ref} --pr-url ${prUrl}\n\n` +
|
|
1758
|
+
`which creates that comment or updates the existing one, so running it twice is safe`
|
|
1759
|
+
);
|
|
1506
1760
|
}
|
|
1507
1761
|
|
|
1508
1762
|
/**
|
|
@@ -1620,6 +1874,33 @@ function bind(args) {
|
|
|
1620
1874
|
console.log(`work-item bound: ${ref} (${file})`);
|
|
1621
1875
|
}
|
|
1622
1876
|
|
|
1877
|
+
/**
|
|
1878
|
+
* Establish the ticket-side backlink for one work item and pull request.
|
|
1879
|
+
*
|
|
1880
|
+
* Idempotent by construction — the writer updates the managed comment it finds
|
|
1881
|
+
* rather than adding a second — so this is safe to run on every push, and safe
|
|
1882
|
+
* to rerun after a failure without inspecting what the last run managed to do.
|
|
1883
|
+
* @param {string[]} args CLI arguments.
|
|
1884
|
+
*/
|
|
1885
|
+
function backlink(args) {
|
|
1886
|
+
const contract = trackerContract();
|
|
1887
|
+
const supplied = option(args, "--ref", "LISA_WORK_ITEM_REF");
|
|
1888
|
+
const bound = readState(true)?.ref;
|
|
1889
|
+
if (!supplied && !bound) {
|
|
1890
|
+
throw new TrackingError(
|
|
1891
|
+
`${BACKLINK_COMMAND} requires --ref <work-item>, or a worktree binding from \`lisa-work-item.mjs link\``
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
const ref = canonicalizeRef(supplied ?? bound, contract);
|
|
1895
|
+
const prUrl =
|
|
1896
|
+
option(args, "--pr-url", "LISA_PR_URL") ??
|
|
1897
|
+
option(args, "--url", "LISA_PR_URL");
|
|
1898
|
+
if (!prUrl)
|
|
1899
|
+
throw new TrackingError(`${BACKLINK_COMMAND} requires --pr-url <url>`);
|
|
1900
|
+
const outcome = postBacklink(ref, prUrl, contract);
|
|
1901
|
+
console.log(`work-item backlink ${outcome} on ${ref}: ${MARKER} ${prUrl}`);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1623
1904
|
function prepareCommitMessage(args) {
|
|
1624
1905
|
const [file, source = ""] = args;
|
|
1625
1906
|
if (!file)
|
|
@@ -1755,12 +2036,13 @@ function main() {
|
|
|
1755
2036
|
rmSync(statePath(), { force: true });
|
|
1756
2037
|
return console.log("work-item binding cleared");
|
|
1757
2038
|
}
|
|
2039
|
+
if (command === "backlink") return backlink(args);
|
|
1758
2040
|
if (command === "prepare-commit-msg") return prepareCommitMessage(args);
|
|
1759
2041
|
if (command === "validate-commit") return validateCommit(args);
|
|
1760
2042
|
if (command === "validate-push") return validatePush(args);
|
|
1761
2043
|
if (command === "validate-pr") return validatePr(args);
|
|
1762
2044
|
throw new TrackingError(
|
|
1763
|
-
"Usage: lisa-work-item.mjs link|current|attach-branch|clear|prepare-commit-msg|validate-commit|validate-push|validate-pr" +
|
|
2045
|
+
"Usage: lisa-work-item.mjs link|current|attach-branch|clear|backlink|prepare-commit-msg|validate-commit|validate-push|validate-pr" +
|
|
1764
2046
|
"\n(`bind` is accepted as an alias for `link`, but some agent harnesses refuse the token `bind` in a command line.)"
|
|
1765
2047
|
);
|
|
1766
2048
|
}
|
|
@@ -1409,6 +1409,27 @@ export declare const getJsFilesOverride: () => {
|
|
|
1409
1409
|
"max-lines-per-function": string;
|
|
1410
1410
|
};
|
|
1411
1411
|
};
|
|
1412
|
+
/**
|
|
1413
|
+
* Glob patterns covering the repository-maintenance script tree that Lisa
|
|
1414
|
+
* installs into every consumer (`scripts/`), plus nested `scripts/` trees such
|
|
1415
|
+
* as `projects/<name>/scripts/`. Exported so the coverage test can assert the
|
|
1416
|
+
* shipped `.mjs` paths land inside the same set the override targets.
|
|
1417
|
+
*/
|
|
1418
|
+
export declare const scriptsFilePatterns: string[];
|
|
1419
|
+
/**
|
|
1420
|
+
* Standalone-script override — the rule profile for the `scripts/` tree.
|
|
1421
|
+
*
|
|
1422
|
+
* These files are repository-maintenance CLIs invoked by a developer or a CI
|
|
1423
|
+
* runner, not application source and not published library API. They were
|
|
1424
|
+
* previously excluded from linting entirely; that is the defect this override
|
|
1425
|
+
* exists to make survivable. Every rule turned off here is turned off because
|
|
1426
|
+
* it encodes an assumption about application code that a CLI script does not
|
|
1427
|
+
* meet — correctness and bug-finding rules (including the SonarJS rules an
|
|
1428
|
+
* external scanner also runs, e.g. `sonarjs/no-alphabetical-sort`) stay ON,
|
|
1429
|
+
* because those are precisely what the exclusion was hiding.
|
|
1430
|
+
* @returns {object} ESLint flat config object for standalone scripts
|
|
1431
|
+
*/
|
|
1432
|
+
export declare const getScriptsFilesOverride: () => Linter.Config;
|
|
1412
1433
|
/**
|
|
1413
1434
|
* Shared hooks and components override - relaxes import restrictions.
|
|
1414
1435
|
* @returns {object} ESLint flat config object for shared files
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/configs/eslint/base.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AACH,OAAO,cAAc,MAAM,iDAAiD,CAAC;AAC7E,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,KAAK,MAAM,qBAAqB,CAAC;AAExC,OAAO,QAAQ,MAAM,oCAAoC,CAAC;AAC1D,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AAIzC;;;GAGG;AACH,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/configs/eslint/base.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AACH,OAAO,cAAc,MAAM,iDAAiD,CAAC;AAC7E,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,KAAK,MAAM,qBAAqB,CAAC;AAExC,OAAO,QAAQ,MAAM,oCAAoC,CAAC;AAC1D,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AAIzC;;;GAGG;AACH,eAAO,MAAM,cAAc,UAuE1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;;CAI7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,MAAM,CAAC,MAAM,EA6Cd,CAAC;AAElC;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,YAAY,OAAO,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyJjE,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUjC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;;;CAO7B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,UAAkC,CAAC;AAanE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,QAAO,MAAM,CAAC,MAoChD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;CAKjC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,qBAAoB,MAAM,EAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCpE,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,cAAc,MAAM,EAAE,YAAc,EACpC,iBAAiB,MAAM;;;;;;;;;;;;;;;;;;CAuBvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,eAAc,MAAM,EAAsC;;;;;;CAS1D,CAAC;AAGH,OAAO,EACL,cAAc,EACd,UAAU,EACV,OAAO,EACP,EAAE,EACF,KAAK,EACL,QAAQ,EACR,OAAO,EACP,QAAQ,GACT,CAAC"}
|