@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.
- package/all/copy-contents/gitignore +6 -1
- package/cdk/copy-overwrite/eslint.config.ts +25 -3
- package/cdk/copy-overwrite/eslint.slow.config.ts +28 -1
- package/dist/core/learnings-document.d.ts +22 -0
- package/dist/core/learnings-document.d.ts.map +1 -0
- package/dist/core/learnings-document.js +106 -0
- package/dist/core/learnings-document.js.map +1 -0
- package/dist/core/learnings-writer.d.ts +1 -13
- package/dist/core/learnings-writer.d.ts.map +1 -1
- package/dist/core/learnings-writer.js +2 -70
- package/dist/core/learnings-writer.js.map +1 -1
- package/expo/copy-overwrite/eslint.config.ts +26 -3
- package/expo/copy-overwrite/eslint.slow.config.ts +32 -1
- package/expo/copy-overwrite/scripts/check-e2e-coverage.mjs +1 -1
- package/expo/create-only/e2e.thresholds.json +3 -2
- package/expo/package-lisa/package.lisa.json +4 -2
- package/harper-fabric/copy-overwrite/eslint.config.ts +24 -2
- package/nestjs/copy-overwrite/eslint.config.ts +27 -3
- package/nestjs/copy-overwrite/eslint.slow.config.ts +33 -2
- package/package.json +2 -1
- package/phaser/copy-contents/gitignore +13 -0
- package/phaser/copy-overwrite/.github/workflows/ci.yml +3 -0
- package/phaser/copy-overwrite/.husky/pre-push.verify +4 -1
- package/phaser/copy-overwrite/eslint.config.ts +28 -3
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/rails/create-only/.github/workflows/ci.yml +12 -0
- package/scripts/check-learnings-budget.ts +219 -0
- package/typescript/copy-contents/.husky/pre-commit +4 -1
- package/typescript/copy-overwrite/audit.ignore.config.json +0 -5
- package/typescript/copy-overwrite/eslint.config.ts +27 -3
- package/typescript/copy-overwrite/eslint.slow.config.ts +32 -1
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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":"
|
|
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 {
|
|
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;
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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;
|
|
@@ -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":
|
|
4
|
+
"routes": 0
|
|
4
5
|
},
|
|
5
6
|
"maestro": {
|
|
6
|
-
"routes":
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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;
|