@codyswann/lisa 4.19.0 → 4.21.0

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 (133) hide show
  1. package/all/copy-contents/.gitattributes +2 -1
  2. package/all/create-only/.lisa/PROJECT_LEARNINGS.md +1 -1
  3. package/dist/core/learnings-alias.d.ts +26 -45
  4. package/dist/core/learnings-alias.d.ts.map +1 -1
  5. package/dist/core/learnings-alias.js +29 -48
  6. package/dist/core/learnings-alias.js.map +1 -1
  7. package/dist/core/learnings-budget-check.d.ts +2 -1
  8. package/dist/core/learnings-budget-check.d.ts.map +1 -1
  9. package/dist/core/learnings-budget-check.js +13 -11
  10. package/dist/core/learnings-budget-check.js.map +1 -1
  11. package/dist/core/learnings-contract.d.ts +27 -10
  12. package/dist/core/learnings-contract.d.ts.map +1 -1
  13. package/dist/core/learnings-contract.js +37 -9
  14. package/dist/core/learnings-contract.js.map +1 -1
  15. package/dist/core/learnings-document.d.ts +20 -1
  16. package/dist/core/learnings-document.d.ts.map +1 -1
  17. package/dist/core/learnings-document.js +81 -14
  18. package/dist/core/learnings-document.js.map +1 -1
  19. package/dist/core/learnings-entry.d.ts +11 -1
  20. package/dist/core/learnings-entry.d.ts.map +1 -1
  21. package/dist/core/learnings-entry.js +98 -24
  22. package/dist/core/learnings-entry.js.map +1 -1
  23. package/dist/core/learnings-merge-driver.d.ts +4 -2
  24. package/dist/core/learnings-merge-driver.d.ts.map +1 -1
  25. package/dist/core/learnings-merge-driver.js +6 -3
  26. package/dist/core/learnings-merge-driver.js.map +1 -1
  27. package/dist/core/learnings-merge.d.ts.map +1 -1
  28. package/dist/core/learnings-merge.js +96 -10
  29. package/dist/core/learnings-merge.js.map +1 -1
  30. package/dist/core/learnings-overflow.d.ts.map +1 -1
  31. package/dist/core/learnings-overflow.js +72 -3
  32. package/dist/core/learnings-overflow.js.map +1 -1
  33. package/dist/core/learnings-supersede.d.ts +56 -0
  34. package/dist/core/learnings-supersede.d.ts.map +1 -0
  35. package/dist/core/learnings-supersede.js +199 -0
  36. package/dist/core/learnings-supersede.js.map +1 -0
  37. package/dist/core/learnings-writer.d.ts +9 -7
  38. package/dist/core/learnings-writer.d.ts.map +1 -1
  39. package/dist/core/learnings-writer.js +31 -58
  40. package/dist/core/learnings-writer.js.map +1 -1
  41. package/dist/core/learnings.d.ts +3 -0
  42. package/dist/core/learnings.d.ts.map +1 -1
  43. package/dist/core/learnings.js +3 -0
  44. package/dist/core/learnings.js.map +1 -1
  45. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  46. package/dist/core/upstream-evidence-manifest.js +24 -7
  47. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  48. package/package.json +3 -2
  49. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa/.codex-plugin/skills/lisa-debrief-apply/SKILL.md +5 -4
  52. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +2 -2
  53. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  54. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  55. package/plugins/lisa/agents/learner.md +7 -6
  56. package/plugins/lisa/rules/reference/project-learnings.md +34 -1
  57. package/plugins/lisa/skills/lisa-debrief-apply/SKILL.md +5 -4
  58. package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +2 -2
  59. package/plugins/lisa/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  60. package/plugins/lisa/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  61. package/plugins/lisa-agy/agents/learner.md +7 -6
  62. package/plugins/lisa-agy/plugin.json +1 -1
  63. package/plugins/lisa-agy/skills/lisa-debrief-apply/SKILL.md +5 -4
  64. package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +2 -2
  65. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  66. package/plugins/lisa-agy/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  67. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  69. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  70. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-copilot/agents/learner.agent.md +7 -6
  74. package/plugins/lisa-copilot/rules/reference/project-learnings.md +34 -1
  75. package/plugins/lisa-copilot/skills/lisa-debrief-apply/SKILL.md +5 -4
  76. package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +2 -2
  77. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  78. package/plugins/lisa-copilot/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  79. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-cursor/agents/learner.md +7 -6
  81. package/plugins/lisa-cursor/rules/project-learnings-reference.mdc +34 -1
  82. package/plugins/lisa-cursor/skills/lisa-debrief-apply/SKILL.md +5 -4
  83. package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +2 -2
  84. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  85. package/plugins/lisa-cursor/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  86. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-expo-agy/plugin.json +1 -1
  89. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  93. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  94. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  98. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  99. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  103. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  104. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  108. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  109. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  113. package/plugins/lisa-rails-agy/plugin.json +1 -1
  114. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  116. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  117. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  118. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  119. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  120. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  123. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  124. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  126. package/plugins/src/base/agents/learner.md +7 -6
  127. package/plugins/src/base/rules/reference/project-learnings.md +34 -1
  128. package/plugins/src/base/skills/lisa-debrief-apply/SKILL.md +5 -4
  129. package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +2 -2
  130. package/plugins/src/base/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +349 -0
  131. package/plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md +23 -5
  132. package/scripts/verify-learner-frontmatter-built.mjs +1 -1
  133. package/scripts/verify-packed-learnings-contract.mjs +268 -0
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env node
2
+ /** Empirical proof that the packed public learnings API preserves v2 semantics. */
3
+ import assert from "node:assert/strict";
4
+ import { execFile } from "node:child_process";
5
+ import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import { pathToFileURL } from "node:url";
8
+ import { promisify } from "node:util";
9
+
10
+ const executeFile = promisify(execFile);
11
+ const root = path.resolve(import.meta.dirname, "..");
12
+ const temporary = await mkdtemp(path.join(root, ".packed-learnings-proof-"));
13
+
14
+ try {
15
+ const archive = path.join(temporary, "lisa-packed.tgz");
16
+ await executeFile(
17
+ "bun",
18
+ ["pm", "pack", "--ignore-scripts", "--filename", archive, "--quiet"],
19
+ { cwd: root }
20
+ );
21
+ await executeFile("/usr/bin/tar", ["-xzf", archive, "-C", temporary]);
22
+ const packedRoot = path.join(temporary, "package");
23
+ const learnings = await import(
24
+ pathToFileURL(path.join(packedRoot, "dist", "core", "learnings.js")).href
25
+ );
26
+
27
+ assert.equal(learnings.LEARNINGS_CONTRACT.version, 2);
28
+ assert.equal(learnings.LEARNINGS_CONTRACT.fields.length, 8);
29
+ assert.equal(learnings.MAX_STABLE_TOKEN_BYTES, 128);
30
+ assert.equal(learnings.LEARNINGS_CONTRACT.maxTokens, 14_900);
31
+
32
+ const packedVictim = "packed-victim";
33
+ const packedProvenance = "issue:#2015";
34
+ const packedDate = "2026-08-26";
35
+ const packedEntry = {
36
+ id: packedVictim,
37
+ fingerprint: "packed-victim-fingerprint",
38
+ rule: "Keep packed entry validation inert.",
39
+ why: "Consumers pass untrusted values through the public writer.",
40
+ provenance: [packedProvenance],
41
+ first_learned: packedDate,
42
+ last_confirmed: packedDate,
43
+ confidence: "high",
44
+ };
45
+ let entryGetterCalls = 0;
46
+ const accessorEntry = { ...packedEntry };
47
+ Object.defineProperty(accessorEntry, "why", {
48
+ get: () => {
49
+ entryGetterCalls += 1;
50
+ return "hostile";
51
+ },
52
+ });
53
+ assert.throws(
54
+ () => learnings.validateLearningEntry(accessorEntry),
55
+ /why.*accessor/i
56
+ );
57
+ assert.equal(entryGetterCalls, 0);
58
+ const hostileProvenance = [packedProvenance];
59
+ hostileProvenance.unexpected = true;
60
+ assert.throws(
61
+ () =>
62
+ learnings.validateLearningEntry({
63
+ ...packedEntry,
64
+ provenance: hostileProvenance,
65
+ }),
66
+ /provenance array/i
67
+ );
68
+
69
+ let stampGetterCalls = 0;
70
+ const accessorStamps = [];
71
+ accessorStamps.length = 1;
72
+ Object.defineProperty(accessorStamps, "0", {
73
+ get: () => {
74
+ stampGetterCalls += 1;
75
+ return { id: packedVictim, fingerprint: packedVictim };
76
+ },
77
+ });
78
+ assert.throws(
79
+ () => learnings.validateLearningEntryStamps(accessorStamps),
80
+ /supersede array/i
81
+ );
82
+ assert.equal(stampGetterCalls, 0);
83
+ Object.defineProperties(Object.prototype, {
84
+ id: { configurable: true, value: { value: packedVictim } },
85
+ fingerprint: {
86
+ configurable: true,
87
+ value: { value: packedVictim },
88
+ },
89
+ });
90
+ try {
91
+ const pollutedEntry = { ...packedEntry };
92
+ delete pollutedEntry.id;
93
+ delete pollutedEntry.fingerprint;
94
+ pollutedEntry.unrelatedId = "ignored";
95
+ pollutedEntry.unrelatedFingerprint = "ignored";
96
+ assert.throws(
97
+ () => learnings.validateLearningEntry(pollutedEntry),
98
+ /entry fields.*exactly/i
99
+ );
100
+ assert.throws(
101
+ () =>
102
+ learnings.validateLearningEntryStamps([
103
+ { harmless: true, stillHarmless: true },
104
+ ]),
105
+ /exactly.*id.*fingerprint/i
106
+ );
107
+ } finally {
108
+ delete Object.prototype.id;
109
+ delete Object.prototype.fingerprint;
110
+ }
111
+ assert.throws(
112
+ () =>
113
+ learnings.validateLearningEntryStamps([
114
+ { id: "A".repeat(129), fingerprint: packedVictim },
115
+ ]),
116
+ /id exceeds max stable token bytes 128/i
117
+ );
118
+ const inheritedProvenance = [];
119
+ inheritedProvenance.length = 1;
120
+ Object.defineProperty(Object.prototype, "0", {
121
+ configurable: true,
122
+ writable: true,
123
+ value: { value: "issue:#forged" },
124
+ });
125
+ try {
126
+ assert.throws(
127
+ () =>
128
+ learnings.validateLearningEntry({
129
+ ...packedEntry,
130
+ provenance: inheritedProvenance,
131
+ }),
132
+ /provenance array/i
133
+ );
134
+ } finally {
135
+ delete Object.prototype["0"];
136
+ }
137
+
138
+ const legacy = `# Project Learnings\n\n<!-- lisa-learnings-contract:v1 -->\n\n\`\`\`jsonl\n{"id":"learner-base","rule":"Keep packed compatibility executable.","why":"Consumers install the tarball, not the source tree.","provenance":["issue:#2015"],"first_learned":"2026-08-26","last_confirmed":"2026-08-26","confidence":"high"}\n\`\`\`\n`;
139
+ const normalized = learnings.parseLearningsDocument(legacy);
140
+ assert.equal(normalized.sourceVersion, 1);
141
+ assert.equal(normalized.entries[0].fingerprint, "learner-base");
142
+ const oversizedLegacyId = "a".repeat(7007);
143
+ const oversizedLegacy = `# Project Learnings\n\n<!-- lisa-learnings-contract:v1 -->\n\n\`\`\`jsonl\n${JSON.stringify({ ...JSON.parse(legacy.split("\n")[5]), id: oversizedLegacyId, rule: "r", why: "w".repeat(20), provenance: ["p"] })}\n\`\`\`\n`;
144
+ assert.equal(Buffer.byteLength(oversizedLegacy, "utf8"), 7226);
145
+ assert.throws(
146
+ () => learnings.parseLearningsDocument(oversizedLegacy),
147
+ /id exceeds max stable token bytes 128/i
148
+ );
149
+
150
+ const project = path.join(temporary, "consumer");
151
+ await mkdir(project);
152
+ const base = {
153
+ ...normalized.entries[0],
154
+ fingerprint: "learner-base-fingerprint",
155
+ };
156
+ await learnings.persistLearningEntry(project, base);
157
+ const observed = { id: base.id, fingerprint: base.fingerprint };
158
+ const stale = [];
159
+ const candidates = Array.from({ length: 9 }, (_unused, index) => ({
160
+ ...base,
161
+ id: `learner-candidate-${index}`,
162
+ fingerprint: `learner-candidate-fingerprint-${index}`,
163
+ rule: `Packed writer candidate ${index}.`,
164
+ }));
165
+ await Promise.all(
166
+ candidates.map(candidate =>
167
+ learnings.persistConsolidatedLearning(project, candidate, {
168
+ supersede: [observed],
169
+ onStaleSupersede: targets => stale.push(targets),
170
+ })
171
+ )
172
+ );
173
+
174
+ const ledgerPath = path.join(project, ".lisa", "PROJECT_LEARNINGS.md");
175
+ const afterRace = learnings.parseLearningsFile(
176
+ await readFile(ledgerPath, "utf8")
177
+ );
178
+ assert.equal(afterRace.length, 9);
179
+ assert.equal(stale.length, 8);
180
+ const current = afterRace.find(entry => entry.id === base.id);
181
+ assert.ok(current);
182
+
183
+ const chained = {
184
+ ...current,
185
+ id: "learner-chained-candidate",
186
+ fingerprint: "learner-chained-fingerprint",
187
+ rule: "A current stamp may rewrite the stable identity.",
188
+ };
189
+ await learnings.persistConsolidatedLearning(project, chained, {
190
+ supersede: [{ id: current.id, fingerprint: current.fingerprint }],
191
+ });
192
+ const beforeCollision = await readFile(ledgerPath, "utf8");
193
+ await assert.rejects(
194
+ learnings.persistLearningEntry(project, {
195
+ ...chained,
196
+ id: "learner-duplicate-content",
197
+ }),
198
+ /duplicate learning fingerprint/i
199
+ );
200
+ assert.equal(await readFile(ledgerPath, "utf8"), beforeCollision);
201
+
202
+ const ancestor = normalized.entries[0];
203
+ const ours = { ...ancestor, fingerprint: "learner-ours-fingerprint" };
204
+ const theirs = { ...ancestor, fingerprint: "learner-theirs-fingerprint" };
205
+ const merged = learnings.mergeLearningsDocuments(
206
+ legacy,
207
+ learnings.renderLearningsFile([ours]),
208
+ learnings.renderLearningsFile([theirs])
209
+ );
210
+ assert.equal(merged.kind, "merged");
211
+ const mergedEntries = learnings.parseLearningsFile(merged.content);
212
+ assert.equal(mergedEntries.length, 2);
213
+ assert.equal(new Set(mergedEntries.map(entry => entry.fingerprint)).size, 2);
214
+ assert.equal(learnings.projectLearnings(mergedEntries).omittedCount, 0);
215
+
216
+ const overflowProject = path.join(temporary, "overflow-consumer");
217
+ await mkdir(overflowProject);
218
+ const overflowEntry = index => ({
219
+ id: `packed-overflow-${index}`,
220
+ fingerprint: `packed-overflow-${index}`,
221
+ rule: `Packed overflow entry ${index}.`,
222
+ why: "Compact packed proof.",
223
+ provenance: [`issue:#${index}`],
224
+ first_learned: packedDate,
225
+ last_confirmed: packedDate,
226
+ confidence: "high",
227
+ });
228
+ for (
229
+ let index = 0;
230
+ index < learnings.LEARNINGS_CONTRACT.maxEntries;
231
+ index += 1
232
+ ) {
233
+ await learnings.persistLearningEntry(overflowProject, overflowEntry(index));
234
+ }
235
+ const firstDrop = overflowEntry(20);
236
+ await assert.rejects(
237
+ learnings.persistLearningEntry(overflowProject, firstDrop),
238
+ /was preserved/
239
+ );
240
+ const sameIdFork = { ...overflowEntry(21), id: firstDrop.id };
241
+ await assert.rejects(
242
+ learnings.persistLearningEntry(overflowProject, sameIdFork),
243
+ /was preserved/
244
+ );
245
+ const overflow = await learnings.readLearningsOverflow(overflowProject);
246
+ assert.deepEqual(
247
+ overflow.entries.map(entry => [entry.id, entry.fingerprint]),
248
+ [
249
+ [firstDrop.id, firstDrop.fingerprint],
250
+ [sameIdFork.fingerprint, sameIdFork.fingerprint],
251
+ ]
252
+ );
253
+ const beforeOverflowCollision = await readFile(overflow.file, "utf8");
254
+ await assert.rejects(
255
+ learnings.persistLearningEntry(overflowProject, {
256
+ ...overflowEntry(22),
257
+ fingerprint: firstDrop.fingerprint,
258
+ }),
259
+ /duplicate learning fingerprint/i
260
+ );
261
+ assert.equal(await readFile(overflow.file, "utf8"), beforeOverflowCollision);
262
+
263
+ console.log(
264
+ "[EVIDENCE: packed-learnings-contract] v1=migratable legacy=bounded entries=hardened stamps=hardened race=9 stale=8 chain=stable collision=atomic overflow=fork merge=2 projection=bounded"
265
+ );
266
+ } finally {
267
+ await rm(temporary, { recursive: true, force: true });
268
+ }