@codyswann/lisa 2.219.0 → 2.220.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 (81) hide show
  1. package/all/copy-contents/gitignore +6 -1
  2. package/cdk/copy-overwrite/eslint.config.ts +25 -3
  3. package/cdk/copy-overwrite/eslint.slow.config.ts +28 -1
  4. package/dist/core/learnings-document.d.ts +22 -0
  5. package/dist/core/learnings-document.d.ts.map +1 -0
  6. package/dist/core/learnings-document.js +106 -0
  7. package/dist/core/learnings-document.js.map +1 -0
  8. package/dist/core/learnings-writer.d.ts +1 -13
  9. package/dist/core/learnings-writer.d.ts.map +1 -1
  10. package/dist/core/learnings-writer.js +2 -70
  11. package/dist/core/learnings-writer.js.map +1 -1
  12. package/expo/copy-overwrite/eslint.config.ts +26 -3
  13. package/expo/copy-overwrite/eslint.slow.config.ts +32 -1
  14. package/expo/copy-overwrite/scripts/check-e2e-coverage.mjs +1 -1
  15. package/expo/create-only/e2e.thresholds.json +3 -2
  16. package/expo/package-lisa/package.lisa.json +4 -2
  17. package/harper-fabric/copy-overwrite/eslint.config.ts +24 -2
  18. package/nestjs/copy-overwrite/eslint.config.ts +27 -3
  19. package/nestjs/copy-overwrite/eslint.slow.config.ts +33 -2
  20. package/package.json +2 -1
  21. package/phaser/copy-contents/gitignore +13 -0
  22. package/phaser/copy-overwrite/.github/workflows/ci.yml +3 -0
  23. package/phaser/copy-overwrite/.husky/pre-push.verify +4 -1
  24. package/phaser/copy-overwrite/eslint.config.ts +28 -3
  25. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-agy/plugin.json +1 -1
  28. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  31. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-expo-agy/plugin.json +1 -1
  38. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  48. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  53. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  58. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-rails-agy/plugin.json +1 -1
  63. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  68. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  73. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  75. package/rails/create-only/.github/workflows/ci.yml +12 -0
  76. package/scripts/check-learnings-budget.ts +219 -0
  77. package/typescript/copy-contents/.husky/pre-commit +4 -1
  78. package/typescript/copy-overwrite/audit.ignore.config.json +0 -5
  79. package/typescript/copy-overwrite/eslint.config.ts +27 -3
  80. package/typescript/copy-overwrite/eslint.slow.config.ts +32 -1
  81. package/typescript/package-lisa/package.lisa.json +4 -2
@@ -9,6 +9,8 @@ jspm_packages
9
9
  dist/
10
10
  dist
11
11
  .build/
12
+ # NestJS pre-push AppModule boot-check scratch dir (untracked; keep out of `git add -A`)
13
+ .build-boot/
12
14
  .serverless/
13
15
  .serverless
14
16
  cdk.out/
@@ -157,8 +159,11 @@ service-key.json
157
159
  # Task files
158
160
  .windsurfrules
159
161
  .taskmasterconfig
160
- !tasks/tasks.json
162
+ # Ignore stray tasks.json anywhere, but keep the tracked tasks/tasks.json.
163
+ # The broad ignore MUST precede the re-include — gitignore is last-match-wins,
164
+ # so a `tasks.json` rule after `!tasks/tasks.json` would re-ignore it.
161
165
  tasks.json
166
+ !tasks/tasks.json
162
167
 
163
168
  # AI Assistant and MCP
164
169
  **/.claude/settings.local.json
@@ -7,11 +7,15 @@
7
7
  * ESLint 9 Flat Config - Main Entry Point (CDK)
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";
15
19
  import { createRequire } from "module";
16
20
  import path from "path";
17
21
  import { fileURLToPath } from "url";
@@ -31,11 +35,29 @@ const thresholdsConfig = require("./eslint.thresholds.json");
31
35
  const __filename = fileURLToPath(import.meta.url);
32
36
  const __dirname = path.dirname(__filename);
33
37
 
