@codyswann/lisa 2.218.0 → 2.219.1

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 (84) hide show
  1. package/all/copy-contents/gitignore +6 -1
  2. package/all/create-only/.claude/rules/PROJECT_LEARNINGS.md +7 -0
  3. package/cdk/copy-overwrite/eslint.config.ts +25 -3
  4. package/cdk/copy-overwrite/eslint.slow.config.ts +28 -1
  5. package/dist/core/lisa.d.ts +26 -0
  6. package/dist/core/lisa.d.ts.map +1 -1
  7. package/dist/core/lisa.js +82 -8
  8. package/dist/core/lisa.js.map +1 -1
  9. package/dist/core/project-config.d.ts.map +1 -1
  10. package/dist/core/project-config.js +4 -0
  11. package/dist/core/project-config.js.map +1 -1
  12. package/dist/strategies/create-only.d.ts +10 -0
  13. package/dist/strategies/create-only.d.ts.map +1 -1
  14. package/dist/strategies/create-only.js +27 -7
  15. package/dist/strategies/create-only.js.map +1 -1
  16. package/expo/copy-overwrite/eslint.config.ts +26 -3
  17. package/expo/copy-overwrite/eslint.slow.config.ts +32 -1
  18. package/expo/copy-overwrite/scripts/check-e2e-coverage.mjs +1 -1
  19. package/expo/create-only/e2e.thresholds.json +3 -2
  20. package/expo/package-lisa/package.lisa.json +4 -2
  21. package/harper-fabric/copy-overwrite/eslint.config.ts +24 -2
  22. package/nestjs/copy-overwrite/eslint.config.ts +27 -3
  23. package/nestjs/copy-overwrite/eslint.slow.config.ts +33 -2
  24. package/package.json +1 -1
  25. package/phaser/copy-contents/gitignore +13 -0
  26. package/phaser/copy-overwrite/.github/workflows/ci.yml +3 -0
  27. package/phaser/copy-overwrite/.husky/pre-push.verify +4 -1
  28. package/phaser/copy-overwrite/eslint.config.ts +28 -3
  29. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  31. package/plugins/lisa-agy/plugin.json +1 -1
  32. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  35. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  41. package/plugins/lisa-expo-agy/plugin.json +1 -1
  42. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  52. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  57. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  62. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails-agy/plugin.json +1 -1
  67. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  72. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  77. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  79. package/rails/create-only/.github/workflows/ci.yml +12 -0
  80. package/typescript/copy-contents/.husky/pre-commit +4 -1
  81. package/typescript/copy-overwrite/audit.ignore.config.json +0 -5
  82. package/typescript/copy-overwrite/eslint.config.ts +27 -3
  83. package/typescript/copy-overwrite/eslint.slow.config.ts +32 -1
  84. package/typescript/package-lisa/package.lisa.json +4 -2
@@ -1,12 +1,21 @@
1
1
  import type { FileOperationResult } from "../core/config.js";
2
2
  import type { ICopyStrategy, StrategyContext } from "./strategy.interface.js";
3
+ /** Copy one source into a destination that must not already exist. */
4
+ type ExclusiveCopyFile = (sourcePath: string, destPath: string, mode: number) => Promise<void>;
3
5
  /**
4
6
  * Create-only strategy: Create file if not exists, never update
5
7
  * - Create if not exists
6
8
  * - Skip silently if exists (whether identical or different)
7
9
  */
8
10
  export declare class CreateOnlyStrategy implements ICopyStrategy {
11
+ private readonly copyFileExclusive;
9
12
  readonly name: "create-only";
13
+ /**
14
+ * Create a strategy backed by an atomic exclusive-copy primitive.
15
+ *
16
+ * @param copyFileExclusive - Injectable exclusive copy primitive for tests
17
+ */
18
+ constructor(copyFileExclusive?: ExclusiveCopyFile);
10
19
  /**
11
20
  * Apply create-only strategy: Create file if not exists, never update
12
21
  * @param sourcePath - Source file path
@@ -17,4 +26,5 @@ export declare class CreateOnlyStrategy implements ICopyStrategy {
17
26
  */
18
27
  apply(sourcePath: string, destPath: string, relativePath: string, context: StrategyContext): Promise<FileOperationResult>;
19
28
  }
