@codyswann/lisa 2.265.0 → 2.267.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/dist/core/lisa-plugin-selection.d.ts +11 -0
- package/dist/core/lisa-plugin-selection.d.ts.map +1 -1
- package/dist/core/lisa-plugin-selection.js +11 -1
- package/dist/core/lisa-plugin-selection.js.map +1 -1
- package/dist/core/lisa.d.ts +0 -41
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +23 -104
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/template-ownership.d.ts +46 -0
- package/dist/core/template-ownership.d.ts.map +1 -0
- package/dist/core/template-ownership.js +123 -0
- package/dist/core/template-ownership.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +38 -7
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/health/deadline.d.ts +25 -0
- package/dist/health/deadline.d.ts.map +1 -0
- package/dist/health/deadline.js +50 -0
- package/dist/health/deadline.js.map +1 -0
- package/dist/health/deterministic.d.ts +34 -0
- package/dist/health/deterministic.d.ts.map +1 -0
- package/dist/health/deterministic.js +206 -0
- package/dist/health/deterministic.js.map +1 -0
- package/dist/health/finding-utils.d.ts +17 -0
- package/dist/health/finding-utils.d.ts.map +1 -0
- package/dist/health/finding-utils.js +25 -0
- package/dist/health/finding-utils.js.map +1 -0
- package/dist/health/governance-probes.d.ts +51 -0
- package/dist/health/governance-probes.d.ts.map +1 -0
- package/dist/health/governance-probes.js +94 -0
- package/dist/health/governance-probes.js.map +1 -0
- package/dist/health/hook-inspection.d.ts +35 -0
- package/dist/health/hook-inspection.d.ts.map +1 -0
- package/dist/health/hook-inspection.js +123 -0
- package/dist/health/hook-inspection.js.map +1 -0
- package/dist/health/index.d.ts +1 -0
- package/dist/health/index.d.ts.map +1 -1
- package/dist/health/index.js +1 -0
- package/dist/health/index.js.map +1 -1
- package/dist/health/plugin-inspection.d.ts +37 -0
- package/dist/health/plugin-inspection.d.ts.map +1 -0
- package/dist/health/plugin-inspection.js +128 -0
- package/dist/health/plugin-inspection.js.map +1 -0
- package/dist/health/project-probes.d.ts +34 -0
- package/dist/health/project-probes.d.ts.map +1 -0
- package/dist/health/project-probes.js +104 -0
- package/dist/health/project-probes.js.map +1 -0
- package/dist/health/read-only-fs.d.ts +54 -0
- package/dist/health/read-only-fs.d.ts.map +1 -0
- package/dist/health/read-only-fs.js +165 -0
- package/dist/health/read-only-fs.js.map +1 -0
- package/dist/health/ruleset-inspection.d.ts +57 -0
- package/dist/health/ruleset-inspection.d.ts.map +1 -0
- package/dist/health/ruleset-inspection.js +315 -0
- package/dist/health/ruleset-inspection.js.map +1 -0
- package/dist/health/template-inspection.d.ts +67 -0
- package/dist/health/template-inspection.d.ts.map +1 -0
- package/dist/health/template-inspection.js +469 -0
- package/dist/health/template-inspection.js.map +1 -0
- package/dist/strategies/copy-contents.d.ts +7 -25
- package/dist/strategies/copy-contents.d.ts.map +1 -1
- package/dist/strategies/copy-contents.js +32 -63
- package/dist/strategies/copy-contents.js.map +1 -1
- package/dist/strategies/merge.d.ts +7 -0
- package/dist/strategies/merge.d.ts.map +1 -1
- package/dist/strategies/merge.js +10 -1
- package/dist/strategies/merge.js.map +1 -1
- package/dist/strategies/package-lisa.d.ts +10 -1
- package/dist/strategies/package-lisa.d.ts.map +1 -1
- package/dist/strategies/package-lisa.js +18 -20
- package/dist/strategies/package-lisa.js.map +1 -1
- package/dist/strategies/tagged-merge.d.ts +7 -0
- package/dist/strategies/tagged-merge.d.ts.map +1 -1
- package/dist/strategies/tagged-merge.js +10 -1
- package/dist/strategies/tagged-merge.js.map +1 -1
- package/dist/sync/config-sync.d.ts +7 -0
- package/dist/sync/config-sync.d.ts.map +1 -1
- package/dist/sync/config-sync.js +23 -16
- package/dist/sync/config-sync.js.map +1 -1
- package/dist/utils/ignore-patterns.d.ts +18 -0
- package/dist/utils/ignore-patterns.d.ts.map +1 -1
- package/dist/utils/ignore-patterns.js +2 -2
- package/dist/utils/ignore-patterns.js.map +1 -1
- package/package.json +3 -2
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +22 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-spec-conformance/SKILL.md +43 -11
- package/plugins/lisa/agents/spec-conformance-specialist.md +5 -2
- package/plugins/lisa/agents/verification-specialist.md +1 -1
- package/plugins/lisa/hooks/enforce-verification-gate.sh +83 -9
- package/plugins/lisa/rules/reference/verification.md +2 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +22 -1
- package/plugins/lisa/skills/lisa-spec-conformance/SKILL.md +43 -11
- package/plugins/lisa-agy/agents/spec-conformance-specialist.md +5 -2
- package/plugins/lisa-agy/agents/verification-specialist.md +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +22 -1
- package/plugins/lisa-agy/skills/lisa-spec-conformance/SKILL.md +43 -11
- 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-copilot/agents/spec-conformance-specialist.agent.md +5 -2
- package/plugins/lisa-copilot/agents/verification-specialist.agent.md +1 -1
- package/plugins/lisa-copilot/hooks/enforce-verification-gate.sh +83 -9
- package/plugins/lisa-copilot/rules/reference/verification.md +2 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +22 -1
- package/plugins/lisa-copilot/skills/lisa-spec-conformance/SKILL.md +43 -11
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/spec-conformance-specialist.md +5 -2
- package/plugins/lisa-cursor/agents/verification-specialist.md +1 -1
- package/plugins/lisa-cursor/hooks/enforce-verification-gate.sh +83 -9
- package/plugins/lisa-cursor/rules/verification-reference.mdc +2 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +22 -1
- package/plugins/lisa-cursor/skills/lisa-spec-conformance/SKILL.md +43 -11
- 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/plugins/src/base/agents/spec-conformance-specialist.md +5 -2
- package/plugins/src/base/agents/verification-specialist.md +1 -1
- package/plugins/src/base/hooks/enforce-verification-gate.sh +83 -9
- package/plugins/src/base/rules/reference/verification.md +2 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +22 -1
- package/plugins/src/base/skills/lisa-spec-conformance/SKILL.md +43 -11
- package/scripts/detect-stale-workflow-inputs.mjs +21 -148
- package/scripts/lib/reusable-workflow-contract.d.mts +24 -0
- package/scripts/lib/reusable-workflow-contract.mjs +123 -0
- package/scripts/verify-health-deterministic-built.mjs +499 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
/** Safe read-only template ownership and conformance inspection. */
|
|
2
|
+
/* eslint-disable jsdoc/require-param-description, jsdoc/require-returns, max-lines -- ownership planning and exact comparators stay colocated */
|
|
3
|
+
import { lstat, readFile } from "node:fs/promises";
|
|
4
|
+
import * as fse from "fs-extra";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { PROJECT_TYPE_HIERARCHY, PROJECT_TYPE_ORDER, } from "../core/config.js";
|
|
7
|
+
import { decideTemplateOwnership, pendingDeletionPaths, } from "../core/template-ownership.js";
|
|
8
|
+
import { DEFAULT_PROJECT_LEARNINGS_FILE, resolveLegacyProjectLearningsFile, resolveProjectLearningsFile, } from "../core/project-config.js";
|
|
9
|
+
import { mergeCopyContents } from "../strategies/copy-contents.js";
|
|
10
|
+
import { mergeTemplateJson } from "../strategies/merge.js";
|
|
11
|
+
import { PackageLisaStrategy } from "../strategies/package-lisa.js";
|
|
12
|
+
import { TaggedMergeStrategy } from "../strategies/tagged-merge.js";
|
|
13
|
+
import { listFilesRecursive } from "../utils/file-operations.js";
|
|
14
|
+
import { matchesAnyPattern, parseIgnorePatterns, } from "../utils/ignore-patterns.js";
|
|
15
|
+
import { diffStaleKeys, extractCallerJobs, extractDeclaredInputs, } from "../../scripts/lib/reusable-workflow-contract.mjs";
|
|
16
|
+
import { projectPathKind, readProjectFile, readProjectJsonObject, readProjectText, } from "./read-only-fs.js";
|
|
17
|
+
const COPY_OVERWRITE = "copy-overwrite";
|
|
18
|
+
const COPY_CONTENTS = "copy-contents";
|
|
19
|
+
const CREATE_ONLY = "create-only";
|
|
20
|
+
const MANAGED = "managed";
|
|
21
|
+
const HOOKS = "hooks";
|
|
22
|
+
const WORKFLOWS = "workflows";
|
|
23
|
+
const HARPER_APP = "harper-app";
|
|
24
|
+
const LISA_WORKFLOW_OWNER = "codyswanngt";
|
|
25
|
+
const LISA_WORKFLOW_REPO = "lisa";
|
|
26
|
+
const STRATEGIES = [
|
|
27
|
+
COPY_OVERWRITE,
|
|
28
|
+
COPY_CONTENTS,
|
|
29
|
+
CREATE_ONLY,
|
|
30
|
+
"merge",
|
|
31
|
+
"tagged-merge",
|
|
32
|
+
];
|
|
33
|
+
const MAX_TEMPLATE_BYTES = 1024 * 1024;
|
|
34
|
+
const PROJECT_LEARNINGS_SOURCE = path.join(".lisa", "PROJECT_LEARNINGS.md");
|
|
35
|
+
/**
|
|
36
|
+
* Resolve whether a caller contract is shipped by Lisa, local, or external.
|
|
37
|
+
* @param owner
|
|
38
|
+
* @param repo
|
|
39
|
+
* @param config
|
|
40
|
+
*/
|
|
41
|
+
function workflowContractOwner(owner, repo, config) {
|
|
42
|
+
if (owner === null && repo === null)
|
|
43
|
+
return "project";
|
|
44
|
+
const normalizedOwner = owner?.toLowerCase();
|
|
45
|
+
const normalizedRepo = repo?.toLowerCase();
|
|
46
|
+
if (normalizedOwner === LISA_WORKFLOW_OWNER &&
|
|
47
|
+
normalizedRepo === LISA_WORKFLOW_REPO) {
|
|
48
|
+
return "lisa";
|
|
49
|
+
}
|
|
50
|
+
const github = config.github;
|
|
51
|
+
if (github === null || typeof github !== "object" || Array.isArray(github)) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const configuredOwner = Reflect.get(github, "org");
|
|
55
|
+
const configuredRepo = Reflect.get(github, "repo");
|
|
56
|
+
return typeof configuredOwner === "string" &&
|
|
57
|
+
typeof configuredRepo === "string" &&
|
|
58
|
+
normalizedOwner === configuredOwner.toLowerCase() &&
|
|
59
|
+
normalizedRepo === configuredRepo.toLowerCase()
|
|
60
|
+
? "project"
|
|
61
|
+
: undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Read a safe file signal, rejecting unsafe path types.
|
|
65
|
+
* @param root
|
|
66
|
+
* @param relativePath
|
|
67
|
+
*/
|
|
68
|
+
async function fileSignal(root, relativePath) {
|
|
69
|
+
const kind = await projectPathKind(root, relativePath);
|
|
70
|
+
if (kind === "directory")
|
|
71
|
+
throw new Error("Project signal is not a file");
|
|
72
|
+
return kind === "file";
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Return dependency maps from a safe package document.
|
|
76
|
+
* @param packageJson
|
|
77
|
+
*/
|
|
78
|
+
function dependencyNames(packageJson) {
|
|
79
|
+
if (packageJson === undefined)
|
|
80
|
+
return [];
|
|
81
|
+
return ["dependencies", "devDependencies"].flatMap(field => {
|
|
82
|
+
const value = packageJson[field];
|
|
83
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
84
|
+
? Object.keys(value)
|
|
85
|
+
: [];
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Expand detected types using Lisa's canonical hierarchy and ordering.
|
|
90
|
+
* @param types
|
|
91
|
+
*/
|
|
92
|
+
function expandTypes(types) {
|
|
93
|
+
const expanded = new Set([
|
|
94
|
+
...types,
|
|
95
|
+
...[...types].flatMap(type => {
|
|
96
|
+
const parent = PROJECT_TYPE_HIERARCHY[type];
|
|
97
|
+
return parent === undefined ? [] : [parent];
|
|
98
|
+
}),
|
|
99
|
+
]);
|
|
100
|
+
return PROJECT_TYPE_ORDER.filter(type => expanded.has(type));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Detect project types using only confined, bounded project reads.
|
|
104
|
+
* @param projectRoot - Canonical host project root
|
|
105
|
+
* @returns Safe project shape
|
|
106
|
+
*/
|
|
107
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity -- explicit signals keep stack detection auditable
|
|
108
|
+
export async function detectHealthProjectShape(projectRoot) {
|
|
109
|
+
const packageJson = await readProjectJsonObject(projectRoot, "package.json");
|
|
110
|
+
const dependencies = dependencyNames(packageJson);
|
|
111
|
+
const hasDependency = (name) => dependencies.includes(name);
|
|
112
|
+
const hasPrefix = (prefix) => dependencies.some(name => name.startsWith(prefix));
|
|
113
|
+
const [tsconfig, app, eas, nest, cdk, railsBin, railsApp, harperConfig, harperSchema,] = await Promise.all([
|
|
114
|
+
fileSignal(projectRoot, "tsconfig.json"),
|
|
115
|
+
fileSignal(projectRoot, "app.json"),
|
|
116
|
+
fileSignal(projectRoot, "eas.json"),
|
|
117
|
+
fileSignal(projectRoot, "nest-cli.json"),
|
|
118
|
+
fileSignal(projectRoot, "cdk.json"),
|
|
119
|
+
fileSignal(projectRoot, path.join("bin", "rails")),
|
|
120
|
+
fileSignal(projectRoot, path.join("config", "application.rb")),
|
|
121
|
+
fileSignal(projectRoot, path.join(HARPER_APP, "config.yaml")),
|
|
122
|
+
fileSignal(projectRoot, path.join(HARPER_APP, "schema.graphql")),
|
|
123
|
+
]);
|
|
124
|
+
const harperText = harperConfig
|
|
125
|
+
? await readProjectText(projectRoot, path.join(HARPER_APP, "config.yaml"))
|
|
126
|
+
: undefined;
|
|
127
|
+
const harperSignals = harperText?.includes("graphqlSchema:") === true &&
|
|
128
|
+
harperText.includes("jsResource:") &&
|
|
129
|
+
harperText.includes("static:");
|
|
130
|
+
const harper = harperConfig &&
|
|
131
|
+
harperSchema &&
|
|
132
|
+
(harperSignals || hasDependency("harperdb"));
|
|
133
|
+
const publishable = packageJson !== undefined &&
|
|
134
|
+
packageJson.private !== true &&
|
|
135
|
+
["main", "bin", "exports", "files"].some(field => Object.hasOwn(packageJson, field));
|
|
136
|
+
const detected = new Set([
|
|
137
|
+
...(tsconfig || hasDependency("typescript") ? ["typescript"] : []),
|
|
138
|
+
...(app || eas || hasDependency("expo") ? ["expo"] : []),
|
|
139
|
+
...(nest || hasPrefix("@nestjs") ? ["nestjs"] : []),
|
|
140
|
+
...(cdk || hasPrefix("aws-cdk") ? ["cdk"] : []),
|
|
141
|
+
...(harper ? ["harper-fabric"] : []),
|
|
142
|
+
...(hasDependency("phaser") ? ["phaser"] : []),
|
|
143
|
+
...(publishable ? ["npm-package"] : []),
|
|
144
|
+
...(railsBin || railsApp ? ["rails"] : []),
|
|
145
|
+
]);
|
|
146
|
+
return { types: expandTypes(detected), packageJson };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Backward-compatible type-only detector surface.
|
|
150
|
+
* @param projectRoot
|
|
151
|
+
*/
|
|
152
|
+
export async function detectHealthProjectTypes(projectRoot) {
|
|
153
|
+
return (await detectHealthProjectShape(projectRoot)).types;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Normalize a template destination exactly as apply does.
|
|
157
|
+
* @param relativePath
|
|
158
|
+
* @param strategy
|
|
159
|
+
* @param type
|
|
160
|
+
* @param learningsFile
|
|
161
|
+
*/
|
|
162
|
+
function destinationFor(relativePath, strategy, type, learningsFile) {
|
|
163
|
+
if (type === "all" &&
|
|
164
|
+
strategy === CREATE_ONLY &&
|
|
165
|
+
relativePath === PROJECT_LEARNINGS_SOURCE) {
|
|
166
|
+
return learningsFile;
|
|
167
|
+
}
|
|
168
|
+
if (strategy === COPY_CONTENTS &&
|
|
169
|
+
path.basename(relativePath) === "gitignore") {
|
|
170
|
+
return path.join(path.dirname(relativePath), ".gitignore");
|
|
171
|
+
}
|
|
172
|
+
return relativePath;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Collect all trusted template candidates.
|
|
176
|
+
* @param lisaRoot
|
|
177
|
+
* @param types
|
|
178
|
+
* @param learningsFile
|
|
179
|
+
*/
|
|
180
|
+
async function collectCandidates(lisaRoot, types, learningsFile) {
|
|
181
|
+
const groups = await Promise.all(["all", ...types].flatMap(type => STRATEGIES.map(async (strategy) => {
|
|
182
|
+
const directory = path.join(lisaRoot, type, strategy);
|
|
183
|
+
if (!(await fse.pathExists(directory)))
|
|
184
|
+
return [];
|
|
185
|
+
return (await listFilesRecursive(directory)).map(source => {
|
|
186
|
+
const relativePath = path.relative(directory, source);
|
|
187
|
+
return {
|
|
188
|
+
type,
|
|
189
|
+
strategy,
|
|
190
|
+
source,
|
|
191
|
+
destination: destinationFor(relativePath, strategy, type, learningsFile),
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
})));
|
|
195
|
+
return groups.flat();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Resolve most-specific path ownership for one strategy.
|
|
199
|
+
* @param candidates
|
|
200
|
+
* @param strategy
|
|
201
|
+
*/
|
|
202
|
+
function owners(candidates, strategy) {
|
|
203
|
+
return new Map(candidates
|
|
204
|
+
.filter(candidate => candidate.strategy === strategy)
|
|
205
|
+
.map(candidate => [candidate.destination, candidate.type]));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Read pending deletion paths from trusted Lisa manifests.
|
|
209
|
+
* @param lisaRoot
|
|
210
|
+
* @param types
|
|
211
|
+
*/
|
|
212
|
+
async function pendingDeletions(lisaRoot, types) {
|
|
213
|
+
const all = await Promise.all(["all", ...types].map(async (type) => {
|
|
214
|
+
try {
|
|
215
|
+
const parsed = JSON.parse(await readFile(path.join(lisaRoot, type, "deletions.json"), "utf8"));
|
|
216
|
+
return pendingDeletionPaths(parsed).map(item => path.normalize(item));
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}));
|
|
222
|
+
return new Set(all.flat());
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Build the exact applicable template plan without touching the host.
|
|
226
|
+
* @param lisaRoot
|
|
227
|
+
* @param projectRoot
|
|
228
|
+
* @param types
|
|
229
|
+
* @param config
|
|
230
|
+
*/
|
|
231
|
+
async function applicableCandidates(lisaRoot, projectRoot, types, config) {
|
|
232
|
+
const projectConfig = config;
|
|
233
|
+
const learningsFile = path.normalize(resolveProjectLearningsFile(projectConfig));
|
|
234
|
+
const candidates = await collectCandidates(lisaRoot, types, learningsFile);
|
|
235
|
+
const createOwners = owners(candidates, CREATE_ONLY);
|
|
236
|
+
const overwriteOwners = owners(candidates, COPY_OVERWRITE);
|
|
237
|
+
const deletions = await pendingDeletions(lisaRoot, types);
|
|
238
|
+
const ignoreText = await readProjectText(projectRoot, ".lisaignore");
|
|
239
|
+
const ignorePatterns = parseIgnorePatterns(ignoreText ?? "");
|
|
240
|
+
const canonicalLearning = candidates.some(candidate => candidate.type === "all" &&
|
|
241
|
+
candidate.strategy === CREATE_ONLY &&
|
|
242
|
+
candidate.destination === learningsFile);
|
|
243
|
+
const legacyFile = path.normalize(resolveLegacyProjectLearningsFile(projectConfig));
|
|
244
|
+
const suppressLearning = canonicalLearning &&
|
|
245
|
+
legacyFile !== learningsFile &&
|
|
246
|
+
(await projectPathKind(projectRoot, legacyFile)) === "file" &&
|
|
247
|
+
(await projectPathKind(projectRoot, learningsFile)) === "missing";
|
|
248
|
+
const orderedTypes = ["all", ...types];
|
|
249
|
+
return candidates.filter(candidate => decideTemplateOwnership({
|
|
250
|
+
relativePath: candidate.destination,
|
|
251
|
+
strategy: candidate.strategy,
|
|
252
|
+
currentType: candidate.type,
|
|
253
|
+
orderedTypes,
|
|
254
|
+
ignored: matchesAnyPattern(candidate.destination, ignorePatterns),
|
|
255
|
+
pendingDeletion: deletions.has(path.normalize(candidate.destination)),
|
|
256
|
+
projectLearningsPath: canonicalLearning ? learningsFile : undefined,
|
|
257
|
+
suppressLearningsSeed: suppressLearning,
|
|
258
|
+
createOnlyOwner: createOwners.get(candidate.destination),
|
|
259
|
+
copyOverwriteOwner: overwriteOwners.get(candidate.destination),
|
|
260
|
+
}).process);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Classify a template destination into its health finding.
|
|
264
|
+
* @param relativePath
|
|
265
|
+
*/
|
|
266
|
+
function categoryFor(relativePath) {
|
|
267
|
+
const normalized = relativePath.split(path.sep).join("/");
|
|
268
|
+
if (normalized === "lefthook.yml" || normalized.startsWith(".husky/")) {
|
|
269
|
+
return HOOKS;
|
|
270
|
+
}
|
|
271
|
+
if (normalized.startsWith(".github/workflows/"))
|
|
272
|
+
return WORKFLOWS;
|
|
273
|
+
return MANAGED;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Decode trusted or safely captured bytes as strict UTF-8.
|
|
277
|
+
* @param bytes
|
|
278
|
+
*/
|
|
279
|
+
function decode(bytes) {
|
|
280
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Canonicalize JSON object key order while preserving array order.
|
|
284
|
+
* @param value
|
|
285
|
+
*/
|
|
286
|
+
function canonicalJson(value) {
|
|
287
|
+
if (Array.isArray(value))
|
|
288
|
+
return value.map(canonicalJson);
|
|
289
|
+
if (value !== null && typeof value === "object") {
|
|
290
|
+
return Object.fromEntries(Object.entries(value)
|
|
291
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
292
|
+
.map(([key, item]) => [key, canonicalJson(item)]));
|
|
293
|
+
}
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Apply one candidate to an in-memory destination in factory order.
|
|
298
|
+
* @param current
|
|
299
|
+
* @param candidate
|
|
300
|
+
*/
|
|
301
|
+
async function composeCandidate(current, candidate) {
|
|
302
|
+
const sourceStat = await lstat(candidate.source);
|
|
303
|
+
if (!sourceStat.isFile() || sourceStat.size > MAX_TEMPLATE_BYTES) {
|
|
304
|
+
throw new Error("Unsafe health template source");
|
|
305
|
+
}
|
|
306
|
+
const source = await readFile(candidate.source);
|
|
307
|
+
const executable = current.requiresExecutable || (sourceStat.mode & 0o100) !== 0;
|
|
308
|
+
if (candidate.strategy === COPY_OVERWRITE) {
|
|
309
|
+
return {
|
|
310
|
+
bytes: source,
|
|
311
|
+
comparison: "bytes",
|
|
312
|
+
requiresExecutable: executable,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
if (candidate.strategy === COPY_CONTENTS) {
|
|
316
|
+
return {
|
|
317
|
+
bytes: Buffer.from(mergeCopyContents(decode(source), decode(current.bytes))),
|
|
318
|
+
comparison: "bytes",
|
|
319
|
+
requiresExecutable: executable,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
const sourceJson = JSON.parse(decode(source));
|
|
323
|
+
const targetJson = JSON.parse(decode(current.bytes));
|
|
324
|
+
const merged = candidate.strategy === "merge"
|
|
325
|
+
? mergeTemplateJson(sourceJson, targetJson)
|
|
326
|
+
: new TaggedMergeStrategy().mergeJson(sourceJson, targetJson);
|
|
327
|
+
return {
|
|
328
|
+
bytes: Buffer.from(JSON.stringify(merged, null, 2)),
|
|
329
|
+
comparison: "json",
|
|
330
|
+
requiresExecutable: executable,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Compare one final composed destination against its captured host state.
|
|
335
|
+
* @param projectRoot
|
|
336
|
+
* @param candidates
|
|
337
|
+
*/
|
|
338
|
+
async function destinationConforms(projectRoot, candidates) {
|
|
339
|
+
const first = candidates[0];
|
|
340
|
+
if (first === undefined)
|
|
341
|
+
return true;
|
|
342
|
+
const target = await readProjectFile(projectRoot, first.destination);
|
|
343
|
+
if (target === undefined)
|
|
344
|
+
return false;
|
|
345
|
+
const initial = {
|
|
346
|
+
bytes: target.bytes,
|
|
347
|
+
comparison: "bytes",
|
|
348
|
+
requiresExecutable: false,
|
|
349
|
+
};
|
|
350
|
+
const planned = await candidates.reduce(async (state, candidate) => composeCandidate(await state, candidate), Promise.resolve(initial));
|
|
351
|
+
const normalizedDestination = first.destination.split(path.sep).join("/");
|
|
352
|
+
if (normalizedDestination.startsWith(".husky/") &&
|
|
353
|
+
planned.requiresExecutable &&
|
|
354
|
+
(target.mode & 0o100) === 0) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
if (planned.comparison === "bytes")
|
|
358
|
+
return planned.bytes.equals(target.bytes);
|
|
359
|
+
return (JSON.stringify(canonicalJson(JSON.parse(decode(planned.bytes)))) ===
|
|
360
|
+
JSON.stringify(canonicalJson(JSON.parse(decode(target.bytes)))));
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Inspect one category using Lisa's shared ownership and pure comparators.
|
|
364
|
+
* @param lisaRoot - Lisa package root
|
|
365
|
+
* @param projectRoot - Canonical host root
|
|
366
|
+
* @param types - Canonically ordered types
|
|
367
|
+
* @param category - Health category
|
|
368
|
+
* @param config - Safe project config
|
|
369
|
+
* @returns Sorted project-relative drift paths
|
|
370
|
+
*/
|
|
371
|
+
export async function inspectManagedTemplates(lisaRoot, projectRoot, types, category, config = {}) {
|
|
372
|
+
const candidates = (await applicableCandidates(lisaRoot, projectRoot, types, config)).filter(candidate => candidate.strategy !== CREATE_ONLY &&
|
|
373
|
+
categoryFor(candidate.destination) === category);
|
|
374
|
+
const destinations = [...new Set(candidates.map(item => item.destination))];
|
|
375
|
+
const checks = await Promise.all(destinations.map(async (destination) => ({
|
|
376
|
+
destination,
|
|
377
|
+
conforms: await destinationConforms(projectRoot, candidates.filter(candidate => candidate.destination === destination)).catch(() => false),
|
|
378
|
+
})));
|
|
379
|
+
return [
|
|
380
|
+
...new Set(checks
|
|
381
|
+
.filter(check => !check.conforms)
|
|
382
|
+
.map(check => check.destination.split(path.sep).join("/"))),
|
|
383
|
+
].sort((left, right) => left.localeCompare(right));
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Require applicable create-only workflow presence while preserving bytes.
|
|
387
|
+
* @param lisaRoot - Lisa package root
|
|
388
|
+
* @param projectRoot - Canonical host root
|
|
389
|
+
* @param types - Canonically ordered types
|
|
390
|
+
* @param config - Safe project config
|
|
391
|
+
* @returns Missing workflow paths
|
|
392
|
+
*/
|
|
393
|
+
export async function inspectCreateOnlyWorkflows(lisaRoot, projectRoot, types, config = {}) {
|
|
394
|
+
const candidates = (await applicableCandidates(lisaRoot, projectRoot, types, config)).filter(candidate => candidate.strategy === CREATE_ONLY &&
|
|
395
|
+
categoryFor(candidate.destination) === WORKFLOWS);
|
|
396
|
+
const missing = await Promise.all(candidates.map(async (candidate) => (await projectPathKind(projectRoot, candidate.destination)) === "file"
|
|
397
|
+
? undefined
|
|
398
|
+
: candidate.destination.split(path.sep).join("/")));
|
|
399
|
+
return missing
|
|
400
|
+
.filter((item) => item !== undefined)
|
|
401
|
+
.sort((left, right) => left.localeCompare(right));
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Inspect applicable create-only workflow caller inputs against shipped contracts.
|
|
405
|
+
* @param lisaRoot - Lisa package root
|
|
406
|
+
* @param projectRoot - Canonical host root
|
|
407
|
+
* @param types - Canonically ordered types
|
|
408
|
+
* @param config - Safe project config
|
|
409
|
+
* @returns Stale and unknown caller descriptions
|
|
410
|
+
*/
|
|
411
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity -- parser outcomes remain explicit and bounded
|
|
412
|
+
export async function inspectWorkflowInputs(lisaRoot, projectRoot, types, config = {}) {
|
|
413
|
+
const candidates = (await applicableCandidates(lisaRoot, projectRoot, types, config)).filter(candidate => categoryFor(candidate.destination) === WORKFLOWS);
|
|
414
|
+
const stale = [];
|
|
415
|
+
const unknown = [];
|
|
416
|
+
for (const candidate of candidates) {
|
|
417
|
+
const caller = await readProjectText(projectRoot, candidate.destination);
|
|
418
|
+
if (caller === undefined)
|
|
419
|
+
continue;
|
|
420
|
+
for (const job of extractCallerJobs(caller)) {
|
|
421
|
+
const label = `${candidate.destination.split(path.sep).join("/")}#${job.reusableFile}`;
|
|
422
|
+
const contractOwner = workflowContractOwner(job.owner, job.repo, config);
|
|
423
|
+
if (contractOwner === undefined) {
|
|
424
|
+
// eslint-disable-next-line functional/immutable-data -- bounded local result collector
|
|
425
|
+
unknown.push(label);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const declared = contractOwner === "lisa"
|
|
429
|
+
? await readFile(path.join(lisaRoot, ".github", WORKFLOWS, path.basename(job.reusableFile)), "utf8")
|
|
430
|
+
.then(content => extractDeclaredInputs(content))
|
|
431
|
+
.catch(() => null)
|
|
432
|
+
: await readProjectText(projectRoot, path.join(".github", WORKFLOWS, job.reusableFile))
|
|
433
|
+
.then(content => content === undefined ? null : extractDeclaredInputs(content))
|
|
434
|
+
.catch(() => null);
|
|
435
|
+
if (declared === null) {
|
|
436
|
+
// eslint-disable-next-line functional/immutable-data -- bounded local result collector
|
|
437
|
+
unknown.push(label);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
for (const key of diffStaleKeys(job.withKeys, declared)) {
|
|
441
|
+
// eslint-disable-next-line functional/immutable-data -- bounded local result collector
|
|
442
|
+
stale.push(`${label}:${key}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
stale: [...new Set(stale)].sort((left, right) => left.localeCompare(right)),
|
|
448
|
+
unknown: [...new Set(unknown)].sort((left, right) => left.localeCompare(right)),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Inspect package.json through the exact pure package-lisa planner.
|
|
453
|
+
* @param lisaRoot - Lisa package root
|
|
454
|
+
* @param projectRoot - Canonical host root
|
|
455
|
+
* @param types - Safely detected project types
|
|
456
|
+
* @param packageJson - Safely captured package document
|
|
457
|
+
* @returns Whether package.json conforms
|
|
458
|
+
*/
|
|
459
|
+
export async function packageJsonConforms(lisaRoot, projectRoot, types, packageJson) {
|
|
460
|
+
const safePackage = packageJson ?? (await readProjectJsonObject(projectRoot, "package.json"));
|
|
461
|
+
if (safePackage === undefined)
|
|
462
|
+
return false;
|
|
463
|
+
const safeTypes = types ?? (await detectHealthProjectTypes(projectRoot));
|
|
464
|
+
const planned = await new PackageLisaStrategy().planPackageJson({ ...safePackage }, safeTypes, lisaRoot);
|
|
465
|
+
return (JSON.stringify(planned, null, 2) === JSON.stringify(safePackage, null, 2));
|
|
466
|
+
}
|
|
467
|
+
export { DEFAULT_PROJECT_LEARNINGS_FILE };
|
|
468
|
+
/* eslint-enable jsdoc/require-param-description, jsdoc/require-returns, max-lines -- restore repository defaults */
|
|
469
|
+
//# sourceMappingURL=template-inspection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-inspection.js","sourceRoot":"","sources":["../../src/health/template-inspection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { FileOperationResult } from "../core/config.js";
|
|
2
2
|
import type { ICopyStrategy, StrategyContext } from "./strategy.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* Produce the exact copy-contents result without reading or writing files.
|
|
5
|
+
* @param sourceContent - Lisa-managed source block
|
|
6
|
+
* @param destinationContent - Existing host content
|
|
7
|
+
* @returns Content that apply would persist
|
|
8
|
+
*/
|
|
9
|
+
export declare function mergeCopyContents(sourceContent: string, destinationContent: string): string;
|
|
3
10
|
/**
|
|
4
11
|
* Copy-contents strategy: Block-based merge for .gitignore-style files
|
|
5
12
|
* - Create new files silently
|
|
@@ -9,31 +16,6 @@ import type { ICopyStrategy, StrategyContext } from "./strategy.interface.js";
|
|
|
9
16
|
*/
|
|
10
17
|
export declare class CopyContentsStrategy implements ICopyStrategy {
|
|
11
18
|
readonly name: "copy-contents";
|
|
12
|
-
private readonly BEGIN_MARKER_PREFIX;
|
|
13
|
-
private readonly END_MARKER_PREFIX;
|
|
14
|
-
/**
|
|
15
|
-
* Find the guardrails block in content
|
|
16
|
-
* @param content File content to search
|
|
17
|
-
* @param beginMarker Opening marker for this managed block
|
|
18
|
-
* @param endMarker Closing marker for this managed block
|
|
19
|
-
* @returns Object with start/end indices or null if not found
|
|
20
|
-
*/
|
|
21
|
-
private findGuardrailsBlock;
|
|
22
|
-
/**
|
|
23
|
-
* Read the guardrail marker pair from the source block. This supports
|
|
24
|
-
* stack-specific blocks such as `# BEGIN: AI GUARDRAILS HARPER-FABRIC`
|
|
25
|
-
* without replacing the universal guardrails block.
|
|
26
|
-
* @param sourceContent Source file content
|
|
27
|
-
* @returns Marker pair to use while merging
|
|
28
|
-
*/
|
|
29
|
-
private getSourceMarkers;
|
|
30
|
-
/**
|
|
31
|
-
* Merge source content with destination, replacing or appending guardrails block
|
|
32
|
-
* @param sourceContent Source file content
|
|
33
|
-
* @param destContent Destination file content
|
|
34
|
-
* @returns Merged content
|
|
35
|
-
*/
|
|
36
|
-
private mergeContent;
|
|
37
19
|
/**
|
|
38
20
|
* Handle the case where destination doesn't exist
|
|
39
21
|
* @param sourcePath Source file path
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-contents.d.ts","sourceRoot":"","sources":["../../src/strategies/copy-contents.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"copy-contents.d.ts","sourceRoot":"","sources":["../../src/strategies/copy-contents.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM9E;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,GACzB,MAAM,CAuBR;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACxD,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;IAEzC;;;;;;;OAOG;YACW,aAAa;IAc3B;;;;;;;OAOG;YACW,WAAW;IAkBzB;;;;;;;;;;;;OAYG;YACW,eAAe;IAkB7B;;;;;;;OAOG;IACG,KAAK,CACT,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CA0EhC"}
|
|
@@ -2,6 +2,36 @@ import * as fse from "fs-extra";
|
|
|
2
2
|
import { readFile, copyFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { filesIdentical, ensureParentDir } from "../utils/file-operations.js";
|
|
5
|
+
const BEGIN_MARKER_PREFIX = "# BEGIN: AI GUARDRAILS";
|
|
6
|
+
const END_MARKER_PREFIX = "# END: AI GUARDRAILS";
|
|
7
|
+
/**
|
|
8
|
+
* Produce the exact copy-contents result without reading or writing files.
|
|
9
|
+
* @param sourceContent - Lisa-managed source block
|
|
10
|
+
* @param destinationContent - Existing host content
|
|
11
|
+
* @returns Content that apply would persist
|
|
12
|
+
*/
|
|
13
|
+
export function mergeCopyContents(sourceContent, destinationContent) {
|
|
14
|
+
if (!sourceContent.includes(BEGIN_MARKER_PREFIX))
|
|
15
|
+
return sourceContent;
|
|
16
|
+
const lines = sourceContent.split(/\r?\n/);
|
|
17
|
+
const begin = lines.find(line => line.startsWith(BEGIN_MARKER_PREFIX)) ??
|
|
18
|
+
BEGIN_MARKER_PREFIX;
|
|
19
|
+
const suffix = begin.slice(BEGIN_MARKER_PREFIX.length);
|
|
20
|
+
const matchingEnd = `${END_MARKER_PREFIX}${suffix}`;
|
|
21
|
+
const end = lines.includes(matchingEnd) ? matchingEnd : END_MARKER_PREFIX;
|
|
22
|
+
const startIndex = destinationContent.indexOf(begin);
|
|
23
|
+
const endIndex = destinationContent.indexOf(end, startIndex + begin.length);
|
|
24
|
+
if (startIndex !== -1 && endIndex !== -1) {
|
|
25
|
+
const trimmedSource = sourceContent.endsWith("\n")
|
|
26
|
+
? sourceContent.slice(0, -1)
|
|
27
|
+
: sourceContent;
|
|
28
|
+
return (destinationContent.slice(0, startIndex) +
|
|
29
|
+
trimmedSource +
|
|
30
|
+
destinationContent.slice(endIndex + end.length));
|
|
31
|
+
}
|
|
32
|
+
const prefix = destinationContent.endsWith("\n") ? "\n" : "\n\n";
|
|
33
|
+
return destinationContent + prefix + sourceContent;
|
|
34
|
+
}
|
|
5
35
|
/**
|
|
6
36
|
* Copy-contents strategy: Block-based merge for .gitignore-style files
|
|
7
37
|
* - Create new files silently
|
|
@@ -11,67 +41,6 @@ import { filesIdentical, ensureParentDir } from "../utils/file-operations.js";
|
|
|
11
41
|
*/
|
|
12
42
|
export class CopyContentsStrategy {
|
|
13
43
|
name = "copy-contents";
|
|
14
|
-
BEGIN_MARKER_PREFIX = "# BEGIN: AI GUARDRAILS";
|
|
15
|
-
END_MARKER_PREFIX = "# END: AI GUARDRAILS";
|
|
16
|
-
/**
|
|
17
|
-
* Find the guardrails block in content
|
|
18
|
-
* @param content File content to search
|
|
19
|
-
* @param beginMarker Opening marker for this managed block
|
|
20
|
-
* @param endMarker Closing marker for this managed block
|
|
21
|
-
* @returns Object with start/end indices or null if not found
|
|
22
|
-
*/
|
|
23
|
-
findGuardrailsBlock(content, beginMarker, endMarker) {
|
|
24
|
-
const startIndex = content.indexOf(beginMarker);
|
|
25
|
-
if (startIndex === -1) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
const endIndex = content.indexOf(endMarker, startIndex + beginMarker.length);
|
|
29
|
-
if (endIndex === -1) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
return { start: startIndex, end: endIndex + endMarker.length };
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Read the guardrail marker pair from the source block. This supports
|
|
36
|
-
* stack-specific blocks such as `# BEGIN: AI GUARDRAILS HARPER-FABRIC`
|
|
37
|
-
* without replacing the universal guardrails block.
|
|
38
|
-
* @param sourceContent Source file content
|
|
39
|
-
* @returns Marker pair to use while merging
|
|
40
|
-
*/
|
|
41
|
-
getSourceMarkers(sourceContent) {
|
|
42
|
-
const lines = sourceContent.split(/\r?\n/);
|
|
43
|
-
const begin = lines.find(line => line.startsWith(this.BEGIN_MARKER_PREFIX)) ??
|
|
44
|
-
this.BEGIN_MARKER_PREFIX;
|
|
45
|
-
const suffix = begin.slice(this.BEGIN_MARKER_PREFIX.length);
|
|
46
|
-
const matchingEnd = `${this.END_MARKER_PREFIX}${suffix}`;
|
|
47
|
-
const end = lines.includes(matchingEnd)
|
|
48
|
-
? matchingEnd
|
|
49
|
-
: this.END_MARKER_PREFIX;
|
|
50
|
-
return { begin, end };
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Merge source content with destination, replacing or appending guardrails block
|
|
54
|
-
* @param sourceContent Source file content
|
|
55
|
-
* @param destContent Destination file content
|
|
56
|
-
* @returns Merged content
|
|
57
|
-
*/
|
|
58
|
-
mergeContent(sourceContent, destContent) {
|
|
59
|
-
const markers = this.getSourceMarkers(sourceContent);
|
|
60
|
-
const block = this.findGuardrailsBlock(destContent, markers.begin, markers.end);
|
|
61
|
-
if (block) {
|
|
62
|
-
// Replace existing block, trimming source trailing newline
|
|
63
|
-
// to prevent doubling with destination's post-marker newline
|
|
64
|
-
const trimmedSource = sourceContent.endsWith("\n")
|
|
65
|
-
? sourceContent.slice(0, -1)
|
|
66
|
-
: sourceContent;
|
|
67
|
-
return (destContent.slice(0, block.start) +
|
|
68
|
-
trimmedSource +
|
|
69
|
-
destContent.slice(block.end));
|
|
70
|
-
}
|
|
71
|
-
// Append if no block exists
|
|
72
|
-
const prefix = destContent.endsWith("\n") ? "\n" : "\n\n";
|
|
73
|
-
return destContent + prefix + sourceContent;
|
|
74
|
-
}
|
|
75
44
|
/**
|
|
76
45
|
* Handle the case where destination doesn't exist
|
|
77
46
|
* @param sourcePath Source file path
|
|
@@ -178,10 +147,10 @@ export class CopyContentsStrategy {
|
|
|
178
147
|
// bug that shipped lisa-mutation.mjs twice). Such a file is fully
|
|
179
148
|
// Lisa-managed and belongs in copy-overwrite — treat it as an overwrite
|
|
180
149
|
// here so a miscategorized file can never silently self-duplicate.
|
|
181
|
-
if (!sourceContent.includes(
|
|
150
|
+
if (!sourceContent.includes(BEGIN_MARKER_PREFIX)) {
|
|
182
151
|
return this.handleOverwrite(realDestPath, realRelativePath, sourceContent, context);
|
|
183
152
|
}
|
|
184
|
-
const mergedContent =
|
|
153
|
+
const mergedContent = mergeCopyContents(sourceContent, destContent);
|
|
185
154
|
if (mergedContent === destContent) {
|
|
186
155
|
return {
|
|
187
156
|
relativePath: realRelativePath,
|