@codyswann/lisa 3.14.2 → 3.14.3

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 (56) hide show
  1. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  2. package/dist/core/upstream-evidence-manifest.js +4 -1
  3. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa-agy/plugin.json +1 -1
  8. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  9. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  10. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  11. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  17. package/plugins/lisa-expo-agy/plugin.json +1 -1
  18. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  23. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  28. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  33. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  38. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-rails-agy/plugin.json +1 -1
  43. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  48. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  53. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  55. package/scripts/check-derived-artifacts.mjs +237 -0
  56. package/typescript/copy-contents/.husky/pre-commit +16 -0
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Fail a commit whose generated artifacts no longer vouch for the bytes beside
4
+ * them.
5
+ *
6
+ * Lisa has two artifacts derived from tracked sources:
7
+ *
8
+ * - `src/core/upstream-evidence-manifest.ts`, regenerated by
9
+ * `bun run build:upstream-evidence-manifest`
10
+ * - `src/core/lisa-owned-hash-ledger.ts`, regenerated by
11
+ * `bun run build:lisa-owned-hash-ledger`
12
+ *
13
+ * Each already has a suite-level guard, but the suite is the slow and late place
14
+ * to learn it. CodySwannGT/lisa#2557 is what that costs: three branches, each
15
+ * green on its own, combined into a red `main` that only failed in a post-merge
16
+ * Release run. The second obligation was days old and in nobody's checklist, and
17
+ * the measured pattern in this repository is that executable controls hold where
18
+ * prose does not — so this is the control, at the earliest point that can name
19
+ * the command to run.
20
+ *
21
+ * **This script verifies nothing itself.** It delegates to each generator's own
22
+ * `--check`, which is the point: a second implementation of "is the artifact
23
+ * current" would be a second thing to keep in step. The two checks deliberately
24
+ * assert different properties, and both are correct for their artifact:
25
+ *
26
+ * - The manifest hashes tracked files with no history walk, so a fresh
27
+ * regeneration is byte-reproducible anywhere and byte-identity is a fair test.
28
+ * - The ledger walks history, whose result depends on clone depth and merge
29
+ * topology. It therefore asserts only that the bytes shipped *right now* are
30
+ * recorded — the property that actually protects refresh. Asserting
31
+ * byte-identity there made a correct ledger fail once `autoupdate` merged
32
+ * `main` in (CodySwannGT/lisa#2556), and at this repository's concurrency that
33
+ * would mean every merge reddens every open PR. Do not tighten it back.
34
+ *
35
+ * Both triggers below are deliberate **supersets** of what each artifact covers.
36
+ * Re-deriving exact membership here would create a second copy of the rule that
37
+ * could drift and silently stop firing; over-triggering only costs a few seconds
38
+ * on a commit that owed nothing, which is the safe direction to be wrong in.
39
+ * @module scripts/check-derived-artifacts
40
+ */
41
+ import { execFileSync } from "node:child_process";
42
+ import path from "node:path";
43
+ import process from "node:process";
44
+ import { fileURLToPath } from "node:url";
45
+
46
+ const repoRoot = path.resolve(import.meta.dirname, "..");
47
+
48
+ /**
49
+ * Path prefixes whose contents the upstream evidence manifest hash-pins.
50
+ *
51
+ * Mirrors `packagedEvidencePrefixes` in
52
+ * `scripts/generate-upstream-evidence-manifest.mjs`. The duplication is
53
+ * deliberate: that script runs top-level code which refuses to load outside the
54
+ * canonical repository, so it cannot be imported for its constant. A test
55
+ * asserts the two lists agree, the same arrangement `isLisaOwned` already uses
56
+ * in the ledger generator.
57
+ * @type {readonly string[]}
58
+ */
59
+ export const PACKAGED_EVIDENCE_PREFIXES = Object.freeze([
60
+ "plugins/src/",
61
+ "all/",
62
+ "cdk/",
63
+ "expo/",
64
+ "eslint-plugin-code-organization/",
65
+ "eslint-plugin-component-structure/",
66
+ "eslint-plugin-phaser/",
67
+ "eslint-plugin-ui-standards/",
68
+ "harper-fabric/",
69
+ "nestjs/",
70
+ "npm-package/",
71
+ "oxlint/",
72
+ "phaser/",
73
+ "rails/",
74
+ "scripts/",
75
+ "tsconfig/",
76
+ "typescript/",
77
+ "ui/",
78
+ ]);
79
+
80
+ /** Marker separating a template's source location from its installed path. */
81
+ const COPY_OVERWRITE = "/copy-overwrite/";
82
+
83
+ /**
84
+ * Whether this change can move the bytes of a template the ledger vouches for.
85
+ *
86
+ * Every ledger source lives under a `copy-overwrite` directory, so the marker is
87
+ * a superset of the `lisa-` namespace the ledger actually records. Matching the
88
+ * broader set means a template that is renamed *into* the namespace, or added to
89
+ * it later, still trips the gate.
90
+ * @param {readonly string[]} stagedPaths - Repo-relative staged paths
91
+ * @returns {boolean} True when the ledger check is owed
92
+ */
93
+ export function requiresLedgerCheck(stagedPaths) {
94
+ return stagedPaths.some(staged => staged.includes(COPY_OVERWRITE));
95
+ }
96
+
97
+ /**
98
+ * Whether this change touches a path the evidence manifest hash-pins.
99
+ * @param {readonly string[]} stagedPaths - Repo-relative staged paths
100
+ * @returns {boolean} True when the manifest check is owed
101
+ */
102
+ export function requiresManifestCheck(stagedPaths) {
103
+ return stagedPaths.some(staged =>
104
+ PACKAGED_EVIDENCE_PREFIXES.some(prefix => staged.startsWith(prefix))
105
+ );
106
+ }
107
+
108
+ /**
109
+ * Repo-relative paths staged for the pending commit.
110
+ *
111
+ * Renames report their destination (`-M` plus `--diff-filter=R`), and deletions
112
+ * are included because removing a template is also a way to move what the
113
+ * artifacts describe.
114
+ * @returns {string[]} Staged paths, empty when git cannot be consulted
115
+ */
116
+ export function stagedPaths() {
117
+ try {
118
+ return execFileSync(
119
+ "git",
120
+ ["diff", "--cached", "--name-only", "--diff-filter=ACMRD"],
121
+ { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
122
+ )
123
+ .split("\n")
124
+ .filter(Boolean);
125
+ } catch {
126
+ return [];
127
+ }
128
+ }
129
+
130
+ /**
131
+ * The human-readable half of a generator's failure output.
132
+ *
133
+ * A generator that reports by throwing prints a Node stack trace around its
134
+ * message. The frames are noise to the person being blocked — and this gate is
135
+ * read by operators who did not write either generator — so keep the lines that
136
+ * say what is wrong and drop the ones that say where the throw was.
137
+ * @param {string} raw - Combined stdout and stderr from the generator
138
+ * @returns {string} Message lines, stack frames removed
139
+ */
140
+ export function readableFailure(raw) {
141
+ const lines = raw.split("\n");
142
+ // Node prints `<file>:<line>`, the offending source line, then a caret, before
143
+ // the message itself. Everything through the caret is the code frame.
144
+ const caret = lines.findIndex(line => /^\s*\^+\s*$/u.test(line));
145
+ return lines
146
+ .slice(caret + 1)
147
+ .filter(line => !/^\s+at\s/u.test(line))
148
+ .filter(line => !line.startsWith("Node.js v"))
149
+ .map(line => line.replace(/^Error:\s*/u, "").trimEnd())
150
+ .filter(Boolean)
151
+ .join("\n")
152
+ .trim();
153
+ }
154
+
155
+ /**
156
+ * Run one generator's `--check` and report whether it passed.
157
+ * @param {string} script - Repo-relative generator path
158
+ * @returns {{ok: boolean, output: string}} Result and any diagnostic output
159
+ */
160
+ function runCheck(script) {
161
+ try {
162
+ execFileSync(process.execPath, [script, "--check"], {
163
+ cwd: repoRoot,
164
+ encoding: "utf8",
165
+ stdio: ["ignore", "pipe", "pipe"],
166
+ });
167
+ return { ok: true, output: "" };
168
+ } catch (error) {
169
+ const stderr = error?.stderr ?? "";
170
+ const stdout = error?.stdout ?? "";
171
+ return { ok: false, output: readableFailure(`${stdout}${stderr}`) };
172
+ }
173
+ }
174
+
175
+ /**
176
+ * The artifacts this gate knows how to check, in the order they are reported.
177
+ * @type {readonly {name: string, script: string, command: string, trigger: (paths: readonly string[]) => boolean}[]}
178
+ */
179
+ const ARTIFACTS = Object.freeze([
180
+ Object.freeze({
181
+ name: "upstream evidence manifest",
182
+ script: "scripts/generate-upstream-evidence-manifest.mjs",
183
+ command: "bun run build:upstream-evidence-manifest",
184
+ trigger: requiresManifestCheck,
185
+ }),
186
+ Object.freeze({
187
+ name: "Lisa-owned hash ledger",
188
+ script: "scripts/generate-lisa-owned-hash-ledger.mjs",
189
+ command: "bun run build:lisa-owned-hash-ledger",
190
+ trigger: requiresLedgerCheck,
191
+ }),
192
+ ]);
193
+
194
+ /**
195
+ * Check every artifact this commit owes and describe any that are stale.
196
+ * @param {readonly string[]} staged - Repo-relative staged paths
197
+ * @returns {string[]} One remediation block per stale artifact
198
+ */
199
+ export function staleArtifacts(staged) {
200
+ const failures = [];
201
+ for (const artifact of ARTIFACTS) {
202
+ if (!artifact.trigger(staged)) continue;
203
+ const result = runCheck(artifact.script);
204
+ if (result.ok) continue;
205
+ failures.push(
206
+ `${artifact.name} is stale.\n` +
207
+ `${result.output ? `${result.output}\n` : ""}` +
208
+ ` Fix: ${artifact.command}\n` +
209
+ ` Then: git add the regenerated file and commit again.`
210
+ );
211
+ }
212
+ return failures;
213
+ }
214
+
215
+ /** Run the gate over the staged change and exit non-zero when anything is stale. */
216
+ function main() {
217
+ const staged = stagedPaths();
218
+ const failures = staleArtifacts(staged);
219
+ if (failures.length === 0) {
220
+ process.stdout.write("Generated artifacts are current.\n");
221
+ return;
222
+ }
223
+ process.stderr.write(
224
+ `\nA generated artifact no longer matches the sources in this commit:\n\n${failures.join(
225
+ "\n\n"
226
+ )}\n\n` +
227
+ `Both artifacts are regenerated from the working tree, so run the command\n` +
228
+ `above AFTER staging, and again after any reformat, then amend.\n` +
229
+ `If the stale file is one you did not touch, an unstaged local edit to a\n` +
230
+ `tracked file can also trip this — commit or revert it first.\n`
231
+ );
232
+ process.exit(1);
233
+ }
234
+
235
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
236
+ main();
237
+ }
@@ -129,4 +129,20 @@ fi
129
129
  # Run lint-staged for incremental lint and format checks
130
130
  echo "🚀 Running lint-staged..."
131
131
  $EXECUTOR lint-staged --config .lintstagedrc.json
132
+ LINT_STAGED_STATUS=$?
133
+ if [ $LINT_STAGED_STATUS -ne 0 ]; then
134
+ exit $LINT_STAGED_STATUS
135
+ fi
136
+
137
+ # Generated artifacts must vouch for the bytes committed beside them. Runs AFTER
138
+ # lint-staged on purpose: reformatting a staged template moves its bytes, so a
139
+ # check placed above would sign off on bytes that no longer exist. Absent in host
140
+ # projects, which have the hook but none of Lisa's generators.
141
+ if [ -f "scripts/check-derived-artifacts.mjs" ] && command -v node >/dev/null 2>&1; then
142
+ echo "🧾 Checking generated artifacts..."
143
+ node scripts/check-derived-artifacts.mjs --staged
144
+ if [ $? -ne 0 ]; then
145
+ exit 1
146
+ fi
147
+ fi
132
148
  # END: AI GUARDRAILS