29
+ export {};
20
30
  //# sourceMappingURL=create-only.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-only.d.ts","sourceRoot":"","sources":["../../src/strategies/create-only.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG9E;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IACtD,QAAQ,CAAC,IAAI,EAAG,aAAa,CAAU;IAEvC;;;;;;;OAOG;IACG,KAAK,CACT,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CAchC"}
1
+ {"version":3,"file":"create-only.d.ts","sourceRoot":"","sources":["../../src/strategies/create-only.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG9E,sEAAsE;AACtE,KAAK,iBAAiB,GAAG,CACvB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IASpD,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IARpC,QAAQ,CAAC,IAAI,EAAG,aAAa,CAAU;IAEvC;;;;OAIG;gBAEgB,iBAAiB,GAAE,iBAA4B;IAGlE;;;;;;;OAOG;IACG,KAAK,CACT,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CA0BhC"}
@@ -1,4 +1,5 @@
1
1
  import * as fse from "fs-extra";
2
+ import { constants } from "node:fs";
2
3
  import { copyFile } from "node:fs/promises";
3
4
  import { ensureParentDir } from "../utils/file-operations.js";
4
5
  /**
@@ -7,7 +8,16 @@ import { ensureParentDir } from "../utils/file-operations.js";
7
8
  * - Skip silently if exists (whether identical or different)
8
9
  */
9
10
  export class CreateOnlyStrategy {
11
+ copyFileExclusive;
10
12
  name = "create-only";
13
+ /**
14
+ * Create a strategy backed by an atomic exclusive-copy primitive.
15
+ *
16
+ * @param copyFileExclusive - Injectable exclusive copy primitive for tests
17
+ */
18
+ constructor(copyFileExclusive = copyFile) {
19
+ this.copyFileExclusive = copyFileExclusive;
20
+ }
11
21
  /**
12
22
  * Apply create-only strategy: Create file if not exists, never update
13
23
  * @param sourcePath - Source file path
@@ -18,15 +28,25 @@ export class CreateOnlyStrategy {
18
28
  */
19
29
  async apply(sourcePath, destPath, relativePath, context) {
20
30
  const { config } = context;
21
- const destExists = await fse.pathExists(destPath);
22
- if (!destExists) {
23
- if (!config.dryRun) {
24
- await ensureParentDir(destPath);
25
- await copyFile(sourcePath, destPath);
26
- }
31
+ if (config.dryRun) {
32
+ const destExists = await fse.pathExists(destPath);
33
+ return {
34
+ relativePath,
35
+ strategy: this.name,
36
+ action: destExists ? "skipped" : "created",
37
+ };
38
+ }
39
+ await ensureParentDir(destPath);
40
+ try {
41
+ await this.copyFileExclusive(sourcePath, destPath, constants.COPYFILE_EXCL);
27
42
  return { relativePath, strategy: this.name, action: "created" };
28
43
  }
29
- return { relativePath, strategy: this.name, action: "skipped" };
44
+ catch (error) {
45
+ if (error.code === "EEXIST") {
46
+ return { relativePath, strategy: this.name, action: "skipped" };
47
+ }
48
+ throw error;
49
+ }
30
50
  }
31
51
  }
32
52
  //# sourceMappingURL=create-only.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-only.js","sourceRoot":"","sources":["../../src/strategies/create-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IACpB,IAAI,GAAG,aAAsB,CAAC;IAEvC;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CACT,UAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAAwB;QAExB,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAC3B,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAElD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAChC,MAAM,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACvC,CAAC;YACD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAClE,CAAC;QAED,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAClE,CAAC;CACF"}
1
+ {"version":3,"file":"create-only.js","sourceRoot":"","sources":["../../src/strategies/create-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAS9D;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IASV;IARV,IAAI,GAAG,aAAsB,CAAC;IAEvC;;;;OAIG;IACH,YACmB,oBAAuC,QAAQ;QAA/C,sBAAiB,GAAjB,iBAAiB,CAA8B;IAC/D,CAAC;IAEJ;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CACT,UAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAAwB;QAExB,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAC3B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAClD,OAAO;gBACL,YAAY;gBACZ,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;aAC3C,CAAC;QACJ,CAAC;QAED,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,iBAAiB,CAC1B,UAAU,EACV,QAAQ,EACR,SAAS,CAAC,aAAa,CACxB,CAAC;YACF,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACvD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAClE,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -7,12 +7,16 @@
7
7
  * ESLint 9 Flat Config - Main Entry Point (Expo)
8
8
  *
9
9
  * Thin wrapper around @codyswann/lisa eslint config factory.
10
- * Customize via eslint.config.local.ts and eslint.thresholds.json.
10
+ * Customize via eslint.config.local.ts, eslint.thresholds.json, and the
11
+ * optional (create-only) eslint.ignore.config.local.json — its `ignores` are
12
+ * merged into the ignore list so host customizations survive the
13
+ * copy-overwrite of eslint.ignore.config.json on each Lisa update.
11
14
  *
12
15
  * @see https://eslint.org/docs/latest/use/configure/configuration-files-new
13
16
  * @module eslint.config
14
17
  */
15
18
  import { existsSync } from "fs";
19
+ import { createRequire } from "module";
16
20
  import path from "path";
17
21
  import { fileURLToPath } from "url";
18
22
 
@@ -26,6 +30,7 @@ import ignoreConfig from "./eslint.ignore.config.json" with { type: "json" };
26
30
  import thresholdsConfig from "./eslint.thresholds.json" with { type: "json" };
27
31
  import localConfig from "./eslint.config.local";
28
32
 
33
+ const require = createRequire(import.meta.url);
29
34
  const __filename = fileURLToPath(import.meta.url);
30
35
  const __dirname = path.dirname(__filename);
31
36
 
@@ -33,12 +38,30 @@ const __dirname = path.dirname(__filename);
33
38
  // ui-standards, and view-memo overrides target `src/...` instead of the root.
34
39
  const sourceRoot = existsSync(path.join(__dirname, "src", "app")) ? "src/" : "";
35
40
 
36
- export default [
41
+ // Merge the optional project-owned eslint.ignore.config.local.json (create-only,
42
+ // never copy-overwritten) so host ignore customizations are not wiped when Lisa
43
+ // overwrites eslint.ignore.config.json on update.
44
+ const localIgnores: string[] = existsSync(
45
+ path.join(__dirname, "eslint.ignore.config.local.json")
46
+ )
47
+ ? (require("./eslint.ignore.config.local.json").ignores ?? [])
48
+ : [];
49
+
50
+ // Annotate with the factory's return type so `tsc --noEmit` under
51
+ // `declaration: true` does not surface @eslint/core's `RulesConfig` in the
52
+ // emitted declaration (TS2883). Do NOT import `Linter` from "eslint" directly —
53
+ // that trips knip's unlisted-dependency check.
54
+ const config: ReturnType<typeof getExpoConfig> = [
37
55
  ...getExpoConfig({
38
56
  tsconfigRootDir: __dirname,
39
- ignorePatterns: ignoreConfig.ignores || defaultIgnores,
57
+ ignorePatterns: [
58
+ ...(ignoreConfig.ignores || defaultIgnores),
59
+ ...localIgnores,
60
+ ],
40
61
  thresholds: { ...defaultThresholds, ...thresholdsConfig },
41
62
  sourceRoot,
42
63
  }),
43
64
  ...localConfig,
44
65
  ];
66
+
67
+ export default config;
@@ -9,10 +9,41 @@
9
9
  * Thin wrapper around @codyswann/lisa slow eslint config factory.
10
10
  * Run periodically via `lint:slow` rather than on every lint pass.
11
11
  *
12
+ * The optional (create-only) eslint.ignore.config.local.json `ignores` are
13
+ * merged into the ignore list so host customizations survive the
14
+ * copy-overwrite of eslint.ignore.config.json on each Lisa update.
15
+ *
12
16
  * @see https://github.com/import-js/eslint-plugin-import
13
17
  * @module eslint.slow.config
14
18
  */
19
+ import { existsSync } from "fs";
20
+ import { createRequire } from "module";
21
+ import path from "path";
22
+ import { fileURLToPath } from "url";
23
+
15
24
  import { getSlowConfig } from "@codyswann/lisa/eslint/slow";
25
+
16
26
  import ignoreConfig from "./eslint.ignore.config.json" with { type: "json" };
17
27
 
18
- export default getSlowConfig({ ignorePatterns: ignoreConfig.ignores || [] });
28
+ const require = createRequire(import.meta.url);
29
+ const __filename = fileURLToPath(import.meta.url);
30
+ const __dirname = path.dirname(__filename);
31
+
32
+ // Merge the optional project-owned eslint.ignore.config.local.json (create-only,
33
+ // never copy-overwritten) so host ignore customizations are not wiped when Lisa
34
+ // overwrites eslint.ignore.config.json on update.
35
+ const localIgnores: string[] = existsSync(
36
+ path.join(__dirname, "eslint.ignore.config.local.json")
37
+ )
38
+ ? (require("./eslint.ignore.config.local.json").ignores ?? [])
39
+ : [];
40
+
41
+ // Annotate with the factory's return type so `tsc --noEmit` under
42
+ // `declaration: true` does not surface @eslint/core's `RulesConfig` in the
43
+ // emitted declaration (TS2883). Do NOT import `Linter` from "eslint" directly —
44
+ // that trips knip's unlisted-dependency check.
45
+ const config: ReturnType<typeof getSlowConfig> = getSlowConfig({
46
+ ignorePatterns: [...(ignoreConfig.ignores || []), ...localIgnores],
47
+ });
48
+
49
+ export default config;
@@ -81,7 +81,7 @@ export function enumerateRoutes(files) {
81
81
  const routes = files
82
82
  .map(file => routeFromFile(file))
83
83
  .filter(route => route !== null);
84
- return [...new Set(routes)].sort();
84
+ return [...new Set(routes)].sort((a, b) => a.localeCompare(b));
85
85
  }
86
86
 
87
87
  /**
@@ -1,8 +1,9 @@
1
1
  {
2
+ "_comment": "Shipped defaults are 0/0 so adopting Lisa on a brownfield Expo app does not red-gate CI before any e2e specs exist. Treat these as a ratchet: raise each value toward 100 as Playwright/Maestro coverage grows, and never lower an achieved floor. This file is create-only — Lisa writes it once and never overwrites your tuned values.",
2
3
  "playwright": {
3
- "routes": 80
4
+ "routes": 0
4
5
  },
5
6
  "maestro": {
6
- "routes": 80
7
+ "routes": 0
7
8
  }
8
9
  }
@@ -98,14 +98,16 @@
98
98
  "@isaacs/brace-expansion": "^5.0.1",
99
99
  "eslint-plugin-react-hooks": "^7.0.0",
100
100
  "fast-xml-parser": "^5.3.6",
101
- "tar": ">=7.5.11"
101
+ "tar": ">=7.5.11",
102
+ "websocket-driver": ">=0.7.5"
102
103
  },
103
104
  "overrides": {
104
105
  "@isaacs/brace-expansion": "^5.0.1",
105
106
  "eslint-plugin-react-hooks": "^7.0.0",
106
107
  "fast-xml-parser": "^5.3.6",
107
108
  "zod-validation-error": "^4.0.0",
108
- "tar": ">=7.5.11"
109
+ "tar": ">=7.5.11",
110
+ "websocket-driver": ">=0.7.5"
109
111
  }
110
112
  },
111
113
  "defaults": {
@@ -7,11 +7,16 @@
7
7
  * ESLint 9 Flat Config
8
8
  *
9
9
  * Thin wrapper around @codyswann/lisa Harper/Fabric config factory.
10
- * Customize via eslint.config.local.ts and eslint.thresholds.json.
10
+ * Customize via eslint.config.local.ts, eslint.thresholds.json, and the
11
+ * optional (create-only) eslint.ignore.config.local.json — its `ignores` are
12
+ * merged into the ignore list so host customizations survive the
13
+ * copy-overwrite of eslint.ignore.config.json on each Lisa update.
11
14
  *
12
15
  * @see https://eslint.org/docs/latest/use/configure/configuration-files-new
13
16
  * @module eslint.config
14
17
  */
18
+ import { existsSync } from "fs";
19
+ import { createRequire } from "module";
15
20
  import path from "path";
16
21
  import { fileURLToPath } from "url";
17
22
 
@@ -25,17 +30,34 @@ import ignoreConfig from "./eslint.ignore.config.json" with { type: "json" };
25
30
  import thresholdsConfig from "./eslint.thresholds.json" with { type: "json" };
26
31
  import localConfig from "./eslint.config.local";
27
32
 
33
+ const require = createRequire(import.meta.url);
28
34
  const __filename = fileURLToPath(import.meta.url);
29
35
  const __dirname = path.dirname(__filename);
30
36
 
31
- export default [
37
+ // Merge the optional project-owned eslint.ignore.config.local.json (create-only,
38
+ // never copy-overwritten) so host ignore customizations are not wiped when Lisa
39
+ // overwrites eslint.ignore.config.json on update.
40
+ const localIgnores: string[] = existsSync(
41
+ path.join(__dirname, "eslint.ignore.config.local.json")
42
+ )
43
+ ? (require("./eslint.ignore.config.local.json").ignores ?? [])
44
+ : [];
45
+
46
+ // Annotate with the factory's return type so `tsc --noEmit` under
47
+ // `declaration: true` does not surface @eslint/core's `RulesConfig` in the
48
+ // emitted declaration (TS2883). Do NOT import `Linter` from "eslint" directly —
49
+ // that trips knip's unlisted-dependency check.
50
+ const config: ReturnType<typeof getHarperFabricConfig> = [
32
51
  ...getHarperFabricConfig({
33
52
  tsconfigRootDir: __dirname,
34
53
  ignorePatterns: [
35
54
  ...defaultHarperFabricIgnores,
36
55
  ...(ignoreConfig.ignores || []),
56
+ ...localIgnores,
37
57
  ],
38
58
  thresholds: { ...defaultThresholds, ...thresholdsConfig },
39
59
  }),
40
60
  ...localConfig,
41
61
  ];
62
+
63
+ export default config;
@@ -7,11 +7,16 @@
7
7
  * ESLint 9 Flat Config - Main Entry Point (NestJS)
8
8
  *
9
9
  * Thin wrapper around @codyswann/lisa eslint config factory.
10
- * Customize via eslint.config.local.ts and eslint.thresholds.json.
10
+ * Customize via eslint.config.local.ts, eslint.thresholds.json, and the
11
+ * optional (create-only) eslint.ignore.config.local.json — its `ignores` are
12
+ * merged into the ignore list so host customizations survive the
13
+ * copy-overwrite of eslint.ignore.config.json on each Lisa update.
11
14
  *
12
15
  * @see https://eslint.org/docs/latest/use/configure/configuration-files-new
13
16
  * @module eslint.config
14
17
  */
18
+ import { existsSync } from "fs";
19
+ import { createRequire } from "module";
15
20
  import path from "path";
16
21
  import { fileURLToPath } from "url";
17
22
 
@@ -25,14 +30,33 @@ import ignoreConfig from "./eslint.ignore.config.json" with { type: "json" };
25
30
  import thresholdsConfig from "./eslint.thresholds.json" with { type: "json" };
26
31
  import localConfig from "./eslint.config.local";
27
32
 
33
+ const require = createRequire(import.meta.url);
28
34
  const __filename = fileURLToPath(import.meta.url);
29
35
  const __dirname = path.dirname(__filename);
30
36
 
31
- export default [
37
+ // Merge the optional project-owned eslint.ignore.config.local.json (create-only,
38
+ // never copy-overwritten) so host ignore customizations are not wiped when Lisa
39
+ // overwrites eslint.ignore.config.json on update.
40
+ const localIgnores: string[] = existsSync(
41
+ path.join(__dirname, "eslint.ignore.config.local.json")
42
+ )
43
+ ? (require("./eslint.ignore.config.local.json").ignores ?? [])
44
+ : [];
45
+
46
+ // Annotate with the factory's return type so `tsc --noEmit` under
47
+ // `declaration: true` does not surface @eslint/core's `RulesConfig` in the
48
+ // emitted declaration (TS2883). Do NOT import `Linter` from "eslint" directly —
49
+ // that trips knip's unlisted-dependency check.
50
+ const config: ReturnType<typeof getNestjsConfig> = [
32
51
  ...getNestjsConfig({
33
52
  tsconfigRootDir: __dirname,
34
- ignorePatterns: ignoreConfig.ignores || defaultIgnores,
53
+ ignorePatterns: [
54
+ ...(ignoreConfig.ignores || defaultIgnores),
55
+ ...localIgnores,
56
+ ],
35
57
  thresholds: { ...defaultThresholds, ...thresholdsConfig },
36
58
  }),
37
59
  ...localConfig,
38
60
  ];
61
+
62
+ export default config;
@@ -13,14 +13,43 @@
13
13
  * files, since NestJS uses `forwardRef()` to handle circular module
14
14
  * dependencies at runtime.
15
15
  *
16
+ * The optional (create-only) eslint.ignore.config.local.json `ignores` are
17
+ * merged into the ignore list so host customizations survive the
18
+ * copy-overwrite of eslint.ignore.config.json on each Lisa update.
19
+ *
16
20
  * @see https://github.com/import-js/eslint-plugin-import
17
21
  * @module eslint.slow.config
18
22
  */
23
+ import { existsSync } from "fs";
24
+ import { createRequire } from "module";
25
+ import path from "path";
26
+ import { fileURLToPath } from "url";
27
+
19
28
  import { getSlowConfig } from "@codyswann/lisa/eslint/slow";
29
+
20
30
  import ignoreConfig from "./eslint.ignore.config.json" with { type: "json" };
21
31
 
22
- export default [
23
- ...getSlowConfig({ ignorePatterns: ignoreConfig.ignores || [] }),
32
+ const require = createRequire(import.meta.url);
33
+ const __filename = fileURLToPath(import.meta.url);
34
+ const __dirname = path.dirname(__filename);
35
+
36
+ // Merge the optional project-owned eslint.ignore.config.local.json (create-only,
37
+ // never copy-overwritten) so host ignore customizations are not wiped when Lisa
38
+ // overwrites eslint.ignore.config.json on update.
39
+ const localIgnores: string[] = existsSync(
40
+ path.join(__dirname, "eslint.ignore.config.local.json")
41
+ )
42
+ ? (require("./eslint.ignore.config.local.json").ignores ?? [])
43
+ : [];
44
+
45
+ // Annotate with the factory's return type so `tsc --noEmit` under
46
+ // `declaration: true` does not surface @eslint/core's `RulesConfig` in the
47
+ // emitted declaration (TS2883). Do NOT import `Linter` from "eslint" directly —
48
+ // that trips knip's unlisted-dependency check.
49
+ const config: ReturnType<typeof getSlowConfig> = [
50
+ ...getSlowConfig({
51
+ ignorePatterns: [...(ignoreConfig.ignores || []), ...localIgnores],
52
+ }),
24
53
 
25
54
  // NestJS modules use forwardRef() to resolve circular dependencies at runtime.
26
55
  // ESLint's static import/no-cycle analysis cannot understand this pattern.
@@ -31,3 +60,5 @@ export default [
31
60
  },
32
61
  },
33
62
  ];
63
+
64
+ export default config;
package/package.json CHANGED
@@ -101,7 +101,7 @@
101
101
  "form-data": ">=4.0.6"
102
102
  },
103
103
  "name": "@codyswann/lisa",
104
- "version": "2.218.0",
104
+ "version": "2.219.1",
105
105
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
106
106
  "main": "dist/index.js",
107
107
  "exports": {
@@ -2,4 +2,17 @@
2
2
  .vite/
3
3
  .lisabak/
4
4
  .lisabak
5
+
6
+ # The base guardrails block ignores any `example/` directory, which would
7
+ # otherwise re-ignore the committed Phaser example art. Re-include the example
8
+ # asset folders here (this block is written after the base block, so
9
+ # last-match-wins keeps them tracked). Both the folder and a `/**` recursion
10
+ # are needed — git will not re-include files inside an ignored directory
11
+ # unless the directory itself is un-ignored first.
12
+ !assets/src/sprites/example/
13
+ !assets/src/sprites/example/**
14
+ !assets/src/images/example/
15
+ !assets/src/images/example/**
16
+ !public/assets/images/example/
17
+ !public/assets/images/example/**
5
18
  # END: AI GUARDRAILS PHASER
@@ -5,6 +5,9 @@ name: 🔍 CI Quality Checks
5
5
 
6
6
  on:
7
7
  pull_request:
8
+ # Include labeled/unlabeled so toggling the verification-exempt label
9
+ # re-runs CI (the verification gate keys on PR labels).
10
+ types: [opened, synchronize, reopened, labeled, unlabeled]
8
11
  workflow_dispatch:
9
12
 
10
13
  jobs:
@@ -15,5 +15,8 @@
15
15
  #
16
16
  if [ -f scripts/check-verification-coverage.mjs ]; then
17
17
  echo "✅ Verification (UAT) coverage check..."
18
- node scripts/check-verification-coverage.mjs || exit 1
18
+ # No `|| exit 1`: this file is sourced by .husky/pre-push. A hard `exit`
19
+ # here would terminate the parent hook before it can print its own failure
20
+ # message — let the non-zero status bubble up to the caller's handler.
21
+ node scripts/check-verification-coverage.mjs
19
22
  fi
@@ -7,11 +7,16 @@
7
7
  * ESLint 9 Flat Config
8
8
  *
9
9
  * Thin wrapper around @codyswann/lisa Phaser config factory.
10
- * Customize via eslint.config.local.ts and eslint.thresholds.json.
10
+ * Customize via eslint.config.local.ts, eslint.thresholds.json, and the
11
+ * optional (create-only) eslint.ignore.config.local.json — its `ignores` are
12
+ * merged into the ignore list so host customizations survive the
13
+ * copy-overwrite of eslint.ignore.config.json on each Lisa update.
11
14
  *
12
15
  * @see https://eslint.org/docs/latest/use/configure/configuration-files-new
13
16
  * @module eslint.config
14
17
  */
18
+ import { existsSync } from "fs";
19
+ import { createRequire } from "module";
15
20
  import path from "path";
16
21
  import { fileURLToPath } from "url";
17
22
 
@@ -25,14 +30,34 @@ import ignoreConfig from "./eslint.ignore.config.json" with { type: "json" };
25
30
  import thresholdsConfig from "./eslint.thresholds.json" with { type: "json" };
26
31
  import localConfig from "./eslint.config.local";
27
32
 
33
+ const require = createRequire(import.meta.url);
28
34
  const __filename = fileURLToPath(import.meta.url);
29
35
  const __dirname = path.dirname(__filename);
30
36
 
31
- export default [
37
+ // Merge the optional project-owned eslint.ignore.config.local.json (create-only,
38
+ // never copy-overwritten) so host ignore customizations are not wiped when Lisa
39
+ // overwrites eslint.ignore.config.json on update.
40
+ const localIgnores: string[] = existsSync(
41
+ path.join(__dirname, "eslint.ignore.config.local.json")
42
+ )
43
+ ? (require("./eslint.ignore.config.local.json").ignores ?? [])
44
+ : [];
45
+
46
+ // Annotate with the factory's return type so `tsc --noEmit` under
47
+ // `declaration: true` does not surface @eslint/core's `RulesConfig` in the
48
+ // emitted declaration (TS2883). Do NOT import `Linter` from "eslint" directly —
49
+ // that trips knip's unlisted-dependency check.
50
+ const config: ReturnType<typeof getPhaserConfig> = [
32
51
  ...getPhaserConfig({
33
52
  tsconfigRootDir: __dirname,
34
- ignorePatterns: [...defaultPhaserIgnores, ...(ignoreConfig.ignores || [])],
53
+ ignorePatterns: [
54
+ ...defaultPhaserIgnores,
55
+ ...(ignoreConfig.ignores || []),
56
+ ...localIgnores,
57
+ ],
35
58
  thresholds: { ...defaultThresholds, ...thresholdsConfig },
36
59
  }),
37
60
  ...localConfig,
38
61
  ];
62
+
63
+ export default config;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
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-cdk",
3
- "version": "2.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
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-cdk",
3
- "version": "2.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
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",
3
- "version": "2.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
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-expo",
3
- "version": "2.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
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-expo",
3
- "version": "2.218.0",
3
+ "version": "2.219.1",
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.218.0",
3
+ "version": "2.219.1",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"