@cairn-tool/cairn 2.0.1 → 3.0.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/README.md +57 -1367
- package/dist/agent/conditionals.d.ts +43 -0
- package/dist/agent/conditionals.js +314 -0
- package/dist/agent/conditionals.js.map +1 -0
- package/dist/agent/install/config.d.ts +47 -0
- package/dist/agent/install/config.js +156 -0
- package/dist/agent/install/config.js.map +1 -0
- package/dist/agent/install/index.d.ts +95 -14
- package/dist/agent/install/index.js +460 -188
- package/dist/agent/install/index.js.map +1 -1
- package/dist/agent/parser.js +15 -41
- package/dist/agent/parser.js.map +1 -1
- package/dist/agent/render.d.ts +9 -1
- package/dist/agent/render.js +12 -9
- package/dist/agent/render.js.map +1 -1
- package/dist/agent/types.d.ts +4 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/agent/verify/compare.d.ts +58 -0
- package/dist/agent/verify/compare.js +159 -0
- package/dist/agent/verify/compare.js.map +1 -0
- package/dist/agent/verify/config.d.ts +54 -0
- package/dist/agent/verify/config.js +196 -0
- package/dist/agent/verify/config.js.map +1 -0
- package/dist/agent/verify/index.d.ts +87 -0
- package/dist/agent/verify/index.js +331 -0
- package/dist/agent/verify/index.js.map +1 -0
- package/dist/agent/verify/resolve.d.ts +9 -0
- package/dist/agent/verify/resolve.js +97 -0
- package/dist/agent/verify/resolve.js.map +1 -0
- package/dist/cli.js +18 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent-install.d.ts +15 -1
- package/dist/commands/agent-install.js +121 -24
- package/dist/commands/agent-install.js.map +1 -1
- package/dist/commands/agent-verify.d.ts +16 -0
- package/dist/commands/agent-verify.js +42 -0
- package/dist/commands/agent-verify.js.map +1 -0
- package/dist/commands/agent.js +23 -0
- package/dist/commands/agent.js.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/contract/registry.js +11 -2
- package/dist/contract/registry.js.map +1 -1
- package/dist/contract/schemas/agent.js +141 -0
- package/dist/contract/schemas/agent.js.map +1 -1
- package/package.json +10 -10
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentDiagnostic, AgentTarget } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Target-conditional regions, in one place.
|
|
4
|
+
*
|
|
5
|
+
* The grammar used to live in two regexes — one in the parser that validated
|
|
6
|
+
* markers, one in the renderer that stripped them — which could disagree about
|
|
7
|
+
* what a document meant. They are the same grammar here, so a form the
|
|
8
|
+
* validator accepts is a form the renderer understands.
|
|
9
|
+
*
|
|
10
|
+
* Two forms are read. The **legacy** form carries one literal target name and
|
|
11
|
+
* nothing else, and is unchanged:
|
|
12
|
+
*
|
|
13
|
+
* ```markdown
|
|
14
|
+
* <!-- target:cursor -->
|
|
15
|
+
* <!-- /target:cursor -->
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* The **conditional** form carries the expressiveness — a list is an OR, `not`
|
|
19
|
+
* negates the whole list, and a block may branch:
|
|
20
|
+
*
|
|
21
|
+
* ```markdown
|
|
22
|
+
* <!-- if target:claude-code -->
|
|
23
|
+
* <!-- elif target:codex, cursor -->
|
|
24
|
+
* <!-- else -->
|
|
25
|
+
* <!-- endif -->
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
/** Files the renderer runs conditional blocks through, and so must validate. */
|
|
29
|
+
export declare const CONDITIONAL_TEXT: RegExp;
|
|
30
|
+
/**
|
|
31
|
+
* Resolves every conditional region for one target.
|
|
32
|
+
*
|
|
33
|
+
* An unbalanced document is left alone rather than half-stripped: `AB121`
|
|
34
|
+
* already reports it at parse time, and mangling the body would turn one
|
|
35
|
+
* finding into a confusing diff.
|
|
36
|
+
*/
|
|
37
|
+
export declare function applyConditionals(content: string, target: AgentTarget, options?: {
|
|
38
|
+
markdown?: boolean;
|
|
39
|
+
}): string;
|
|
40
|
+
/** Reports `AB120`, `AB121`, and `AB123` for one file's markers. */
|
|
41
|
+
export declare function validateConditionals(content: string, file: string, diagnostics: AgentDiagnostic[], options?: {
|
|
42
|
+
markdown?: boolean;
|
|
43
|
+
}): void;
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { visit } from "unist-util-visit";
|
|
2
|
+
import { parseMarkdown } from "../markdown-ast.js";
|
|
3
|
+
import { TARGETS } from "./types.js";
|
|
4
|
+
import { diagnostic } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Target-conditional regions, in one place.
|
|
7
|
+
*
|
|
8
|
+
* The grammar used to live in two regexes — one in the parser that validated
|
|
9
|
+
* markers, one in the renderer that stripped them — which could disagree about
|
|
10
|
+
* what a document meant. They are the same grammar here, so a form the
|
|
11
|
+
* validator accepts is a form the renderer understands.
|
|
12
|
+
*
|
|
13
|
+
* Two forms are read. The **legacy** form carries one literal target name and
|
|
14
|
+
* nothing else, and is unchanged:
|
|
15
|
+
*
|
|
16
|
+
* ```markdown
|
|
17
|
+
* <!-- target:cursor -->
|
|
18
|
+
* <!-- /target:cursor -->
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* The **conditional** form carries the expressiveness — a list is an OR, `not`
|
|
22
|
+
* negates the whole list, and a block may branch:
|
|
23
|
+
*
|
|
24
|
+
* ```markdown
|
|
25
|
+
* <!-- if target:claude-code -->
|
|
26
|
+
* <!-- elif target:codex, cursor -->
|
|
27
|
+
* <!-- else -->
|
|
28
|
+
* <!-- endif -->
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
/** Files the renderer runs conditional blocks through, and so must validate. */
|
|
32
|
+
export const CONDITIONAL_TEXT = /\.(?:md|txt|json|ya?ml|toml|sh|js|mjs|cjs|ts|py)$/i;
|
|
33
|
+
/** Every HTML comment, with its offsets. The only thing either side scans for. */
|
|
34
|
+
const COMMENT = /<!--([\s\S]*?)-->/g;
|
|
35
|
+
const LEGACY = /^(\/)?(target|platform):(\S+)$/;
|
|
36
|
+
const OPEN = /^(if|elif)\s+(.+)$/i;
|
|
37
|
+
const PREDICATE = /^(not\s+)?(?:target|platform):(\S(?:[^\s]|\s(?=[^\s]))*)$/i;
|
|
38
|
+
/**
|
|
39
|
+
* Whether a comment is *meant* to be a conditional marker.
|
|
40
|
+
*
|
|
41
|
+
* A near-miss — `<!-- target: cursor -->` with a space after the colon, or
|
|
42
|
+
* `<!-- targets:cursor -->` — used to match neither regex and so did nothing at
|
|
43
|
+
* all, silently. Recognising the intent is what lets it be reported.
|
|
44
|
+
*
|
|
45
|
+
* `if`/`elif` only count when the rest mentions a predicate keyword, so an
|
|
46
|
+
* ordinary `<!-- if you change this, update X -->` stays prose.
|
|
47
|
+
*/
|
|
48
|
+
const CANDIDATE = /^\s*(?:\/?\s*(?:target|platform)s?\b|else\s*$|endif\b|(?:el(?:se)?if|if)\b(?=[\s\S]*(?:\bnot\b|(?:target|platform)s?\s*:)))/i;
|
|
49
|
+
function parsePredicate(text) {
|
|
50
|
+
const match = PREDICATE.exec(text.trim());
|
|
51
|
+
if (!match)
|
|
52
|
+
return null;
|
|
53
|
+
const targets = match[2]
|
|
54
|
+
.split(",")
|
|
55
|
+
.map((part) => part.trim())
|
|
56
|
+
.filter((part) => part.length > 0);
|
|
57
|
+
if (!targets.length)
|
|
58
|
+
return null;
|
|
59
|
+
return { negated: Boolean(match[1]), targets };
|
|
60
|
+
}
|
|
61
|
+
function classify(body) {
|
|
62
|
+
const text = body.trim();
|
|
63
|
+
const legacy = LEGACY.exec(text);
|
|
64
|
+
if (legacy)
|
|
65
|
+
return {
|
|
66
|
+
kind: legacy[1] ? "legacy-close" : "legacy-open",
|
|
67
|
+
syntax: legacy[2],
|
|
68
|
+
target: legacy[3],
|
|
69
|
+
};
|
|
70
|
+
const lower = text.toLowerCase();
|
|
71
|
+
if (lower === "else")
|
|
72
|
+
return { kind: "else" };
|
|
73
|
+
if (lower === "endif")
|
|
74
|
+
return { kind: "endif" };
|
|
75
|
+
const open = OPEN.exec(text);
|
|
76
|
+
if (open) {
|
|
77
|
+
const predicate = parsePredicate(open[2]);
|
|
78
|
+
if (predicate)
|
|
79
|
+
return { kind: open[1].toLowerCase() === "if" ? "if" : "elif", predicate };
|
|
80
|
+
// An `if` whose remainder mentions no predicate keyword is prose — the
|
|
81
|
+
// whole point of the candidate test is that `<!-- if you change this -->`
|
|
82
|
+
// must not become an error.
|
|
83
|
+
}
|
|
84
|
+
return CANDIDATE.test(body) ? "malformed" : null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Byte ranges that document the syntax rather than using it: fenced code blocks
|
|
88
|
+
* and inline code spans.
|
|
89
|
+
*
|
|
90
|
+
* Without this, a fenced *example* of a conditional is treated as a live block
|
|
91
|
+
* — which is exactly what happened to this project's own bundle-format
|
|
92
|
+
* reference, whose example rendered as an empty code fence. Inline spans count
|
|
93
|
+
* for the same reason: prose saying that `<!-- target: cursor -->` is wrong must
|
|
94
|
+
* not itself be reported as wrong.
|
|
95
|
+
*
|
|
96
|
+
* It is the guard `synchronizeToc` needs and for the same reason; snippet links
|
|
97
|
+
* avoid needing one by living in the fence info string, which is unreachable by
|
|
98
|
+
* construction.
|
|
99
|
+
*/
|
|
100
|
+
function protectedRanges(tree) {
|
|
101
|
+
const ranges = [];
|
|
102
|
+
visit(tree, (node) => {
|
|
103
|
+
if (node.type !== "code" && node.type !== "inlineCode")
|
|
104
|
+
return;
|
|
105
|
+
const start = node.position?.start.offset;
|
|
106
|
+
const end = node.position?.end.offset;
|
|
107
|
+
if (start !== undefined && end !== undefined)
|
|
108
|
+
ranges.push([start, end]);
|
|
109
|
+
});
|
|
110
|
+
return ranges;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The markers in `content`, in order.
|
|
114
|
+
*
|
|
115
|
+
* `markdown` turns on {@link protectedRanges}.
|
|
116
|
+
*/
|
|
117
|
+
function tokenize(content, markdown) {
|
|
118
|
+
const tokens = [];
|
|
119
|
+
const malformed = [];
|
|
120
|
+
const protect = markdown ? protectedRanges(parseMarkdown(content)) : [];
|
|
121
|
+
// One pass over the string, counting newlines as we go, rather than a
|
|
122
|
+
// `split` per match.
|
|
123
|
+
let cursor = 0;
|
|
124
|
+
let line = 1;
|
|
125
|
+
COMMENT.lastIndex = 0;
|
|
126
|
+
for (let match = COMMENT.exec(content); match; match = COMMENT.exec(content)) {
|
|
127
|
+
while (cursor < match.index) {
|
|
128
|
+
if (content.charCodeAt(cursor) === 10)
|
|
129
|
+
line += 1;
|
|
130
|
+
cursor += 1;
|
|
131
|
+
}
|
|
132
|
+
const marker = classify(match[1]);
|
|
133
|
+
if (marker === null)
|
|
134
|
+
continue;
|
|
135
|
+
if (protect.some(([from, to]) => match.index >= from && match.index < to))
|
|
136
|
+
continue;
|
|
137
|
+
if (marker === "malformed") {
|
|
138
|
+
malformed.push({ raw: match[0], line });
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
let end = match.index + match[0].length;
|
|
142
|
+
if (content[end] === "\r")
|
|
143
|
+
end += 1;
|
|
144
|
+
if (content[end] === "\n")
|
|
145
|
+
end += 1;
|
|
146
|
+
tokens.push({ marker, start: match.index, end, line, raw: match[0] });
|
|
147
|
+
}
|
|
148
|
+
return { tokens, malformed };
|
|
149
|
+
}
|
|
150
|
+
function matches(predicate, target) {
|
|
151
|
+
const listed = predicate.targets.includes(target);
|
|
152
|
+
return predicate.negated ? !listed : listed;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resolves every conditional region for one target.
|
|
156
|
+
*
|
|
157
|
+
* An unbalanced document is left alone rather than half-stripped: `AB121`
|
|
158
|
+
* already reports it at parse time, and mangling the body would turn one
|
|
159
|
+
* finding into a confusing diff.
|
|
160
|
+
*/
|
|
161
|
+
export function applyConditionals(content, target, options = {}) {
|
|
162
|
+
const { tokens } = tokenize(content, options.markdown ?? true);
|
|
163
|
+
if (!tokens.length)
|
|
164
|
+
return content;
|
|
165
|
+
const stack = [];
|
|
166
|
+
const emitting = () => stack.every((frame) => frame.active && frame.enclosing);
|
|
167
|
+
let out = "";
|
|
168
|
+
let cursor = 0;
|
|
169
|
+
for (const token of tokens) {
|
|
170
|
+
if (emitting())
|
|
171
|
+
out += content.slice(cursor, token.start);
|
|
172
|
+
cursor = token.end;
|
|
173
|
+
const marker = token.marker;
|
|
174
|
+
switch (marker.kind) {
|
|
175
|
+
case "legacy-open": {
|
|
176
|
+
const active = marker.target === target;
|
|
177
|
+
stack.push({
|
|
178
|
+
taken: active,
|
|
179
|
+
active,
|
|
180
|
+
enclosing: emitting(),
|
|
181
|
+
seenElse: false,
|
|
182
|
+
legacy: { syntax: marker.syntax, target: marker.target },
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case "if": {
|
|
187
|
+
const active = matches(marker.predicate, target);
|
|
188
|
+
stack.push({ taken: active, active, enclosing: emitting(), seenElse: false });
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case "elif": {
|
|
192
|
+
const frame = stack[stack.length - 1];
|
|
193
|
+
if (!frame)
|
|
194
|
+
return content;
|
|
195
|
+
frame.active = !frame.taken && matches(marker.predicate, target);
|
|
196
|
+
frame.taken = frame.taken || frame.active;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case "else": {
|
|
200
|
+
const frame = stack[stack.length - 1];
|
|
201
|
+
if (!frame)
|
|
202
|
+
return content;
|
|
203
|
+
frame.active = !frame.taken;
|
|
204
|
+
frame.taken = true;
|
|
205
|
+
frame.seenElse = true;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case "endif":
|
|
209
|
+
case "legacy-close": {
|
|
210
|
+
if (!stack.length)
|
|
211
|
+
return content;
|
|
212
|
+
stack.pop();
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (stack.length)
|
|
218
|
+
return content;
|
|
219
|
+
out += content.slice(cursor);
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
function unknownTargets(names) {
|
|
223
|
+
return names.filter((name) => !TARGETS.includes(name));
|
|
224
|
+
}
|
|
225
|
+
/** Reports `AB120`, `AB121`, and `AB123` for one file's markers. */
|
|
226
|
+
export function validateConditionals(content, file, diagnostics, options = {}) {
|
|
227
|
+
const { tokens, malformed } = tokenize(content, options.markdown ?? true);
|
|
228
|
+
for (const item of malformed)
|
|
229
|
+
diagnostics.push({
|
|
230
|
+
...diagnostic("AB123", `Malformed conditional marker on line ${item.line}: ${item.raw}`, "unsupported", {
|
|
231
|
+
path: file,
|
|
232
|
+
remediation: "Write <!-- target:<name> --> with no space after the colon, or <!-- if target:<name> --> / <!-- elif ... --> / <!-- else --> / <!-- endif -->.",
|
|
233
|
+
}),
|
|
234
|
+
severity: "error",
|
|
235
|
+
});
|
|
236
|
+
const unknown = (names, line) => {
|
|
237
|
+
const bad = unknownTargets(names);
|
|
238
|
+
if (!bad.length)
|
|
239
|
+
return;
|
|
240
|
+
diagnostics.push({
|
|
241
|
+
...diagnostic("AB120", `Unknown target block '${bad.join(", ")}' on line ${line}`, "unsupported", {
|
|
242
|
+
path: file,
|
|
243
|
+
remediation: `Use ${TARGETS.join(", ")}.`,
|
|
244
|
+
}),
|
|
245
|
+
severity: "error",
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
const unbalanced = (message, line) => {
|
|
249
|
+
diagnostics.push({
|
|
250
|
+
...diagnostic("AB121", line ? `${message} on line ${line}` : message, "unsupported", {
|
|
251
|
+
path: file,
|
|
252
|
+
remediation: "Close conditional blocks in nesting order.",
|
|
253
|
+
}),
|
|
254
|
+
severity: "error",
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
const stack = [];
|
|
258
|
+
for (const token of tokens) {
|
|
259
|
+
const marker = token.marker;
|
|
260
|
+
switch (marker.kind) {
|
|
261
|
+
case "legacy-open":
|
|
262
|
+
unknown([marker.target], token.line);
|
|
263
|
+
stack.push({ legacy: { syntax: marker.syntax, target: marker.target }, seenElse: false });
|
|
264
|
+
break;
|
|
265
|
+
case "if":
|
|
266
|
+
unknown(marker.predicate.targets, token.line);
|
|
267
|
+
stack.push({ seenElse: false });
|
|
268
|
+
break;
|
|
269
|
+
case "elif": {
|
|
270
|
+
unknown(marker.predicate.targets, token.line);
|
|
271
|
+
const frame = stack[stack.length - 1];
|
|
272
|
+
if (!frame)
|
|
273
|
+
unbalanced("elif outside a conditional block", token.line);
|
|
274
|
+
else if (frame.legacy)
|
|
275
|
+
unbalanced("elif inside a legacy target block; use if/elif/endif", token.line);
|
|
276
|
+
else if (frame.seenElse)
|
|
277
|
+
unbalanced("elif after else", token.line);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case "else": {
|
|
281
|
+
const frame = stack[stack.length - 1];
|
|
282
|
+
if (!frame)
|
|
283
|
+
unbalanced("else outside a conditional block", token.line);
|
|
284
|
+
else if (frame.legacy)
|
|
285
|
+
unbalanced("else inside a legacy target block; use if/else/endif", token.line);
|
|
286
|
+
else if (frame.seenElse)
|
|
287
|
+
unbalanced("a conditional block has two else branches", token.line);
|
|
288
|
+
else
|
|
289
|
+
frame.seenElse = true;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
case "legacy-close": {
|
|
293
|
+
unknown([marker.target], token.line);
|
|
294
|
+
const frame = stack.pop();
|
|
295
|
+
if (!frame?.legacy ||
|
|
296
|
+
frame.legacy.target !== marker.target ||
|
|
297
|
+
frame.legacy.syntax !== marker.syntax)
|
|
298
|
+
unbalanced("Unmatched or misnested target block", token.line);
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
case "endif": {
|
|
302
|
+
const frame = stack.pop();
|
|
303
|
+
if (!frame)
|
|
304
|
+
unbalanced("endif outside a conditional block", token.line);
|
|
305
|
+
else if (frame.legacy)
|
|
306
|
+
unbalanced("endif closing a legacy target block", token.line);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (stack.length)
|
|
312
|
+
unbalanced("Unclosed conditional block");
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=conditionals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditionals.js","sourceRoot":"","sources":["../../src/agent/conditionals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AgentTarget } from "../types.js";
|
|
2
|
+
import type { InstallScope } from "./index.js";
|
|
3
|
+
/** One bundle in the block, with the targets it is installed for. */
|
|
4
|
+
export interface InstallSpecBundle {
|
|
5
|
+
/** Config-relative POSIX path, as written. */
|
|
6
|
+
path: string;
|
|
7
|
+
/** Absolute bundle root. */
|
|
8
|
+
root: string;
|
|
9
|
+
include?: AgentTarget[];
|
|
10
|
+
exclude?: AgentTarget[];
|
|
11
|
+
}
|
|
12
|
+
export interface InstallConfig {
|
|
13
|
+
/** Absolute path to the document this block came from. */
|
|
14
|
+
file: string;
|
|
15
|
+
/** Absolute directory the document lives in; every bundle path resolves from it. */
|
|
16
|
+
root: string;
|
|
17
|
+
targets: AgentTarget[];
|
|
18
|
+
scope: InstallScope;
|
|
19
|
+
/** Absolute install root override, when the block declares `into`. */
|
|
20
|
+
into?: string;
|
|
21
|
+
link: boolean;
|
|
22
|
+
register: boolean;
|
|
23
|
+
bundles: InstallSpecBundle[];
|
|
24
|
+
}
|
|
25
|
+
/** True when `bundle` is installed for `target` under its include/exclude declaration. */
|
|
26
|
+
export declare function installsFor(bundle: InstallSpecBundle, target: AgentTarget): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Parses an `agent:` block's `install` key. Returns `undefined` when absent, so
|
|
29
|
+
* a document that declares only `verify:` is valid rather than an error.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseInstallBlock(value: unknown, context: {
|
|
32
|
+
file: string;
|
|
33
|
+
directory: string;
|
|
34
|
+
}): InstallConfig | undefined;
|
|
35
|
+
/** Validates that every declared bundle root exists. Deferred so a parse is pure. */
|
|
36
|
+
export declare function checkInstallConfig(config: InstallConfig): void;
|
|
37
|
+
/**
|
|
38
|
+
* Finds the `agent.install` block, by explicit path or by walking up.
|
|
39
|
+
*
|
|
40
|
+
* The same discovery `agent verify` uses, and for the same reason: `agent`
|
|
41
|
+
* commands run with configuration discovery disabled, so they resolve their own
|
|
42
|
+
* block rather than reading it off `ResolvedConfig`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveInstallConfig(selection?: {
|
|
45
|
+
explicitPath?: string;
|
|
46
|
+
cwd?: string;
|
|
47
|
+
}): InstallConfig;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { isInside, knownKeys, object, optionalString } from "../../config-schema.js";
|
|
4
|
+
import { TARGETS } from "../types.js";
|
|
5
|
+
import { configIn } from "../../config.js";
|
|
6
|
+
import { readAgentDocument } from "../verify/resolve.js";
|
|
7
|
+
/**
|
|
8
|
+
* The `agent.install` block: what a repository installs into itself.
|
|
9
|
+
*
|
|
10
|
+
* A sibling of `agent.verify` rather than a reuse of it, because the two answer
|
|
11
|
+
* different questions — this one declares what to *write*, `verify` asserts what
|
|
12
|
+
* is *there* — and a flag that could widen either would be a surprise in the
|
|
13
|
+
* other. Parsed here rather than in `src/config.ts` for the reason `scripts:` is:
|
|
14
|
+
* `agent` commands run with configuration discovery disabled, so they resolve
|
|
15
|
+
* this block through their own walk. The loader still calls
|
|
16
|
+
* {@link parseInstallBlock} for its throw, so a typo is an error at `md lint`
|
|
17
|
+
* rather than a surprise at install time.
|
|
18
|
+
*/
|
|
19
|
+
const BLOCK_KEYS = new Set(["targets", "scope", "into", "link", "register", "bundles"]);
|
|
20
|
+
const BUNDLE_KEYS = new Set(["path", "include", "exclude"]);
|
|
21
|
+
function targetList(value, name) {
|
|
22
|
+
if (value === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string"))
|
|
25
|
+
throw new Error(`${name} must be a list of target names`);
|
|
26
|
+
const unknown = value.filter((item) => !TARGETS.includes(item));
|
|
27
|
+
if (unknown.length)
|
|
28
|
+
throw new Error(`Unknown target in ${name}: ${unknown.join(", ")}`);
|
|
29
|
+
return [...new Set(value)];
|
|
30
|
+
}
|
|
31
|
+
function contained(raw, directory, name) {
|
|
32
|
+
const resolved = path.resolve(directory, raw);
|
|
33
|
+
if (!isInside(directory, resolved))
|
|
34
|
+
throw new Error(`${name} escapes the configuration directory: ${raw}`);
|
|
35
|
+
return resolved;
|
|
36
|
+
}
|
|
37
|
+
function specBundle(value, index, directory) {
|
|
38
|
+
const name = `agent.install.bundles[${index}]`;
|
|
39
|
+
const block = object(value, name);
|
|
40
|
+
knownKeys(block, BUNDLE_KEYS, name);
|
|
41
|
+
const raw = optionalString(block.path, `${name}.path`);
|
|
42
|
+
if (!raw)
|
|
43
|
+
throw new Error(`${name}.path is required`);
|
|
44
|
+
const root = contained(raw, directory, `${name}.path`);
|
|
45
|
+
const include = targetList(block.include, `${name}.include`);
|
|
46
|
+
const exclude = targetList(block.exclude, `${name}.exclude`);
|
|
47
|
+
// Same rule as the marketplace spec: one selector or the other, never both,
|
|
48
|
+
// because their intersection has no reading a user would predict.
|
|
49
|
+
if (include && exclude)
|
|
50
|
+
throw new Error(`${name} declares both include and exclude`);
|
|
51
|
+
return { path: raw, root, ...(include ? { include } : {}), ...(exclude ? { exclude } : {}) };
|
|
52
|
+
}
|
|
53
|
+
/** True when `bundle` is installed for `target` under its include/exclude declaration. */
|
|
54
|
+
export function installsFor(bundle, target) {
|
|
55
|
+
if (bundle.include)
|
|
56
|
+
return bundle.include.includes(target);
|
|
57
|
+
if (bundle.exclude)
|
|
58
|
+
return !bundle.exclude.includes(target);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Parses an `agent:` block's `install` key. Returns `undefined` when absent, so
|
|
63
|
+
* a document that declares only `verify:` is valid rather than an error.
|
|
64
|
+
*/
|
|
65
|
+
export function parseInstallBlock(value, context) {
|
|
66
|
+
if (value === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
const root = object(value, "agent");
|
|
69
|
+
if (root.install === undefined)
|
|
70
|
+
return undefined;
|
|
71
|
+
const block = object(root.install, "agent.install");
|
|
72
|
+
knownKeys(block, BLOCK_KEYS, "agent.install");
|
|
73
|
+
const targets = targetList(block.targets, "agent.install.targets");
|
|
74
|
+
if (!targets?.length)
|
|
75
|
+
throw new Error("agent.install.targets must declare at least one target");
|
|
76
|
+
const scopeRaw = optionalString(block.scope, "agent.install.scope") ?? "project";
|
|
77
|
+
if (scopeRaw !== "user" && scopeRaw !== "project")
|
|
78
|
+
throw new Error(`Unknown scope in agent.install.scope: ${scopeRaw}`);
|
|
79
|
+
const intoRaw = optionalString(block.into, "agent.install.into");
|
|
80
|
+
for (const key of ["link", "register"])
|
|
81
|
+
if (block[key] !== undefined && typeof block[key] !== "boolean")
|
|
82
|
+
throw new Error(`agent.install.${key} must be a boolean`);
|
|
83
|
+
const declared = block.bundles;
|
|
84
|
+
if (declared !== undefined && !Array.isArray(declared))
|
|
85
|
+
throw new Error("agent.install.bundles must be a list");
|
|
86
|
+
const list = (declared ?? []);
|
|
87
|
+
if (!list.length)
|
|
88
|
+
throw new Error("agent.install.bundles must declare at least one bundle");
|
|
89
|
+
const bundles = list.map((item, index) => specBundle(item, index, context.directory));
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
for (const bundle of bundles) {
|
|
92
|
+
if (seen.has(bundle.root))
|
|
93
|
+
throw new Error(`agent.install.bundles declares ${bundle.path} twice`);
|
|
94
|
+
seen.add(bundle.root);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
file: context.file,
|
|
98
|
+
root: context.directory,
|
|
99
|
+
targets,
|
|
100
|
+
scope: scopeRaw,
|
|
101
|
+
...(intoRaw ? { into: contained(intoRaw, context.directory, "agent.install.into") } : {}),
|
|
102
|
+
link: Boolean(block.link),
|
|
103
|
+
register: Boolean(block.register),
|
|
104
|
+
bundles,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/** Validates that every declared bundle root exists. Deferred so a parse is pure. */
|
|
108
|
+
export function checkInstallConfig(config) {
|
|
109
|
+
for (const bundle of config.bundles) {
|
|
110
|
+
if (!fs.existsSync(bundle.root) || !fs.statSync(bundle.root).isDirectory())
|
|
111
|
+
throw new Error(`agent.install bundle path is not a directory: ${bundle.path}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function parseAt(file) {
|
|
115
|
+
const directory = path.dirname(path.resolve(file));
|
|
116
|
+
const document = readAgentDocument(file);
|
|
117
|
+
if (document.agent === undefined)
|
|
118
|
+
return undefined;
|
|
119
|
+
return parseInstallBlock(document.agent, { file: path.resolve(file), directory });
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Finds the `agent.install` block, by explicit path or by walking up.
|
|
123
|
+
*
|
|
124
|
+
* The same discovery `agent verify` uses, and for the same reason: `agent`
|
|
125
|
+
* commands run with configuration discovery disabled, so they resolve their own
|
|
126
|
+
* block rather than reading it off `ResolvedConfig`.
|
|
127
|
+
*/
|
|
128
|
+
export function resolveInstallConfig(selection = {}) {
|
|
129
|
+
if (selection.explicitPath) {
|
|
130
|
+
const file = path.resolve(selection.explicitPath);
|
|
131
|
+
if (!fs.existsSync(file))
|
|
132
|
+
throw new Error(`Configuration file not found: ${file}`);
|
|
133
|
+
const config = parseAt(file);
|
|
134
|
+
if (!config)
|
|
135
|
+
throw new Error(`No 'agent.install' block in ${file}`);
|
|
136
|
+
checkInstallConfig(config);
|
|
137
|
+
return config;
|
|
138
|
+
}
|
|
139
|
+
let current = path.resolve(selection.cwd ?? process.cwd());
|
|
140
|
+
while (true) {
|
|
141
|
+
const candidate = configIn(current);
|
|
142
|
+
if (candidate) {
|
|
143
|
+
const config = parseAt(candidate);
|
|
144
|
+
if (config) {
|
|
145
|
+
checkInstallConfig(config);
|
|
146
|
+
return config;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const parent = path.dirname(current);
|
|
150
|
+
if (parent === current)
|
|
151
|
+
break;
|
|
152
|
+
current = parent;
|
|
153
|
+
}
|
|
154
|
+
throw new Error("No 'agent.install' block found. Declare one in .cairn.yml, or pass --config <file>.");
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/agent/install/config.ts"],"names":[],"mappings":"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"}
|