@codyswann/lisa 2.108.0 → 2.109.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 (25) hide show
  1. package/package.json +1 -1
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa/scripts/project-ideation-idempotency-harness.mjs +47 -4
  5. package/plugins/lisa/skills/project-ideation/SKILL.md +20 -0
  6. package/plugins/lisa/skills/project-ideation/examples/idempotency-verification-harness.md +3 -2
  7. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  8. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  9. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  10. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  11. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  13. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  15. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  17. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  19. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  21. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  23. package/plugins/src/base/scripts/project-ideation-idempotency-harness.mjs +47 -4
  24. package/plugins/src/base/skills/project-ideation/SKILL.md +20 -0
  25. package/plugins/src/base/skills/project-ideation/examples/idempotency-verification-harness.md +3 -2
package/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.108.0",
85
+ "version": "2.109.0",
86
86
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
87
87
  "main": "dist/index.js",
88
88
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -5,10 +5,17 @@
5
5
  * The harness runs a caller-supplied deterministic project-ideation command
6
6
  * twice, checks that exactly one open GitHub PRD contains the expected marker,
7
7
  * then optionally removes the automation memory file and verifies a third run
8
- * recreates memory without creating a duplicate PRD.
8
+ * recreates memory without creating a duplicate PRD. The recreated memory entry
9
+ * must include the advisory run fields project-ideation promises to write.
9
10
  */
10
11
 
11
- import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
12
+ import {
13
+ existsSync,
14
+ mkdirSync,
15
+ readFileSync,
16
+ renameSync,
17
+ rmSync,
18
+ } from "node:fs";
12
19
  import { dirname, resolve } from "node:path";
13
20
  import { spawnSync } from "node:child_process";
14
21
 
@@ -190,7 +197,7 @@ function queryOpenIssuesByMarker(repo, marker) {
190
197
  * @param {string} marker
191
198
  * @param {string} command
192
199
  * @param {string} memoryFile
193
- * @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean }}
200
+ * @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean, readonly memoryFieldsRecorded: boolean }}
194
201
  */
195
202
  function runMissingMemoryVariant(repo, marker, command, memoryFile) {
196
203
  const backup = `${memoryFile}.project-ideation-idempotency-backup`;
@@ -208,9 +215,27 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
208
215
  marker,
209
216
  "after missing-memory run"
210
217
  );
218
+ const memoryRecreated = existsSync(memoryFile);
219
+ const memoryFieldsRecorded =
220
+ memoryRecreated &&
221
+ memoryContainsRunEntry(memoryFile, {
222
+ marker,
223
+ prdUrl: String(result.issue.url),
224
+ });
225
+
226
+ if (!memoryRecreated) {
227
+ fail(`Expected missing-memory run to recreate ${memoryFile}`);
228
+ }
229
+ if (!memoryFieldsRecorded) {
230
+ fail(
231
+ `Expected recreated memory to record marker, PRD URL, outcome, lifecycle_role, and source_agreement`
232
+ );
233
+ }
234
+
211
235
  return {
212
236
  ...result,
213
- memoryRecreated: existsSync(memoryFile),
237
+ memoryRecreated,
238
+ memoryFieldsRecorded,
214
239
  };
215
240
  } finally {
216
241
  if (existsSync(backup)) {
@@ -221,6 +246,24 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
221
246
  }
222
247
  }
223
248
 