34
- export default [
38
+ // Merge the optional project-owned eslint.ignore.config.local.json (create-only,
39
+ // never copy-overwritten) so host ignore customizations are not wiped when Lisa
40
+ // overwrites eslint.ignore.config.json on update.
41
+ const localIgnores: string[] = existsSync(
42
+ path.join(__dirname, "eslint.ignore.config.local.json")
43
+ )
44
+ ? (require("./eslint.ignore.config.local.json").ignores ?? [])
45
+ : [];
46
+
47
+ // Annotate with the factory's return type so `tsc --noEmit` under
48
+ // `declaration: true` does not surface @eslint/core's `RulesConfig` in the
49
+ // emitted declaration (TS2883). Do NOT import `Linter` from "eslint" directly —
50
+ // that trips knip's unlisted-dependency check.
51
+ const config: ReturnType<typeof getCdkConfig> = [
35
52
  ...getCdkConfig({
36
53
  tsconfigRootDir: __dirname,
37
- ignorePatterns: ignoreConfig.ignores || defaultIgnores,
54
+ ignorePatterns: [
55
+ ...(ignoreConfig.ignores || defaultIgnores),
56
+ ...localIgnores,
57
+ ],
38
58
  thresholds: { ...defaultThresholds, ...thresholdsConfig },
39
59
  }),
40
60
  ...localConfig,
41
61
  ];
62
+
63
+ export default config;
@@ -9,14 +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";
15
20
  import { createRequire } from "module";
21
+ import path from "path";
22
+ import { fileURLToPath } from "url";
16
23
 
17
24
  import { getSlowConfig } from "@codyswann/lisa/eslint/slow";
18
25
 
19
26
  const require = createRequire(import.meta.url);
20
27
  const ignoreConfig = require("./eslint.ignore.config.json");
21
28
 
22
- export default getSlowConfig({ ignorePatterns: ignoreConfig.ignores || [] });
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;
@@ -0,0 +1,22 @@
1
+ /** Pure parsing and rendering for the canonical project-learnings document. */
2
+ import { type LearningEntry } from "./learnings-contract.js";
3
+ /**
4
+ * Render the complete file in one stable, machine-readable Markdown form.
5
+ * @param entries - Validated entries to serialize
6
+ * @returns Canonical Markdown plus JSONL document
7
+ */
8
+ export declare function renderLearningsFile(entries: readonly LearningEntry[]): string;
9
+ /**
10
+ * Parse and revalidate an existing file before it participates in a write.
11
+ * @param content - Existing learnings document
12
+ * @returns Revalidated entries from the document
13
+ */
14
+ export declare function parseLearningsFile(content: string): LearningEntry[];
15
+ /**
16
+ * Enforce the shared entry-count and model-agnostic token upper bounds.
17
+ * @param content - Canonical document
18
+ * @param entryCount - Number of entries in the document
19
+ * @param context - Error-message prefix
20
+ */
21
+ export declare function assertDocumentBudget(content: string, entryCount: number, context: string): void;
22
+ //# sourceMappingURL=learnings-document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learnings-document.d.ts","sourceRoot":"","sources":["../../src/core/learnings-document.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAC;AAWjC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAG7E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAsBnE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,IAAI,CAYN"}
@@ -0,0 +1,106 @@
1
+ /** Pure parsing and rendering for the canonical project-learnings document. */
2
+ import { LEARNINGS_CONTRACT, estimateLearningTokens, } from "./learnings-contract.js";
3
+ import { validateLearningEntry } from "./learnings-entry.js";
4
+ const FILE_HEADER = `# Project Learnings
5
+
6
+ <!-- lisa-learnings-contract:v${LEARNINGS_CONTRACT.version} -->
7
+
8
+ `;
9
+ const JSON_FENCE_START = "```jsonl\n";
10
+ const JSON_FENCE_END = "\n```\n";
11
+ /**
12
+ * Render the complete file in one stable, machine-readable Markdown form.
13
+ * @param entries - Validated entries to serialize
14
+ * @returns Canonical Markdown plus JSONL document
15
+ */
16
+ export function renderLearningsFile(entries) {
17
+ const lines = entries.map(entry => JSON.stringify(entry)).join("\n");
18
+ return `${FILE_HEADER}${JSON_FENCE_START}${lines}${JSON_FENCE_END}`;
19
+ }
20
+ /**
21
+ * Parse and revalidate an existing file before it participates in a write.
22
+ * @param content - Existing learnings document
23
+ * @returns Revalidated entries from the document
24
+ */
25
+ export function parseLearningsFile(content) {
26
+ if (estimateLearningTokens(content) > LEARNINGS_CONTRACT.maxTokens) {
27
+ throw new Error(`Project learnings payload exceeds maxTokens ${LEARNINGS_CONTRACT.maxTokens}`);
28
+ }
29
+ if (!content.startsWith(`${FILE_HEADER}${JSON_FENCE_START}`) ||
30
+ !content.endsWith(JSON_FENCE_END)) {
31
+ throw new Error("Invalid project learnings file format");
32
+ }
33
+ const jsonStart = FILE_HEADER.length + JSON_FENCE_START.length;
34
+ const jsonEnd = content.length - JSON_FENCE_END.length;
35
+ const entries = parseJsonLines(content.slice(jsonStart, jsonEnd)).map(validateParsedLearningEntry);
36
+ if (new Set(entries.map(entry => entry.id)).size !== entries.length) {
37
+ throw new Error("Invalid project learnings payload: duplicate ids");
38
+ }
39
+ assertDocumentBudget(content, entries.length, "Project learnings payload");
40
+ return entries;
41
+ }
42
+ /**
43
+ * Enforce the shared entry-count and model-agnostic token upper bounds.
44
+ * @param content - Canonical document
45
+ * @param entryCount - Number of entries in the document
46
+ * @param context - Error-message prefix
47
+ */
48
+ export function assertDocumentBudget(content, entryCount, context) {
49
+ if (entryCount > LEARNINGS_CONTRACT.maxEntries) {
50
+ throw new Error(`${context} exceeds maxEntries: measured ${entryCount}, allowed ${LEARNINGS_CONTRACT.maxEntries}`);
51
+ }
52
+ const estimatedTokens = estimateLearningTokens(content);
53
+ if (estimatedTokens > LEARNINGS_CONTRACT.maxTokens) {
54
+ throw new Error(`${context} exceeds maxTokens ${LEARNINGS_CONTRACT.maxTokens} (measured ${estimatedTokens})`);
55
+ }
56
+ }
57
+ /**
58
+ * Add a stable entry identifier to validation failures.
59
+ * @param candidate - Parsed JSONL value
60
+ * @param index - Zero-based JSONL entry index
61
+ * @returns Validated learning entry
62
+ */
63
+ function validateParsedLearningEntry(candidate, index) {
64
+ try {
65
+ return validateLearningEntry(candidate);
66
+ }
67
+ catch (error) {
68
+ const id = readDiagnosticEntryId(candidate);
69
+ const label = id === undefined ? `entry ${index + 1}` : `entry ${JSON.stringify(id)}`;
70
+ const detail = error instanceof Error ? error.message : String(error);
71
+ throw new Error(`Invalid learning ${label}: ${detail}`);
72
+ }
73
+ }
74
+ /**
75
+ * Read a parsed entry id only when it is an inert own data property.
76
+ * @param candidate - Parsed JSONL value
77
+ * @returns Safe diagnostic id, when present
78
+ */
79
+ function readDiagnosticEntryId(candidate) {
80
+ if (candidate === null || typeof candidate !== "object") {
81
+ return undefined;
82
+ }
83
+ const descriptor = Object.getOwnPropertyDescriptor(candidate, "id");
84
+ return descriptor !== undefined &&
85
+ "value" in descriptor &&
86
+ typeof descriptor.value === "string"
87
+ ? descriptor.value
88
+ : undefined;
89
+ }
90
+ /**
91
+ * Parse the strict JSONL payload with a stable, user-facing error.
92
+ * @param payload - JSONL payload
93
+ * @returns Parsed unknown entry values
94
+ */
95
+ function parseJsonLines(payload) {
96
+ if (payload === "") {
97
+ return [];
98
+ }
99
+ try {
100
+ return payload.split("\n").map(line => JSON.parse(line));
101
+ }
102
+ catch {
103
+ throw new Error("Invalid project learnings JSONL payload");
104
+ }
105
+ }
106
+ //# sourceMappingURL=learnings-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learnings-document.js","sourceRoot":"","sources":["../../src/core/learnings-document.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { type LearningEntry } from "./learnings-contract.js";
2
1
  /**
3
2
  * Persist one already-selected learning without generating or truncating it.
4
3
  * @param projectRoot - Absolute path to the host project root
@@ -6,17 +5,6 @@ import { type LearningEntry } from "./learnings-contract.js";
6
5
  * @returns Absolute path to the persisted learnings file
7
6
  */
8
7
  export declare function persistLearningEntry(projectRoot: string, candidate: unknown): Promise<string>;
9
- /**
10
- * Render the complete file in one stable, machine-readable Markdown form.
11
- * @param entries - Validated entries to serialize
12
- * @returns Canonical Markdown plus JSONL document
13
- */
14
- export declare function renderLearningsFile(entries: readonly LearningEntry[]): string;
15
- /**
16
- * Parse and revalidate an existing file before it participates in a write.
17
- * @param content - Existing learnings document
18
- * @returns Revalidated entries from the document
19
- */
20
- export declare function parseLearningsFile(content: string): LearningEntry[];
8
+ export { parseLearningsFile, renderLearningsFile, } from "./learnings-document.js";
21
9
  export { validateLearningEntry } from "./learnings-entry.js";
22
10
  //# sourceMappingURL=learnings-writer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"learnings-writer.d.ts","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAC;AAqBjC;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAG7E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAsBnE;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"learnings-writer.d.ts","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -2,18 +2,11 @@
2
2
  import * as fse from "fs-extra";
3
3
  import { rename, writeFile } from "node:fs/promises";
4
4
  import * as path from "node:path";
5
- import { LEARNINGS_CONTRACT, estimateLearningTokens, } from "./learnings-contract.js";
5
+ import { assertDocumentBudget, parseLearningsFile, renderLearningsFile, } from "./learnings-document.js";
6
6
  import { validateLearningEntry } from "./learnings-entry.js";
7
7
  import { assertSafeLearningParents, readExistingLearnings, resolveSafeLearningTarget, } from "./learnings-file-safety.js";
8
8
  import { withLearningTargetLock } from "./learnings-lock.js";
9
9
  import { readProjectConfig, resolveProjectLearningsFile, } from "./project-config.js";
10
- const FILE_HEADER = `# Project Learnings
11
-
12
- <!-- lisa-learnings-contract:v${LEARNINGS_CONTRACT.version} -->
13
-
14
- `;
15
- const JSON_FENCE_START = "```jsonl\n";
16
- const JSON_FENCE_END = "\n```\n";
17
10
  /**
18
11
  * Persist one already-selected learning without generating or truncating it.
19
12
  * @param projectRoot - Absolute path to the host project root
@@ -47,37 +40,7 @@ export async function persistLearningEntry(projectRoot, candidate) {
47
40
  return target;
48
41
  });
49
42
  }
50
- /**
51
- * Render the complete file in one stable, machine-readable Markdown form.
52
- * @param entries - Validated entries to serialize
53
- * @returns Canonical Markdown plus JSONL document
54
- */
55
- export function renderLearningsFile(entries) {
56
- const lines = entries.map(entry => JSON.stringify(entry)).join("\n");
57
- return `${FILE_HEADER}${JSON_FENCE_START}${lines}${JSON_FENCE_END}`;
58
- }
59
- /**
60
- * Parse and revalidate an existing file before it participates in a write.
61
- * @param content - Existing learnings document
62
- * @returns Revalidated entries from the document
63
- */
64
- export function parseLearningsFile(content) {
65
- if (estimateLearningTokens(content) > LEARNINGS_CONTRACT.maxTokens) {
66
- throw new Error(`Project learnings payload exceeds maxTokens ${LEARNINGS_CONTRACT.maxTokens}`);
67
- }
68
- if (!content.startsWith(`${FILE_HEADER}${JSON_FENCE_START}`) ||
69
- !content.endsWith(JSON_FENCE_END)) {
70
- throw new Error("Invalid project learnings file format");
71
- }
72
- const jsonStart = FILE_HEADER.length + JSON_FENCE_START.length;
73
- const jsonEnd = content.length - JSON_FENCE_END.length;
74
- const entries = parseJsonLines(content.slice(jsonStart, jsonEnd)).map(validateLearningEntry);
75
- if (new Set(entries.map(entry => entry.id)).size !== entries.length) {
76
- throw new Error("Invalid project learnings payload: duplicate ids");
77
- }
78
- assertDocumentBudget(content, entries.length, "Project learnings payload");
79
- return entries;
80
- }
43
+ export { parseLearningsFile, renderLearningsFile, } from "./learnings-document.js";
81
44
  export { validateLearningEntry } from "./learnings-entry.js";
82
45
  /**
83
46
  * Build and budget-check the next canonical document.
@@ -94,35 +57,4 @@ function buildNextDocument(entries, entry) {
94
57
  assertDocumentBudget(rendered, nextEntries.length, "Learnings file");
95
58
  return rendered;
96
59
  }
97
- /**
98
- * Enforce the shared entry-count and model-agnostic token upper bounds.
99
- * @param content - Canonical document
100
- * @param entryCount - Number of entries in the document
101
- * @param context - Error-message prefix
102
- */
103
- function assertDocumentBudget(content, entryCount, context) {
104
- if (entryCount > LEARNINGS_CONTRACT.maxEntries) {
105
- throw new Error(`${context} exceeds maxEntries ${LEARNINGS_CONTRACT.maxEntries}`);
106
- }
107
- const estimatedTokens = estimateLearningTokens(content);
108
- if (estimatedTokens > LEARNINGS_CONTRACT.maxTokens) {
109
- throw new Error(`${context} exceeds maxTokens ${LEARNINGS_CONTRACT.maxTokens} (measured ${estimatedTokens})`);
110
- }
111
- }
112
- /**
113
- * Parse the strict JSONL payload with a stable, user-facing error.
114
- * @param payload - JSONL payload
115
- * @returns Parsed unknown entry values
116
- */
117
- function parseJsonLines(payload) {
118
- if (payload === "") {
119
- return [];
120
- }
121
- try {
122
- return payload.split("\n").map(line => JSON.parse(line));
123
- }
124
- catch {
125
- throw new Error("Invalid project learnings JSONL payload");
126
- }
127
- }
128
60
  //# sourceMappingURL=learnings-writer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"learnings-writer.js","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,GAAG;;gCAEY,kBAAkB,CAAC,OAAO;;CAEzD,CAAC;AACF,MAAM,gBAAgB,GAAG,YAAY,CAAC;AACtC,MAAM,cAAc,GAAG,SAAS,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,EACnB,SAAkB;IAElB,MAAM,KAAK,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,eAAe,GAAG,iBAAiB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACzD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,yBAAyB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC9E,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,OAAO,sBAAsB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAC3E,MAAM,QAAQ,GACZ,QAAQ,KAAK,SAAS;YACpB,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EACpB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,MAAM,CACtE,CAAC;QACF,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACvE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5D,MAAM,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAClC,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAiC;IACnE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrE,OAAO,GAAG,WAAW,GAAG,gBAAgB,GAAG,KAAK,GAAG,cAAc,EAAE,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,IAAI,sBAAsB,CAAC,OAAO,CAAC,GAAG,kBAAkB,CAAC,SAAS,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CACb,+CAA+C,kBAAkB,CAAC,SAAS,EAAE,CAC9E,CAAC;IACJ,CAAC;IACD,IACE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,gBAAgB,EAAE,CAAC;QACxD,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EACjC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;IAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC;IACvD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CACnE,qBAAqB,CACtB,CAAC;IACF,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IACD,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAC3E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;GAKG;AACH,SAAS,iBAAiB,CACxB,OAAiC,EACjC,KAAoB;IAEpB,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC3D,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAChC,CAAC;IACF,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAClD,oBAAoB,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACrE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAC3B,OAAe,EACf,UAAkB,EAClB,OAAe;IAEf,IAAI,UAAU,GAAG,kBAAkB,CAAC,UAAU,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,uBAAuB,kBAAkB,CAAC,UAAU,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,MAAM,eAAe,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,eAAe,GAAG,kBAAkB,CAAC,SAAS,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,sBAAsB,kBAAkB,CAAC,SAAS,cAAc,eAAe,GAAG,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,OAAe;IACrC,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"learnings-writer.js","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"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"}
@@ -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;