249
+ /**
250
+ * @param {string} memoryFile
251
+ * @param {{ readonly marker: string, readonly prdUrl: string }} expected
252
+ * @returns {boolean}
253
+ */
254
+ function memoryContainsRunEntry(memoryFile, expected) {
255
+ const memory = readFileSync(memoryFile, "utf8");
256
+ const requiredFragments = [
257
+ expected.marker,
258
+ expected.prdUrl,
259
+ "outcome:",
260
+ "lifecycle_role:",
261
+ "source_agreement:",
262
+ ];
263
+
264
+ return requiredFragments.every(fragment => memory.includes(fragment));
265
+ }
266
+
224
267
  /**
225
268
  * @param {string} command
226
269
  * @param {readonly string[]} argv
@@ -161,6 +161,26 @@ For each idea in the creation set, invoke `/lisa:research` with:
161
161
  `lisa:prd-source-write`. `project-ideation` never writes to the source directly — it delegates, so
162
162
  the PRD source stays switchable per project. Capture each returned PRD ref / URL / role / outcome.
163
163
 
164
+ ### Optional Codex automation memory
165
+
166
+ When the run has a Codex automation id or memory path, maintain a concise local advisory ledger after
167
+ the PRD source write returns. Resolve the memory path in this order:
168
+
169
+ 1. explicit `memory_file=<path>` or `automation_memory=<path>` argument, when supplied;
170
+ 2. `$CODEX_AUTOMATION_MEMORY`, when set;
171
+ 3. `$CODEX_HOME/automations/<automation_id>/memory.md`, when `automation_id=<id>` or
172
+ `$CODEX_AUTOMATION_ID` is available.
173
+
174
+ Create the parent directory and `memory.md` if missing. Write one concise run entry keyed by the
175
+ dedupe marker and run timestamp. The entry must include the marker, PRD URL/ref, outcome
176
+ (`created | reused | updated | blocked`), lifecycle role (`draft | ready | blocked` or the returned
177
+ source role), and `source_agreement` (`github-source-wins`, `memory-created`, `memory-updated`, or
178
+ `memory-missing-runtime`). If memory says one thing but the PRD source search finds a matching open
179
+ PRD, GitHub/source truth wins: reuse the source PRD and update memory rather than creating a
180
+ duplicate. Keep memory advisory only; never use it to override lifecycle labels, source marker
181
+ matches, or the PRD source writer's returned role. Do not store secrets, tokens, full PRD bodies, or
182
+ private source excerpts in memory.
183
+
164
184
  ### Dedupe marker (stable, never title-based)
165
185
 
166
186
  Each created PRD carries the marker `[lisa-project-ideation] idea=<stable-key>`. Compute
@@ -43,7 +43,8 @@ The harness performs the acceptance check in three phases:
43
43
  require exactly one match.
44
44
  2. Run the same command again, then require the open marker count to remain one.
45
45
  3. Move the automation memory file aside, run the command again, require the marker count to remain
46
- one, and require the memory file to be recreated. The original memory file is restored at the end.
46
+ one, and require the memory file to be recreated with marker, PRD URL, outcome, lifecycle role,
47
+ and source agreement fields. The original memory file is restored at the end.
47
48
 
48
49
  ## Expected evidence
49
50
 
@@ -53,4 +54,4 @@ Capture the harness JSON output as:
53
54
  - `[EVIDENCE: memory-recreated-after-rerun]` from the missing-memory variant.
54
55
 
55
56
  The run passes only when all reported counts are `1`, the issue URL is the same across phases, and
56
- `memoryRecreated` is `true` when `--memory-file` is supplied.
57
+ `memoryRecreated` and `memoryFieldsRecorded` are `true` when `--memory-file` is supplied.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
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": "2.108.0",
3
+ "version": "2.109.0",
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-rails",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.108.0",
3
+ "version": "2.109.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -5,10 +5,17 @@
5
5
  * The harness runs a caller-supplied deterministic project-ideation command
6
6
  * twice, checks that exactly one open GitHub PRD contains the expected marker,
7
7
  * then optionally removes the automation memory file and verifies a third run
8
- * recreates memory without creating a duplicate PRD.
8
+ * recreates memory without creating a duplicate PRD. The recreated memory entry
9
+ * must include the advisory run fields project-ideation promises to write.
9
10
  */
10
11
 
11
- import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
12
+ import {
13
+ existsSync,
14
+ mkdirSync,
15
+ readFileSync,
16
+ renameSync,
17
+ rmSync,
18
+ } from "node:fs";
12
19
  import { dirname, resolve } from "node:path";
13
20
  import { spawnSync } from "node:child_process";
14
21
 
@@ -190,7 +197,7 @@ function queryOpenIssuesByMarker(repo, marker) {
190
197
  * @param {string} marker
191
198
  * @param {string} command
192
199
  * @param {string} memoryFile
193
- * @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean }}
200
+ * @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean, readonly memoryFieldsRecorded: boolean }}
194
201
  */
195
202
  function runMissingMemoryVariant(repo, marker, command, memoryFile) {
196
203
  const backup = `${memoryFile}.project-ideation-idempotency-backup`;
@@ -208,9 +215,27 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
208
215
  marker,
209
216
  "after missing-memory run"
210
217
  );
218
+ const memoryRecreated = existsSync(memoryFile);
219
+ const memoryFieldsRecorded =
220
+ memoryRecreated &&
221
+ memoryContainsRunEntry(memoryFile, {
222
+ marker,
223
+ prdUrl: String(result.issue.url),
224
+ });
225
+
226
+ if (!memoryRecreated) {
227
+ fail(`Expected missing-memory run to recreate ${memoryFile}`);
228
+ }
229
+ if (!memoryFieldsRecorded) {
230
+ fail(
231
+ `Expected recreated memory to record marker, PRD URL, outcome, lifecycle_role, and source_agreement`
232
+ );
233
+ }
234
+
211
235
  return {
212
236
  ...result,
213
- memoryRecreated: existsSync(memoryFile),
237
+ memoryRecreated,
238
+ memoryFieldsRecorded,
214
239
  };
215
240
  } finally {
216
241
  if (existsSync(backup)) {
@@ -221,6 +246,24 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
221
246
  }
222
247
  }
223
248
 
249
+ /**
250
+ * @param {string} memoryFile
251
+ * @param {{ readonly marker: string, readonly prdUrl: string }} expected
252
+ * @returns {boolean}
253
+ */
254
+ function memoryContainsRunEntry(memoryFile, expected) {
255
+ const memory = readFileSync(memoryFile, "utf8");
256
+ const requiredFragments = [
257
+ expected.marker,
258
+ expected.prdUrl,
259
+ "outcome:",
260
+ "lifecycle_role:",
261
+ "source_agreement:",
262
+ ];
263
+
264
+ return requiredFragments.every(fragment => memory.includes(fragment));
265
+ }
266
+
224
267
  /**
225
268
  * @param {string} command
226
269
  * @param {readonly string[]} argv
@@ -161,6 +161,26 @@ For each idea in the creation set, invoke `/lisa:research` with:
161
161
  `lisa:prd-source-write`. `project-ideation` never writes to the source directly — it delegates, so
162
162
  the PRD source stays switchable per project. Capture each returned PRD ref / URL / role / outcome.
163
163
 
164
+ ### Optional Codex automation memory
165
+
166
+ When the run has a Codex automation id or memory path, maintain a concise local advisory ledger after
167
+ the PRD source write returns. Resolve the memory path in this order:
168
+
169
+ 1. explicit `memory_file=<path>` or `automation_memory=<path>` argument, when supplied;
170
+ 2. `$CODEX_AUTOMATION_MEMORY`, when set;
171
+ 3. `$CODEX_HOME/automations/<automation_id>/memory.md`, when `automation_id=<id>` or
172
+ `$CODEX_AUTOMATION_ID` is available.
173
+
174
+ Create the parent directory and `memory.md` if missing. Write one concise run entry keyed by the
175
+ dedupe marker and run timestamp. The entry must include the marker, PRD URL/ref, outcome
176
+ (`created | reused | updated | blocked`), lifecycle role (`draft | ready | blocked` or the returned
177
+ source role), and `source_agreement` (`github-source-wins`, `memory-created`, `memory-updated`, or
178
+ `memory-missing-runtime`). If memory says one thing but the PRD source search finds a matching open
179
+ PRD, GitHub/source truth wins: reuse the source PRD and update memory rather than creating a
180
+ duplicate. Keep memory advisory only; never use it to override lifecycle labels, source marker
181
+ matches, or the PRD source writer's returned role. Do not store secrets, tokens, full PRD bodies, or
182
+ private source excerpts in memory.
183
+
164
184
  ### Dedupe marker (stable, never title-based)
165
185
 
166
186
  Each created PRD carries the marker `[lisa-project-ideation] idea=<stable-key>`. Compute
@@ -43,7 +43,8 @@ The harness performs the acceptance check in three phases:
43
43
  require exactly one match.
44
44
  2. Run the same command again, then require the open marker count to remain one.
45
45
  3. Move the automation memory file aside, run the command again, require the marker count to remain
46
- one, and require the memory file to be recreated. The original memory file is restored at the end.
46
+ one, and require the memory file to be recreated with marker, PRD URL, outcome, lifecycle role,
47
+ and source agreement fields. The original memory file is restored at the end.
47
48
 
48
49
  ## Expected evidence
49
50
 
@@ -53,4 +54,4 @@ Capture the harness JSON output as:
53
54
  - `[EVIDENCE: memory-recreated-after-rerun]` from the missing-memory variant.
54
55
 
55
56
  The run passes only when all reported counts are `1`, the issue URL is the same across phases, and
56
- `memoryRecreated` is `true` when `--memory-file` is supplied.
57
+ `memoryRecreated` and `memoryFieldsRecorded` are `true` when `--memory-file` is supplied.