cyber-agentkit 1.2.0 → 2.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/dist/commands.js CHANGED
@@ -1,9 +1,9 @@
1
- import { A as skillName, C as repositoryRoot, M as require_dist, R as pluginManifestName, S as referenceLayers, _ as parseDocument, a as interfaceLookup, c as readExtends, d as parseReferenceName, f as fitsName, g as isMergeOperation, h as hasBody, i as checkResolved, m as frontmatterPattern, n as referenceImplementations, o as listReferences, p as dataExtensions, r as checkReferences, s as searchReferences, t as whereReference, u as resolveReference, v as parseSections, w as userReferencesDir, x as projectReferencesDir, y as canonical } from "./reference-where-C84J6MsR.js";
1
+ import { B as userAssetsDir, C as referenceLayers, E as ReferenceFailure, F as resolveAsset, I as assetDataExtensions, K as skillName, L as parseAssetName, P as assetNames, R as assetLayers, S as findRoot, b as requireName, c as deleteContribution, d as insideRepository, f as checkReferences, g as searchReferences, h as listReferences, l as describeAnswer, m as interfaceLookup, n as referenceImplementations, p as checkResolved, r as customizeContribution, s as createContribution, t as whereReference, u as validateWrite, v as resolveReference, y as parseReferenceName, z as projectAssetsDir } from "./reference-where-CVmGFOwR.js";
2
2
  import { homedir } from "node:os";
3
3
  import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, rmdirSync, writeFileSync } from "node:fs";
4
- import { basename, dirname, extname, join, resolve, sep } from "node:path";
4
+ import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
5
5
  import { command, exitCodes, t } from "clibuilder";
6
- import { CliError, document, encode, isCliError, printAnswer, result } from "clibuilder/axi";
6
+ import { CliError, document, empty, encode, isCliError, printAnswer, result } from "clibuilder/axi";
7
7
  //#region src/command-output/collapse-home.ts
8
8
  /**
9
9
  * AXI §10: collapses the home directory to `~` so a path in a report is portable to another
@@ -13,351 +13,124 @@ function collapseHome(home, path) {
13
13
  return home && path.startsWith(home + sep) ? `~${path.slice(home.length)}` : path;
14
14
  }
15
15
  //#endregion
16
- //#region src/references/create-reference.ts
17
- var import_dist = require_dist();
18
- /** No `#` title: `patch` matches by heading path, so a title would hold every section under it. */
19
- const defaultTemplate = `---
20
- description: What this reference covers, and when an agent should read it.
21
- tags: [topic]
22
- ---
23
-
24
- ## Overview
25
-
26
- Write the guidance here. Give each topic its own top-level \`##\` section.
27
- `;
28
- /** The interface's sections as `##` headings, in order, each stubbed `**Unsupported.**`. */
29
- function implementationTemplate(iface) {
30
- const sections = iface.sections.map((name) => `## ${name}\n\n**Unsupported.** Replace this line with the section, or say in one sentence why it is not supported.\n`);
31
- return parseTemplate(`---\ndescription: What this ${iface.id} implementation covers, and when an agent should read it.\nimplements: ${iface.id}@${iface.version}\n---\n\n${sections.join("\n")}`, `the ${iface.id} interface`);
32
- }
33
- function readTemplate(path) {
34
- if (path === void 0) return parseTemplate(defaultTemplate, "the default template");
35
- let content;
36
- try {
37
- content = readFileSync(path, "utf8");
38
- } catch (error) {
39
- throw new Error(`cannot read the template ${path}: ${error.message}`);
40
- }
41
- return parseTemplate(content, `the template ${path}`);
42
- }
43
- function parseTemplate(content, label) {
44
- let mapping = true;
45
- const { metadata, body } = parseDocument(content, () => {
46
- mapping = false;
47
- });
48
- if (!mapping) throw new Error(`${label} has frontmatter that is not a YAML mapping.`);
49
- return {
50
- content,
51
- metadata,
52
- body
53
- };
54
- }
55
- /**
56
- * A title: the top level is a single heading holding every other one. `patch` matches by heading
57
- * path, so it would add the whole document under the title instead of patching its sections.
58
- */
59
- function titleOf({ body }) {
60
- const tree = parseSections(body, () => {});
61
- const [only] = tree.children;
62
- return tree.children.length === 1 && only?.children.length ? only.heading.trim() : void 0;
63
- }
64
- function hasDescription({ metadata }) {
65
- return typeof metadata["description"] === "string" && metadata["description"].trim().length > 0;
16
+ //#region src/assets/asset-catalog.ts
17
+ function patternMatcher(pattern) {
18
+ const source = pattern.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&"));
19
+ return new RegExp(`^${source.join(".*")}$`, "s");
66
20
  }
67
21
  /**
68
- * Sets or removes frontmatter keys and keeps the rest of the file as written: other keys, comments,
69
- * and the body. A file left with no key and no body is empty.
22
+ * One row per asset name any layer holds, and one row per name per layer holding it (the skill's own
23
+ * `default` tier is not listed per layer). A `pattern` keeps only the names it matches exactly.
70
24
  */
71
- function editFrontmatter(raw, keys) {
72
- const match = frontmatterPattern.exec(raw);
73
- const body = match ? raw.slice(match[0].length) : raw;
74
- const doc = (0, import_dist.parseDocument)(match ? match[1] : "");
75
- for (const [key, value] of Object.entries(keys)) if (value === void 0) doc.delete(key);
76
- else {
77
- const node = doc.createNode(value, { flow: Array.isArray(value) });
78
- doc.set(key, node);
25
+ function listAssetNames(source, { pattern, ...resolveOptions } = {}) {
26
+ const matcher = pattern === void 0 ? void 0 : patternMatcher(pattern);
27
+ const listing = {
28
+ references: [],
29
+ layers: [],
30
+ warnings: []
31
+ };
32
+ const seen = /* @__PURE__ */ new Set();
33
+ for (const name of assetNames(source)) {
34
+ if (matcher && !matcher.test(name)) continue;
35
+ const resolved = resolveAsset(name, source, resolveOptions);
36
+ for (const warning of resolved.warnings) {
37
+ const key = `${warning.code} ${warning.message}`;
38
+ if (!seen.has(key)) listing.warnings.push(warning);
39
+ seen.add(key);
40
+ }
41
+ const row = {
42
+ name,
43
+ status: resolved.status
44
+ };
45
+ if (resolved.format && resolved.format !== "markdown") row.format = resolved.format;
46
+ if (resolved.options) row.options = resolved.options.filter((option) => option !== "default");
47
+ listing.references.push(row);
48
+ for (const entry of resolved.trace.layers) {
49
+ if (entry.file === void 0 || entry.tier === "default") continue;
50
+ const layerRow = {
51
+ name,
52
+ tier: entry.tier,
53
+ path: entry.path,
54
+ outcome: entry.outcome
55
+ };
56
+ if (entry.plugin !== void 0) layerRow.plugin = entry.plugin;
57
+ if (entry.skill !== void 0) layerRow.skill = entry.skill;
58
+ listing.layers.push(layerRow);
59
+ }
79
60
  }
80
- if (!doc.contents?.items?.length) return body.replace(/^(?:[ \t]*\r?\n)+/, "");
81
- const gap = !match && body.trim() ? "\n" : "";
82
- return `---\n${doc.toString({
83
- lineWidth: 0,
84
- flowCollectionPadding: false
85
- })}---\n${gap}${body}`;
61
+ return listing;
86
62
  }
87
63
  //#endregion
88
- //#region src/references/reference-write.ts
89
- /** A write refused. `usage` marks a wrong invocation (exit 2) rather than a refusal (exit 1). */
90
- var ReferenceWriteError = class extends Error {
91
- code;
92
- usage;
93
- constructor(code, message, usage = false) {
94
- super(message);
95
- this.code = code;
96
- this.usage = usage;
97
- }
64
+ //#region src/assets/asset-where.ts
65
+ const reaches = {
66
+ project: "everyone working in this repository",
67
+ user: "only you, in every repository"
98
68
  };
99
- /** A bare, valid name, or a refusal naming the bare name to use. */
100
- function bareName(value) {
101
- const name = parseReferenceName(value);
102
- if (name.invalid) throw new ReferenceWriteError("reference-invalid", name.invalid);
103
- if (name.plugin !== void 0) throw new ReferenceWriteError("reference-qualified", `a contribution is written under the bare name, which every tier resolves; ask for "${name.name}".`);
104
- return name;
105
- }
106
- /** The repository the root sits in: a write to a source folder must land inside it, and never in an installed copy. */
107
- function insideRepository(folder, root) {
108
- const repository = canonical(repositoryRoot(root) ?? resolve(root));
109
- const target = canonical(folder);
110
- if (target !== repository && !target.startsWith(repository + sep)) return false;
111
- return !target.slice(repository.length).split(sep).includes("node_modules");
112
- }
113
- /** Resolves `--scope`, `--plugin`, and `--default` to the file a write acts on; more than one is a usage error. */
114
- function writeTarget(name, { root, home, scope, plugin, default: skill }) {
115
- if ([
116
- scope,
117
- plugin,
118
- skill
119
- ].filter((value) => value !== void 0).length > 1) throw new ReferenceWriteError("options-conflict", "pass one of --scope, --plugin, and --default.", true);
120
- if (plugin !== void 0) {
121
- const folder = resolve(plugin);
122
- if (!pluginManifestName(folder)) throw new ReferenceWriteError("folder-invalid", `${folder} holds no plugin manifest, so it is not a plugin folder.`);
123
- if (!insideRepository(folder, root)) throw new ReferenceWriteError("folder-invalid", `${folder} is outside the repository, such as an installed copy; write to the plugin's source instead.`);
124
- const dir = join(folder, "references");
125
- return {
126
- scope: "plugin",
127
- dir,
128
- path: join(dir, `${name.name}.md`)
129
- };
130
- }
131
- if (skill !== void 0) {
132
- const folder = resolve(skill);
133
- if (skillName(folder) === void 0) throw new ReferenceWriteError("folder-invalid", `${folder} holds no SKILL.md, so it is not a skill folder.`);
134
- if (!insideRepository(folder, root)) throw new ReferenceWriteError("folder-invalid", `${folder} is outside the repository, such as an installed copy; write to the skill's source instead.`);
135
- const dir = join(folder, "references");
136
- return {
137
- scope: "default",
138
- dir,
139
- path: join(dir, `${name.name}.default.md`)
140
- };
141
- }
142
- if (scope !== void 0 && scope !== "project" && scope !== "user") throw new ReferenceWriteError("options-invalid", "--scope is project or user.", true);
143
- const dir = scope === "user" ? userReferencesDir(home) : projectReferencesDir(resolve(root));
144
- return {
145
- scope: scope === "user" ? "user" : "project",
146
- dir,
147
- path: join(dir, `${name.name}.md`)
148
- };
149
- }
150
- function tryRead(path) {
151
- try {
152
- return readFileSync(path, "utf8");
153
- } catch {
154
- return;
155
- }
156
- }
157
- /** What answers a name, in one sentence a person reads. */
158
- function describeAnswer(resolved, display = (path) => path) {
159
- if (resolved.status === "found") {
160
- const top = resolved.trace.contributions.at(-1);
161
- const by = top ? top.tier === "plugin" ? `plugin ${top.plugin}` : top.tier === "default" ? `the default of ${top.skill}` : `the ${top.tier} file ${display(top.path)}` : "nothing";
162
- const stack = resolved.trace.stack.length ? `, on the stack ${resolved.trace.stack.join(" → ")}` : "";
163
- return `"${resolved.name}" is answered by ${by}${stack}.`;
164
- }
165
- if (resolved.status === "ambiguous") return `"${resolved.name}" is ambiguous: ${resolved.message}; the options are ${(resolved.options ?? []).join(", ")}.`;
166
- return `"${resolved.name}" is ${resolved.status}: ${resolved.message}.`;
167
- }
168
- /** The interface `create --implements` starts an implementation of, once the new name fits it. */
169
- function interfaceFor(id, name, layers, options) {
170
- const parsed = parseReferenceName(id);
171
- if (parsed.invalid || parsed.plugin !== void 0) throw new ReferenceWriteError("interface-invalid", `"${id}" is not an interface id.`);
172
- const iface = interfaceLookup(layers, options)(id);
173
- if (!iface.value) throw new ReferenceWriteError("interface-invalid", iface.failure?.message ?? `no interface ${id}`);
174
- if (!fitsName(iface.value, name)) throw new ReferenceWriteError("interface-invalid", `an implementation of ${id} is named ${iface.value.cardinality === "one" ? id : `${id}.<key>`}, not "${name}".`);
175
- return iface.value;
176
- }
177
- /**
178
- * Hides the new file: a `replace` with a body, or a `remove`, that applies above it. A folder that
179
- * is not a layer the stack reads has nothing above it.
180
- */
181
- function hiddenBy(resolved, dir, path) {
182
- const layers = resolved.trace.layers;
183
- const at = layers.findIndex((layer) => layer.path === dir || layer.path === path);
184
- if (at === -1) return void 0;
185
- const target = layers[at];
186
- const tiered = (tier) => tier === "managed" || tier === "project" || tier === "user";
187
- const stackAt = target && !tiered(target.tier) ? resolved.trace.stack.indexOf(target.tier === "default" ? "default" : target.plugin ?? "") : -1;
188
- const hider = layers.filter((layer, index) => {
189
- if (!layer.file || layer.outcome !== "used") return false;
190
- if (target && tiered(target.tier)) return index < at && tiered(layer.tier);
191
- if (tiered(layer.tier)) return true;
192
- const position = resolved.trace.stack.indexOf(layer.tier === "default" ? "default" : layer.plugin ?? "");
193
- return stackAt !== -1 && position > stackAt;
194
- }).find((layer) => layer.operation === "remove" || layer.operation === "replace" && !layer.empty);
195
- return hider ? `${hider.tier}${hider.plugin ? ` ${hider.plugin}` : ""} ${hider.path} (${hider.operation})` : void 0;
196
- }
197
- /** A selection-only file keeps its own keys, `extends` among them; the template adds the keys it lacks. */
198
- function selectionKeys(existing, template, merge) {
199
- const held = parseDocument(existing, () => {}).metadata;
200
- const keys = {};
201
- for (const [key, value] of Object.entries(template.metadata)) if (key !== "merge" && !(key in held)) keys[key] = value;
202
- keys["merge"] = merge;
203
- return keys;
204
- }
205
- function withBody(head, body) {
206
- const text = body.replace(/^(?:[ \t]*\r?\n)+/, "");
207
- return text ? `${head.replace(/\s*$/, "")}\n\n${text}` : head;
208
- }
209
- function createContribution(layers, args, options = {}) {
210
- const name = bareName(args.name);
211
- const target = writeTarget(name, args);
212
- if (args.merge !== void 0 && !isMergeOperation(args.merge)) throw new ReferenceWriteError("options-invalid", `--merge is replace, patch, combine, or remove; got "${args.merge}".`, true);
213
- const merge = args.merge;
214
- if (args.template !== void 0 && args.implements !== void 0) throw new ReferenceWriteError("options-conflict", "pass --template or --implements, not both.");
215
- if (merge === "remove" && args.template !== void 0) throw new ReferenceWriteError("options-conflict", "--merge remove writes no body, so it takes no --template.");
216
- if (merge !== void 0 && target.scope === "default") throw new ReferenceWriteError("options-conflict", "a default is written with no merge; drop --merge.");
217
- const existing = tryRead(target.path);
218
- if (existing !== void 0 && hasBody(parseDocument(existing, () => {}).body)) throw new ReferenceWriteError("file-exists", `${(options.display ?? ((path) => path))(target.path)} already holds "${name.name}"; change it with the reference skill's Update mode.`);
219
- const template = args.implements !== void 0 ? implementationTemplate(interfaceFor(args.implements, name.name, layers, options)) : merge === "remove" ? {
220
- content: "",
221
- metadata: {},
222
- body: ""
223
- } : readTemplate(args.template === void 0 ? void 0 : resolve(args.template));
224
- const title = titleOf(template);
225
- if (title) throw new ReferenceWriteError("template-title", `the template's top level is the single heading "${title}" holding the others; patch matches by heading path, so make its sections top-level.`);
226
- const before = resolveReference(name, layers, options);
227
- const hider = hiddenBy(before, target.dir, target.path);
228
- if (hider) throw new ReferenceWriteError("hidden", `${hider} applies above it, so nothing would read the new file; change that contribution instead.`);
229
- const operation = target.scope === "default" ? void 0 : args.implements !== void 0 ? "replace" : merge ?? (isMergeOperation(template.metadata["merge"]) ? template.metadata["merge"] : void 0) ?? (target.scope === "plugin" ? void 0 : "patch");
230
- const content = existing === void 0 ? editFrontmatter(template.content, { merge: operation }) : withBody(editFrontmatter(existing, selectionKeys(existing, template, operation)), template.body);
231
- const hints = hasDescription(parseDocument(content, () => {})) ? [] : ["it has no `description` in its frontmatter, so `list` and `search` show it blank"];
232
- const result = {
233
- name: name.name,
234
- scope: target.scope,
235
- path: target.path,
236
- content,
237
- dryRun: Boolean(args.dryRun),
238
- hints
69
+ /** The project and user folders an override of the asset `name` is written to. */
70
+ function whereAsset(input, source, { root, display = (path) => path }) {
71
+ const name = typeof input === "string" ? parseAssetName(input) : input;
72
+ const resolved = resolveAsset(name, source, { display });
73
+ const report = {
74
+ name: name.raw,
75
+ status: resolved.status,
76
+ places: [],
77
+ layers: resolved.trace.layers,
78
+ warnings: resolved.warnings
239
79
  };
240
- if (result.dryRun) return result;
241
- mkdirSync(target.dir, { recursive: true });
242
- writeFileSync(target.path, content, existing === void 0 ? { flag: "wx" } : {});
243
- if (before.trace.layers.some((layer) => layer.path === target.dir || layer.path === target.path)) result.after = resolveReference(name, layers, options);
244
- return result;
245
- }
246
- function selectContribution(layers, args, options = {}) {
247
- const name = bareName(args.name);
248
- const target = writeTarget(name, {
249
- root: args.root,
250
- home: args.home,
251
- scope: args.scope
252
- });
253
- const scope = target.scope;
254
- const existing = tryRead(target.path);
255
- if (args.clear) {
256
- if (args.options.length) throw new ReferenceWriteError("options-conflict", "--clear takes no option.", true);
257
- if (!(existing === void 0 ? void 0 : readExtends(parseDocument(existing, () => {}).metadata["extends"], () => {}))) throw new ReferenceWriteError("no-selection", `the ${scope} tier holds no selection of "${name.name}". ${describeAnswer(resolveReference(name, layers, options), options.display)}`);
258
- const content = editFrontmatter(existing, { extends: void 0 });
259
- const deleted = !content.trim();
260
- if (deleted) rmSync(target.path);
261
- else writeFileSync(target.path, content);
262
- return {
263
- name: name.name,
80
+ if (resolved.options) report.options = resolved.options;
81
+ if (name.invalid) return report;
82
+ const userDir = (Array.isArray(source) ? source : source.layers).find(({ tier }) => tier === "user")?.dir;
83
+ const targets = [{
84
+ scope: "project",
85
+ dir: projectAssetsDir(root)
86
+ }, ...userDir ? [{
87
+ scope: "user",
88
+ dir: userDir
89
+ }] : []];
90
+ for (const { scope, dir } of targets) {
91
+ const held = [join(dir, `${name.name}.md`), ...assetDataExtensions.map(({ extension }) => join(dir, `${name.name}${extension}`))];
92
+ const entry = resolved.trace.layers.find((layer) => held.includes(layer.path));
93
+ report.places.push({
264
94
  scope,
265
- path: target.path,
266
- extends: [],
267
- deleted,
268
- after: resolveReference(name, layers, options)
269
- };
270
- }
271
- if (!args.options.length) throw new ReferenceWriteError("options-missing", "name at least one option: a plugin or default.", true);
272
- const before = resolveReference(name, layers, options);
273
- const holders = new Set(before.trace.layers.flatMap(({ plugin, file }) => plugin !== void 0 && file ? [plugin] : []));
274
- const picked = args.options.map((option) => option.endsWith(`/${name.name}`) ? option.slice(0, -name.name.length - 1) : option);
275
- const wrong = picked.filter((option) => option !== "default" && !holders.has(option));
276
- if (wrong.length) throw new ReferenceWriteError("option-invalid", `${wrong.join(", ")} ${wrong.length === 1 ? "is" : "are"} neither default nor a plugin holding "${name.name}"${holders.size ? `; the plugins holding it are ${[...holders].join(", ")}` : ""}.`);
277
- const content = editFrontmatter(existing ?? "", { extends: picked });
278
- mkdirSync(target.dir, { recursive: true });
279
- writeFileSync(target.path, content);
280
- return {
281
- name: name.name,
282
- scope,
283
- path: target.path,
284
- extends: picked,
285
- deleted: false,
286
- after: resolveReference(name, layers, options)
287
- };
288
- }
289
- /** The data files that can answer a name in a folder. */
290
- function dataFiles(dir, name) {
291
- return dataExtensions.map(({ extension }) => join(dir, `${name}${extension}`));
292
- }
293
- function deleteContribution(layers, args, options = {}) {
294
- const name = bareName(args.name);
295
- const target = writeTarget(name, args);
296
- const shown = options.display ?? ((path) => path);
297
- const before = resolveReference(name, layers, options);
298
- const path = (target.scope === "project" || target.scope === "user" ? [
299
- target.path,
300
- join(target.dir, name.name, "README.md"),
301
- ...dataFiles(target.dir, name.name)
302
- ] : [target.path]).find((candidate) => candidate !== options.without && existsSync(candidate));
303
- if (!path) throw new ReferenceWriteError("no-file", `${shown(target.path)} does not exist, so there is no ${target.scope} contribution of "${name.name}" to delete. ${describeAnswer(before, options.display)} An installed plugin's, a managed, or an installed skill's contribution is never deleted.`);
304
- const steps = readExtends(parseDocument(tryRead(path) ?? "", () => {}).metadata["extends"], () => {});
305
- const dryRun = Boolean(args.dryRun);
306
- if (!dryRun) {
307
- rmSync(path);
308
- if (dirname(path) !== target.dir) try {
309
- rmdirSync(dirname(path));
310
- } catch {}
95
+ path: `${scope === "project" ? relative(root, dir) : display(dir)}/${name.name}.<ext>`,
96
+ reaches: reaches[scope],
97
+ status: entry?.file ? entry.outcome : "empty",
98
+ sections: []
99
+ });
311
100
  }
312
- const result = {
313
- name: name.name,
314
- scope: target.scope,
315
- path,
316
- dryRun,
317
- after: resolveReference(name, layers, {
318
- ...options,
319
- without: dryRun ? path : void 0
320
- })
321
- };
322
- if (steps) result.selection = steps.map(({ target: step }) => step);
323
- return result;
101
+ return report;
324
102
  }
325
103
  //#endregion
326
- //#region src/references/reference.command.ts
327
- const rootOption$1 = {
104
+ //#region src/assets/asset-resolve.command.ts
105
+ const rootOption$2 = {
328
106
  description: "Directory the project tier is read from, and each folder above it up to the repository root; a project write goes to its `.agents/references/`. Defaults to the current directory.",
329
107
  type: t.string().optional()
330
108
  };
331
- const traceOption = {
109
+ const traceOption$1 = {
332
110
  description: "Add the trace: every layer checked, with its outcome, and how the result was built.",
333
111
  type: t.boolean().optional()
334
112
  };
335
- const defaultOption = {
113
+ const defaultOption$1 = {
336
114
  description: "The calling skill's folder: its `references/<name>.default.md` joins as the `default` tier.",
337
115
  type: t.string().optional()
338
116
  };
339
- function layerSource(plugin, folder = "references") {
340
- return (args, home) => referenceLayers({
117
+ function layerSource$1(plugin) {
118
+ return (args, home) => assetLayers({
341
119
  root: args.root ?? process.cwd(),
342
120
  home,
343
121
  platform: process.platform,
344
122
  env: process.env,
345
123
  plugin,
346
- defaultSkill: args.default,
347
- folder
124
+ defaultSkill: args.default
348
125
  });
349
126
  }
350
127
  /** A field with nothing to say is left out: `undefined`, an empty string, or an empty list. */
351
- function compact(value) {
128
+ function compact$1(value) {
352
129
  return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== void 0 && field !== "" && !(Array.isArray(field) && !field.length)));
353
130
  }
354
131
  /** stderr, not stdout: an agent parses stdout. Rethrows what the cli already reports. */
355
132
  function fail$1(error, fallback) {
356
133
  if (isCliError(error)) throw error;
357
- if (error instanceof ReferenceWriteError) {
358
- process.stderr.write(`error: ${error.code}: ${error.message}\n`);
359
- throw CliError.reported(error.usage ? exitCodes.usage : exitCodes.error);
360
- }
361
134
  process.stderr.write(`error: ${error instanceof Error ? error.message : fallback}\n`);
362
135
  throw CliError.reported(exitCodes.error);
363
136
  }
@@ -371,10 +144,10 @@ function answerThenFail(answer, format) {
371
144
  if (text !== void 0) process.stdout.write(text);
372
145
  throw CliError.reported(exitCodes.error);
373
146
  }
374
- function writeWarnings(warnings) {
147
+ function writeWarnings$1(warnings) {
375
148
  for (const { code, message } of warnings) process.stderr.write(`warning: ${code}: ${message}\n`);
376
149
  }
377
- function displayTrace(trace, home) {
150
+ function displayTrace$1(trace, home) {
378
151
  const show = (path) => collapseHome(home, path);
379
152
  return {
380
153
  ...trace,
@@ -388,37 +161,27 @@ function displayTrace(trace, home) {
388
161
  }))
389
162
  };
390
163
  }
391
- /** The text trace: a summary line, then each section with its contributions, then each layer. */
392
- function traceText(resolved, home) {
393
- const trace = displayTrace(resolved.trace, home);
164
+ /** The text trace: a summary line, then each layer. */
165
+ function traceText$1(resolved, home) {
166
+ const trace = displayTrace$1(resolved.trace, home);
394
167
  const lines = [`${resolved.name}: ${resolved.status}; stack: ${trace.stack.join(" → ") || "none"}; built by ${trace.builtBy}; harnesses: ${trace.harnesses.join(", ") || "none detected"}`];
395
168
  if (resolved.message) lines.push(` ${resolved.message}`);
396
- if (trace.sections.length) {
397
- lines.push("sections:");
398
- for (const section of trace.sections) {
399
- const how = section.removedBy ? `removed by ${section.removedBy}` : section.contributions.map(({ contribution, operation }) => `${contribution} (${operation})`).join(", ") || "no text";
400
- lines.push(` ${section.path}: ${how}`);
401
- }
402
- }
403
169
  lines.push("layers:");
404
170
  for (const layer of trace.layers) {
405
171
  const who = layer.plugin ? ` ${layer.plugin}` : layer.skill ? ` ${layer.skill}` : "";
406
172
  const how = layer.operation ? `; ${layer.operation} from ${layer.operationFrom}` : "";
407
- const extendsNote = layer.extendsOutcome ? `; extends ${layer.extendsOutcome}` : "";
408
- const selection = layer.selection ? "; selection" : "";
409
- const frontmatter = layer.frontmatter && Object.keys(layer.frontmatter).length ? ` ${JSON.stringify(layer.frontmatter)}` : "";
410
- lines.push(` ${layer.tier}${who} ${layer.path}: ${layer.outcome}${how}${extendsNote}${selection}${frontmatter}`);
173
+ lines.push(` ${layer.tier}${who} ${layer.path}: ${layer.outcome}${how}`);
411
174
  }
412
175
  return `${lines.join("\n")}\n`;
413
176
  }
414
- /** A name not found, for a person: the name, why, and, when ambiguous, the options `reference select` takes. */
177
+ /** A name not found, for a person: the name, why, and, when ambiguous, the options `asset select` takes. */
415
178
  function errorText(entry) {
416
179
  const options = entry.options?.length ? `; options: ${entry.options.join(", ")}` : "";
417
180
  return `${entry.name}: ${entry.message}${options}`;
418
181
  }
419
182
  function showEntry(resolved, home, { metadata, trace }) {
420
183
  if (resolved.status === "found" && !resolved.warnings.length && !metadata && !trace && resolved.format === "markdown") return resolved.content;
421
- return compact({
184
+ return compact$1({
422
185
  name: resolved.name,
423
186
  status: resolved.status,
424
187
  content: resolved.content,
@@ -427,11 +190,11 @@ function showEntry(resolved, home, { metadata, trace }) {
427
190
  options: resolved.options,
428
191
  metadata: metadata ? resolved.metadata : void 0,
429
192
  warnings: resolved.warnings,
430
- trace: trace ? displayTrace(resolved.trace, home) : void 0
193
+ trace: trace ? displayTrace$1(resolved.trace, home) : void 0
431
194
  });
432
195
  }
433
196
  /** One entry per name asked, as the array itself rather than under a `result` key. */
434
- const bareArray = {
197
+ const bareArray$1 = {
435
198
  toon: (entries) => encode(entries, "toon"),
436
199
  json: (entries) => encode(entries, "json")
437
200
  };
@@ -443,9 +206,9 @@ function showText(resolved) {
443
206
  const blocks = resolved.map((entry) => entry.content === void 0 ? `<reference name="${entry.name}" status="${entry.status}" />` : `<reference name="${entry.name}">\n${entry.content}</reference>`);
444
207
  return document(blocks.join("\n\n"));
445
208
  }
446
- function showCommand$1(layersFor, name = "show") {
209
+ function loadCommand$1(layersFor) {
447
210
  return command({
448
- name,
211
+ name: "load",
449
212
  description: "Print one or more references, each composed from its contributions as their extends and merge declare.",
450
213
  arguments: [{
451
214
  name: "names",
@@ -453,13 +216,13 @@ function showCommand$1(layersFor, name = "show") {
453
216
  type: t.array(t.string())
454
217
  }],
455
218
  options: {
456
- root: rootOption$1,
457
- default: defaultOption,
219
+ root: rootOption$2,
220
+ default: defaultOption$1,
458
221
  metadata: {
459
222
  description: "Make every entry a record with its status and composed frontmatter.",
460
223
  type: t.boolean().optional()
461
224
  },
462
- trace: traceOption
225
+ trace: traceOption$1
463
226
  },
464
227
  async run(args) {
465
228
  const format = this.format;
@@ -468,24 +231,13 @@ function showCommand$1(layersFor, name = "show") {
468
231
  const home = homedir();
469
232
  const layers = await layersFor(args, home);
470
233
  const display = (path) => collapseHome(home, path);
471
- const lookup = interfaceLookup(layers, { display });
472
- const resolved = args.names.map((name) => {
473
- const entry = resolveReference(parseReferenceName(name), layers, { display });
474
- for (const failure of checkResolved(entry, lookup).failures) {
475
- if (failure.check === "compose") continue;
476
- entry.warnings.push({
477
- code: "interface-check",
478
- message: `it fails its interface check (${failure.check}): ${failure.message}`
479
- });
480
- }
481
- return entry;
482
- });
234
+ const resolved = args.names.map((name) => resolveAsset(parseAssetName(name), layers, { display }));
483
235
  const ok = resolved.every((entry) => entry.status === "found");
484
236
  if (format === "text") {
485
237
  for (const entry of resolved) {
486
- writeWarnings(entry.warnings);
238
+ writeWarnings$1(entry.warnings);
487
239
  if (entry.status !== "found") process.stderr.write(`error: ${entry.code}: ${errorText(entry)}\n`);
488
- if (args.trace) process.stderr.write(traceText(entry, home));
240
+ if (args.trace) process.stderr.write(traceText$1(entry, home));
489
241
  }
490
242
  const answer = showText(resolved);
491
243
  if (answer === void 0) throw CliError.reported(exitCodes.error);
@@ -495,7 +247,7 @@ function showCommand$1(layersFor, name = "show") {
495
247
  metadata: Boolean(args.metadata),
496
248
  trace: Boolean(args.trace)
497
249
  };
498
- const answer = result(resolved.map((entry) => showEntry(entry, home, flags)), { render: bareArray });
250
+ const answer = result(resolved.map((entry) => showEntry(entry, home, flags)), { render: bareArray$1 });
499
251
  return ok ? answer : answerThenFail(answer, format);
500
252
  } catch (error) {
501
253
  return fail$1(error, "Reference lookup failed.");
@@ -503,9 +255,9 @@ function showCommand$1(layersFor, name = "show") {
503
255
  }
504
256
  });
505
257
  }
506
- function listCommand$1(layersFor, name = "list") {
258
+ function namesCommand(layersFor) {
507
259
  return command({
508
- name,
260
+ name: "names",
509
261
  description: "List every reference name, one row each, with its description, and its status when not found. A pattern keeps only the names it matches exactly.",
510
262
  arguments: [{
511
263
  name: "pattern",
@@ -513,9 +265,9 @@ function listCommand$1(layersFor, name = "list") {
513
265
  type: t.string().optional()
514
266
  }],
515
267
  options: {
516
- root: rootOption$1,
268
+ root: rootOption$2,
517
269
  trace: {
518
- ...traceOption,
270
+ ...traceOption$1,
519
271
  description: "Add one row per name per layer holding it, with its outcome."
520
272
  }
521
273
  },
@@ -523,25 +275,21 @@ function listCommand$1(layersFor, name = "list") {
523
275
  try {
524
276
  const home = homedir();
525
277
  const display = (path) => collapseHome(home, path);
526
- const listing = listReferences(await layersFor(args, home), {
278
+ const listing = listAssetNames(await layersFor(args, home), {
527
279
  display,
528
280
  pattern: args.pattern
529
281
  });
530
- const report = compact({
531
- references: listing.references.length ? listing.references.map((row) => compact({
532
- name: row.name,
533
- description: row.description,
534
- format: row.format,
535
- status: row.status === "found" ? void 0 : row.status,
536
- options: row.options
537
- })) : args.pattern ? `0 references match "${args.pattern}"` : "0 references — no layer holds one",
538
- defaults: listing.defaults
539
- });
540
- if (args.trace) report.layers = listing.layers.map((row) => compact({
282
+ const report = compact$1({ references: listing.references.length ? listing.references.map((row) => compact$1({
283
+ name: row.name,
284
+ format: row.format,
285
+ status: row.status === "found" ? void 0 : row.status,
286
+ options: row.options
287
+ })) : args.pattern ? `0 references match "${args.pattern}"` : "0 references — no layer holds one" });
288
+ if (args.trace) report["layers"] = listing.layers.map((row) => compact$1({
541
289
  ...row,
542
290
  path: display(row.path)
543
291
  }));
544
- if (listing.warnings.length) report.warnings = listing.warnings;
292
+ if (listing.warnings.length) report["warnings"] = listing.warnings;
545
293
  return result(report);
546
294
  } catch (error) {
547
295
  return fail$1(error, "Reference listing failed.");
@@ -549,55 +297,9 @@ function listCommand$1(layersFor, name = "list") {
549
297
  }
550
298
  });
551
299
  }
552
- function searchCommand(layersFor) {
553
- return command({
554
- name: "search",
555
- description: "Find references by name, description, tags, headings, or body, best match first.",
556
- arguments: [{
557
- name: "query",
558
- description: "What the reference is about.",
559
- type: t.string()
560
- }],
561
- options: {
562
- root: rootOption$1,
563
- trace: {
564
- ...traceOption,
565
- description: "Add the tier and plugin or skill of each match."
566
- }
567
- },
568
- async run(args) {
569
- const query = args.query.trim();
570
- if (!query) usage$1("search needs a query.");
571
- try {
572
- const home = homedir();
573
- const { matches, defaults } = searchReferences(query, await layersFor(args, home), { display: (path) => collapseHome(home, path) });
574
- const report = compact({
575
- references: matches.length ? matches.map(({ name, description, match, tier, plugin, skill, loadedBy }) => compact(args.trace ? {
576
- name,
577
- description,
578
- match,
579
- loadedBy,
580
- tier,
581
- plugin,
582
- skill
583
- } : {
584
- name,
585
- description,
586
- match,
587
- loadedBy
588
- })) : `0 references match "${query}"`,
589
- defaults
590
- });
591
- return result(report);
592
- } catch (error) {
593
- return fail$1(error, "Reference search failed.");
594
- }
595
- }
596
- });
597
- }
598
- function whereCommand(layersFor, assets = false, name = "where") {
300
+ function whereCommand$1(layersFor) {
599
301
  return command({
600
- name,
302
+ name: "where",
601
303
  description: "Give the project and user files a customization or a selection of a reference is written to, who each reaches, and the heading paths of the document below each.",
602
304
  arguments: [{
603
305
  name: "name",
@@ -605,10 +307,10 @@ function whereCommand(layersFor, assets = false, name = "where") {
605
307
  type: t.string()
606
308
  }],
607
309
  options: {
608
- root: rootOption$1,
609
- default: defaultOption,
310
+ root: rootOption$2,
311
+ default: defaultOption$1,
610
312
  trace: {
611
- ...traceOption,
313
+ ...traceOption$1,
612
314
  description: "Add every layer checked, with its outcome."
613
315
  }
614
316
  },
@@ -617,15 +319,14 @@ function whereCommand(layersFor, assets = false, name = "where") {
617
319
  try {
618
320
  const home = homedir();
619
321
  const display = (path) => collapseHome(home, path);
620
- const report = whereReference(args.name, await layersFor(args, home), {
322
+ const report = whereAsset(args.name, await layersFor(args, home), {
621
323
  root: resolve(args.root ?? process.cwd()),
622
- display,
623
- assets
324
+ display
624
325
  });
625
- const output = compact({
326
+ const output = compact$1({
626
327
  name: report.name,
627
328
  status: report.status,
628
- places: report.places.map((place) => compact(place)),
329
+ places: report.places.map((place) => compact$1(place)),
629
330
  options: report.options,
630
331
  layers: args.trace ? report.layers.map((layer) => ({
631
332
  ...layer,
@@ -633,10 +334,10 @@ function whereCommand(layersFor, assets = false, name = "where") {
633
334
  })) : void 0,
634
335
  warnings: report.warnings
635
336
  });
636
- if (format === "text") writeWarnings(report.warnings);
337
+ if (format === "text") writeWarnings$1(report.warnings);
637
338
  const answer = result(output);
638
339
  if (report.status !== "invalid") return answer;
639
- process.stderr.write(`error: reference-invalid: ${parseReferenceName(args.name).invalid}\n`);
340
+ process.stderr.write(`error: reference-invalid: ${parseAssetName(args.name).invalid}\n`);
640
341
  return answerThenFail(answer, format);
641
342
  } catch (error) {
642
343
  return fail$1(error, "Reference placement lookup failed.");
@@ -644,384 +345,33 @@ function whereCommand(layersFor, assets = false, name = "where") {
644
345
  }
645
346
  });
646
347
  }
647
- function parseAccept(value) {
648
- if (value === void 0) return void 0;
649
- const versions = value.split(",").map((part) => part.trim());
650
- if (!versions.length || versions.some((part) => !/^[1-9]\d*$/.test(part))) usage$1(`--accept is a comma-separated list of versions, such as 1,2; got "${value}".`);
651
- return versions.map(Number);
348
+ /** The tiered, by-name commands over `assets/` folders. `asset`'s own commands act on a path. */
349
+ function createAssetResolveCommands({ plugin } = {}) {
350
+ const layersFor = layerSource$1(plugin);
351
+ return {
352
+ load: loadCommand$1(layersFor),
353
+ names: namesCommand(layersFor),
354
+ where: whereCommand$1(layersFor)
355
+ };
652
356
  }
653
- function implementationsCommand(layersFor) {
654
- return command({
655
- name: "implementations",
656
- description: "List the implementations of an interface a consumer can load, each with its pin, description, and unsupported sections.",
657
- arguments: [{
658
- name: "id",
659
- description: "Interface id, the name of its `<id>.interface` document without the suffix.",
660
- type: t.string()
661
- }],
662
- options: {
663
- root: rootOption$1,
664
- accept: {
665
- description: "Versions the consumer knows, comma-separated, such as 1,2. Defaults to the interface's `versions`.",
666
- type: t.string().optional()
667
- },
668
- trace: {
669
- ...traceOption,
670
- description: "Add each one's name, tier, and version, the interface's cardinality and accepted versions, and the ones left out with why."
671
- }
672
- },
673
- async run(args) {
674
- const accept = parseAccept(args.accept);
675
- try {
676
- const home = homedir();
677
- const found = referenceImplementations(args.id, await layersFor(args, home), {
678
- accept,
679
- display: (path) => collapseHome(home, path)
680
- });
681
- const rows = found.implementations.map(({ pin, description, unsupported, name, tier, version }) => compact(args.trace ? {
682
- pin,
683
- description,
684
- unsupported,
685
- name,
686
- tier,
687
- version
688
- } : {
689
- pin,
690
- description,
691
- unsupported
692
- }));
693
- const report = { implementations: rows.length ? rows : `0 implementations of ${args.id}` };
694
- if (args.trace) Object.assign(report, compact({
695
- interface: found.interface,
696
- cardinality: found.cardinality,
697
- accept: found.accept,
698
- excluded: found.excluded,
699
- warnings: found.warnings
700
- }));
701
- return result(report);
702
- } catch (error) {
703
- return fail$1(error, "Implementation lookup failed.");
704
- }
705
- }
706
- });
707
- }
708
- function checkCommand(layersFor) {
709
- return command({
710
- name: "check",
711
- description: "Check interface documents and their implementations, each composed as `show` composes it. With no names, checks every one found. Exits 1 on any failure.",
712
- arguments: [{
713
- name: "names",
714
- description: "Reference names, without the `.md` extension; `<plugin>/<name>` builds that plugin’s own stack.",
715
- type: t.array(t.string()).optional()
716
- }],
717
- options: {
718
- root: rootOption$1,
719
- trace: {
720
- ...traceOption,
721
- description: "Add every entry checked, with its kind and unsupported sections."
722
- }
723
- },
724
- async run(args) {
725
- const format = this.format;
726
- try {
727
- const home = homedir();
728
- const entries = checkReferences(await layersFor(args, home), {
729
- names: args.names ?? [],
730
- display: (path) => collapseHome(home, path)
731
- });
732
- const failing = entries.filter((entry) => entry.status !== "passed");
733
- const strip = (failures) => failures.map(({ check, message }) => ({
734
- check,
735
- message
736
- }));
737
- const report = compact({
738
- failed: failing.map(({ name, loadedBy, failures }) => compact({
739
- name,
740
- loadedBy,
741
- failures: strip(failures)
742
- })),
743
- passed: entries.length - failing.length,
744
- references: args.trace ? entries.map(({ name, loadedBy, kind, status, failures, unsupported }) => compact({
745
- name,
746
- loadedBy,
747
- kind,
748
- status,
749
- failures: strip(failures),
750
- unsupported
751
- })) : void 0
752
- });
753
- const answer = result(report);
754
- if (!failing.length) return answer;
755
- if (format === "text") process.stderr.write(`error: interface-check: ${failing.length} of ${entries.length} references fail their check.\n`);
756
- return answerThenFail(answer, format);
757
- } catch (error) {
758
- return fail$1(error, "Reference check failed.");
759
- }
760
- }
761
- });
762
- }
763
- const scopeOption = {
764
- description: "Tier to write: project (default), `.agents/references/` at the root, or user, `~/.agents/references/`.",
765
- type: t.string().optional()
766
- };
767
- const pluginOption = {
768
- description: "A plugin's source folder in this repository: acts on its `references/<name>.md`.",
769
- type: t.string().optional()
770
- };
771
- const skillOption$1 = {
772
- description: "A skill's source folder in this repository: acts on its `references/<name>.default.md`.",
773
- type: t.string().optional()
774
- };
775
- function createCommand(layersFor) {
776
- return command({
777
- name: "create",
778
- description: "Start a contribution in the project or user tier, or in a plugin's or a skill's source folder, from a template or an interface. Never overwrites a file with a body.",
779
- arguments: [{
780
- name: "name",
781
- description: "Reference name, without the `.md` extension.",
782
- type: t.string()
783
- }],
784
- options: {
785
- root: rootOption$1,
786
- scope: scopeOption,
787
- plugin: pluginOption,
788
- default: skillOption$1,
789
- merge: {
790
- description: "How it applies over what is below: replace, patch (default), combine, or remove. remove writes no body.",
791
- type: t.string().optional()
792
- },
793
- template: {
794
- description: "File whose text is written as it is, frontmatter included. Defaults to a built-in template of top-level `##` sections.",
795
- type: t.string().optional()
796
- },
797
- implements: {
798
- description: "Interface id to implement: the interface's sections become `##` headings, each stubbed `**Unsupported.**`, with `merge: replace`. Not with --template.",
799
- type: t.string().optional()
800
- },
801
- "dry-run": {
802
- description: "Print the target path and the exact content, and write nothing.",
803
- type: t.boolean().optional()
804
- },
805
- trace: {
806
- ...traceOption,
807
- description: "After the write, add the trace, in which the new file applies."
808
- }
809
- },
810
- async run(args) {
811
- const format = this.format;
812
- try {
813
- const home = homedir();
814
- const root = resolve(args.root ?? process.cwd());
815
- const display = (path) => collapseHome(home, path);
816
- const created = createContribution(await layersFor({
817
- root,
818
- default: args.default
819
- }, home), {
820
- root,
821
- home,
822
- name: args.name,
823
- scope: args.scope,
824
- plugin: args.plugin,
825
- default: args.default,
826
- merge: args.merge,
827
- template: args.template,
828
- implements: args.implements,
829
- dryRun: args["dry-run"]
830
- }, { display });
831
- const report = compact({
832
- path: display(created.path),
833
- hint: created.dryRun ? "Nothing was written. Run again without --dry-run to write it." : `Run \`show ${created.name}\` for the composed document.`,
834
- content: created.dryRun ? created.content : void 0,
835
- hints: created.hints,
836
- trace: args.trace && created.after ? displayTrace(created.after.trace, home) : void 0
837
- });
838
- if (format !== "text") return result(report);
839
- for (const hint of created.hints) process.stderr.write(`hint: ${hint}\n`);
840
- if (args.trace && created.after) process.stderr.write(traceText(created.after, home));
841
- return document(created.dryRun ? `${report.path}\n\n${created.content}` : `${report.path}\n\n${report.hint}\n`);
842
- } catch (error) {
843
- return fail$1(error, "Reference creation failed.");
844
- }
845
- }
846
- });
847
- }
848
- function selectCommand$1(layersFor) {
849
- return command({
850
- name: "select",
851
- description: "Choose what a name builds on: write an `extends` listing the options, bottom first, in the project or user file. Its body and other keys are kept.",
852
- arguments: [{
853
- name: "name",
854
- description: "Reference name, without the `.md` extension.",
855
- type: t.string()
856
- }, {
857
- name: "options",
858
- description: "Bottom first: each a plugin holding the name (or `<plugin>/<name>`), or `default`.",
859
- type: t.array(t.string()).optional()
860
- }],
861
- options: {
862
- root: rootOption$1,
863
- scope: scopeOption,
864
- clear: {
865
- description: "Remove the selection; a file left with nothing in it is deleted.",
866
- type: t.boolean().optional()
867
- }
868
- },
869
- async run(args) {
870
- const format = this.format;
871
- try {
872
- const home = homedir();
873
- const root = resolve(args.root ?? process.cwd());
874
- const display = (path) => collapseHome(home, path);
875
- const selected = selectContribution(await layersFor({ root }, home), {
876
- root,
877
- home,
878
- name: args.name,
879
- options: args.options ?? [],
880
- scope: args.scope,
881
- clear: args.clear
882
- }, { display });
883
- const report = compact({
884
- path: display(selected.path),
885
- extends: selected.extends,
886
- deleted: selected.deleted || void 0,
887
- status: selected.after.status,
888
- stack: selected.after.trace.stack,
889
- answer: describeAnswer(selected.after, display)
890
- });
891
- if (format !== "text") return result(report);
892
- writeWarnings(selected.after.warnings);
893
- const what = args.clear ? selected.deleted ? "selection cleared; the file was left empty and deleted" : "selection cleared" : `extends: [${selected.extends.join(", ")}]`;
894
- return document(`${report.path}\n\n${what}. ${report.answer}\n`);
895
- } catch (error) {
896
- return fail$1(error, "Reference selection failed.");
897
- }
898
- }
899
- });
900
- }
901
- function deleteReport(deleted, home) {
902
- const selection = deleted.selection ? `the selection extends: [${deleted.selection.join(", ")}] went with it; run \`reference select ${deleted.name} <option>...\` to restore it` : void 0;
903
- return compact({
904
- path: collapseHome(home, deleted.path),
905
- dryRun: deleted.dryRun || void 0,
906
- status: deleted.after.status,
907
- stack: deleted.after.trace.stack,
908
- answer: describeAnswer(deleted.after, (path) => collapseHome(home, path)),
909
- selection,
910
- options: deleted.after.options
911
- });
912
- }
913
- function deleteCommand$1(layersFor) {
914
- return command({
915
- name: "delete",
916
- description: "Delete a project or user contribution, or one in a plugin's or a skill's source folder, and report what answers the name afterwards. Never deletes an installed or managed one.",
917
- arguments: [{
918
- name: "name",
919
- description: "Reference name, without the `.md` extension.",
920
- type: t.string()
921
- }],
922
- options: {
923
- root: rootOption$1,
924
- scope: {
925
- ...scopeOption,
926
- description: "Tier to delete from: project (default) or user."
927
- },
928
- plugin: pluginOption,
929
- default: skillOption$1,
930
- "dry-run": {
931
- description: "Print the file that would be deleted and what would answer the name then, and delete nothing.",
932
- type: t.boolean().optional()
933
- }
934
- },
935
- async run(args) {
936
- const format = this.format;
937
- try {
938
- const home = homedir();
939
- const root = resolve(args.root ?? process.cwd());
940
- const deleted = deleteContribution(await layersFor({
941
- root,
942
- default: args.default
943
- }, home), {
944
- root,
945
- home,
946
- name: args.name,
947
- scope: args.scope,
948
- plugin: args.plugin,
949
- default: args.default,
950
- dryRun: args["dry-run"]
951
- }, { display: (path) => collapseHome(home, path) });
952
- const report = deleteReport(deleted, home);
953
- if (format !== "text") return result(report);
954
- const selection = report.selection ? ` Also, ${report.selection}.` : "";
955
- return document(`${report.path}\n\n${deleted.dryRun ? "would delete" : "deleted"}. ${report.answer}${selection}\n`);
956
- } catch (error) {
957
- return fail$1(error, "Reference deletion failed.");
958
- }
959
- }
960
- });
961
- }
962
- /** A factory, so each host CLI names itself as the plugin whose own references come first. */
963
- function createReferenceCommands({ plugin } = {}) {
964
- const layersFor = layerSource(plugin);
965
- return {
966
- show: showCommand$1(layersFor),
967
- list: listCommand$1(layersFor),
968
- search: searchCommand(layersFor),
969
- where: whereCommand(layersFor),
970
- create: createCommand(layersFor),
971
- select: selectCommand$1(layersFor),
972
- delete: deleteCommand$1(layersFor),
973
- check: checkCommand(layersFor),
974
- implementations: implementationsCommand(layersFor)
975
- };
976
- }
977
- function createReferenceCommand(options = {}) {
978
- const { show, list, search, where, create, select, delete: remove, check, implementations } = createReferenceCommands(options);
979
- return command({
980
- name: "reference",
981
- description: "Read reference documents by name, composed from the managed, project, user, plugin, and default tiers, and write, select, or delete a contribution.",
982
- commands: [
983
- show,
984
- list,
985
- search,
986
- where,
987
- create,
988
- select,
989
- remove,
990
- implementations,
991
- check
992
- ]
993
- });
994
- }
995
- /**
996
- * The tiered, by-name commands over `assets/` folders: the reference engine reading `assets/` where it
997
- * reads `references/`. `load` is `show`, `names` is `list`. `asset`'s own commands act on a path.
998
- */
999
- function createAssetResolveCommands({ plugin } = {}) {
1000
- const layersFor = layerSource(plugin, "assets");
1001
- return {
1002
- load: showCommand$1(layersFor, "load"),
1003
- names: listCommand$1(layersFor, "names"),
1004
- where: whereCommand(layersFor, true)
1005
- };
1006
- }
1007
- //#endregion
1008
- //#region src/assets/skill-assets.ts
1009
- /** A refusal. `usage` marks a wrong invocation (exit 2) rather than a refusal (exit 1). */
1010
- var AssetError = class extends Error {
1011
- code;
1012
- usage;
1013
- constructor(code, message, usage = false) {
1014
- super(message);
1015
- this.code = code;
1016
- this.usage = usage;
1017
- }
1018
- };
1019
- /** The skill's `assets/` folder, for a skill folder that holds a `SKILL.md`. */
1020
- function assetsDir(skill, root, write) {
1021
- const folder = resolve(skill);
1022
- if (skillName(folder) === void 0) throw new AssetError("folder-invalid", `${folder} holds no SKILL.md, so it is not a skill folder.`);
1023
- if (write && !insideRepository(folder, root)) throw new AssetError("folder-invalid", `${folder} is outside the repository, such as an installed copy; write to the skill's source instead.`);
1024
- return join(folder, "assets");
357
+ //#endregion
358
+ //#region src/assets/skill-assets.ts
359
+ /** A refusal. `usage` marks a wrong invocation (exit 2) rather than a refusal (exit 1). */
360
+ var AssetError = class extends Error {
361
+ code;
362
+ usage;
363
+ constructor(code, message, usage = false) {
364
+ super(message);
365
+ this.code = code;
366
+ this.usage = usage;
367
+ }
368
+ };
369
+ /** The skill's `assets/` folder, for a skill folder that holds a `SKILL.md`. */
370
+ function assetsDir(skill, root, write) {
371
+ const folder = resolve(skill);
372
+ if (skillName(folder) === void 0) throw new AssetError("folder-invalid", `${folder} holds no SKILL.md, so it is not a skill folder.`);
373
+ if (write && !insideRepository(folder, root)) throw new AssetError("folder-invalid", `${folder} is outside the repository, such as an installed copy; write to the skill's source instead.`);
374
+ return join(folder, "assets");
1025
375
  }
1026
376
  /** An asset path: relative, `/`-separated, with no `..` or empty part. */
1027
377
  function assetFile(dir, path) {
@@ -1107,21 +457,20 @@ function deleteAsset(skill, root, path) {
1107
457
  * were in effect (the managed file would still win).
1108
458
  */
1109
459
  function copyAsset(layers, args) {
1110
- const name = parseReferenceName(args.name);
460
+ const name = parseAssetName(args.name);
1111
461
  if (name.invalid || name.plugin !== void 0) throw new AssetError("name-invalid", name.invalid ?? "create takes a bare name.", true);
1112
462
  if (args.scope !== "project" && args.scope !== "user") throw new AssetError("options-invalid", "--scope is project or user.", true);
1113
463
  if (args.copy === "managed") throw new AssetError("managed-copy-refused", "the managed tier is policy and is never copied into a project or user folder. Ask the administrator, or read it with \"asset load\".");
1114
- const found = resolveReference(name.name, layers).trace.layers.filter(({ file }) => file);
464
+ const found = resolveAsset(name.name, layers).trace.layers.filter(({ file }) => file);
1115
465
  const source = found.find((layer) => args.copy === "default" || args.copy === "project" || args.copy === "user" ? layer.tier === args.copy : layer.tier === "plugin" && layer.plugin === args.copy);
1116
466
  if (!source) {
1117
467
  const held = found.map((layer) => layer.tier === "plugin" ? layer.plugin : layer.tier);
1118
468
  throw new AssetError("no-source", `"${args.copy}" holds no asset "${name.name}". Held by: ${held.filter((tier) => tier !== "managed").join(", ") || "nothing"}.`);
1119
469
  }
1120
470
  const extension = extname(source.path);
1121
- if (source.file && ![...dataExtensions.map((item) => item.extension), ".md"].includes(extension)) throw new AssetError("no-source", `${basename(source.path)} is not a file an asset name resolves to.`);
1122
- const base = args.scope === "user" ? userReferencesDir(args.home) : projectReferencesDir(resolve(args.root));
1123
- const dir = join(dirname(base), "assets");
1124
- const taken = [".md", ...dataExtensions.map((item) => item.extension)].map((item) => join(dir, `${name.name}${item}`)).find((path) => existsSync(path));
471
+ if (source.file && ![...assetDataExtensions.map((item) => item.extension), ".md"].includes(extension)) throw new AssetError("no-source", `${basename(source.path)} is not a file an asset name resolves to.`);
472
+ const dir = args.scope === "user" ? userAssetsDir(args.home) : projectAssetsDir(resolve(args.root));
473
+ const taken = [".md", ...assetDataExtensions.map((item) => item.extension)].map((item) => join(dir, `${name.name}${item}`)).find((path) => existsSync(path));
1125
474
  if (taken) throw new AssetError("exists", `${taken} already exists; edit it, or delete it first.`);
1126
475
  const path = join(dir, `${name.name}${extension}`);
1127
476
  mkdirSync(dir, { recursive: true });
@@ -1136,7 +485,7 @@ function copyAsset(layers, args) {
1136
485
  //#region src/assets/select-asset.ts
1137
486
  /** The options a name can be selected between: each plugin holding it, and `default` when the skill's file exists. */
1138
487
  function options(layers, name) {
1139
- const found = resolveReference(name, layers).trace.layers;
488
+ const found = resolveAsset(name, layers).trace.layers;
1140
489
  return [...found.filter(({ tier, file }) => tier === "plugin" && file).map(({ plugin }) => plugin), ...found.some(({ tier, file }) => tier === "default" && file) ? ["default"] : []];
1141
490
  }
1142
491
  /**
@@ -1144,11 +493,11 @@ function options(layers, name) {
1144
493
  * answers the name. Nothing else is in the file, and nothing extends or merges.
1145
494
  */
1146
495
  function selectAsset(layers, args) {
1147
- const name = parseReferenceName(args.name);
496
+ const name = parseAssetName(args.name);
1148
497
  if (name.invalid || name.plugin !== void 0) throw new AssetError("name-invalid", name.invalid ?? "select takes a bare name.", true);
1149
498
  if (args.scope !== void 0 && args.scope !== "project" && args.scope !== "user") throw new AssetError("options-invalid", "--scope is project or user.", true);
1150
- const base = args.scope === "user" ? userReferencesDir(args.home) : projectReferencesDir(resolve(args.root));
1151
- const path = join(dirname(base), "assets", `${name.name}.select`);
499
+ const dir = args.scope === "user" ? userAssetsDir(args.home) : projectAssetsDir(resolve(args.root));
500
+ const path = join(dir, `${name.name}.select`);
1152
501
  if (args.clear) {
1153
502
  if (!existsSync(path)) throw new AssetError("no-file", `${path} does not exist, so there is no selection to clear.`);
1154
503
  rmSync(path);
@@ -1161,7 +510,7 @@ function selectAsset(layers, args) {
1161
510
  const result = {
1162
511
  path,
1163
512
  cleared: Boolean(args.clear),
1164
- after: resolveReference(name.name, layers, { without: args.clear ? path : void 0 })
513
+ after: resolveAsset(name.name, layers, { without: args.clear ? path : void 0 })
1165
514
  };
1166
515
  return args.clear ? result : {
1167
516
  ...result,
@@ -1170,11 +519,11 @@ function selectAsset(layers, args) {
1170
519
  }
1171
520
  //#endregion
1172
521
  //#region src/assets/asset.command.ts
1173
- const skillOption = {
522
+ const skillOption$1 = {
1174
523
  description: "A skill's source folder in this repository: its `assets/` folder is acted on.",
1175
524
  type: t.string()
1176
525
  };
1177
- const rootOption = {
526
+ const rootOption$1 = {
1178
527
  description: "The directory the repository is found from. Defaults to the current directory.",
1179
528
  type: t.string().optional()
1180
529
  };
@@ -1195,13 +544,13 @@ const pathDescription = "Asset path inside `assets/`, such as `data/rules.json`.
1195
544
  function rootOf(args) {
1196
545
  return resolve(args.root ?? process.cwd());
1197
546
  }
1198
- function listCommand() {
547
+ function listCommand$1() {
1199
548
  return command({
1200
549
  name: "list",
1201
550
  description: "List every file in a skill's assets/ folder with its size.",
1202
551
  options: {
1203
- skill: skillOption,
1204
- root: rootOption
552
+ skill: skillOption$1,
553
+ root: rootOption$1
1205
554
  },
1206
555
  async run(args) {
1207
556
  try {
@@ -1223,8 +572,8 @@ function showCommand() {
1223
572
  type: t.string()
1224
573
  }],
1225
574
  options: {
1226
- skill: skillOption,
1227
- root: rootOption
575
+ skill: skillOption$1,
576
+ root: rootOption$1
1228
577
  },
1229
578
  async run(args) {
1230
579
  try {
@@ -1247,10 +596,10 @@ function writeCommand(mode, host) {
1247
596
  }],
1248
597
  options: {
1249
598
  skill: {
1250
- ...skillOption,
599
+ ...skillOption$1,
1251
600
  type: t.string().optional()
1252
601
  },
1253
- root: rootOption,
602
+ root: rootOption$1,
1254
603
  content: {
1255
604
  description: "The text to write.",
1256
605
  type: t.string().optional()
@@ -1280,14 +629,13 @@ function writeCommand(mode, host) {
1280
629
  if (args.content !== void 0 || args.from !== void 0) usage("--copy takes neither --content nor --from.");
1281
630
  const home = homedir();
1282
631
  const root = rootOf(args);
1283
- const done = copyAsset(await referenceLayers({
632
+ const done = copyAsset(await assetLayers({
1284
633
  root,
1285
634
  home,
1286
635
  platform: process.platform,
1287
636
  env: process.env,
1288
637
  plugin: host.plugin,
1289
- defaultSkill: args.default ?? args.skill,
1290
- folder: "assets"
638
+ defaultSkill: args.default ?? args.skill
1291
639
  }), {
1292
640
  root,
1293
641
  home,
@@ -1308,7 +656,7 @@ function writeCommand(mode, host) {
1308
656
  }
1309
657
  });
1310
658
  }
1311
- function deleteCommand() {
659
+ function deleteCommand$1() {
1312
660
  return command({
1313
661
  name: "delete",
1314
662
  description: "Delete an asset from a skill's source folder, and any folder it leaves empty.",
@@ -1318,8 +666,8 @@ function deleteCommand() {
1318
666
  type: t.string()
1319
667
  }],
1320
668
  options: {
1321
- skill: skillOption,
1322
- root: rootOption
669
+ skill: skillOption$1,
670
+ root: rootOption$1
1323
671
  },
1324
672
  async run(args) {
1325
673
  try {
@@ -1344,7 +692,7 @@ function selectCommand(options) {
1344
692
  type: t.string().optional()
1345
693
  }],
1346
694
  options: {
1347
- root: rootOption,
695
+ root: rootOption$1,
1348
696
  default: {
1349
697
  description: "The calling skill's folder: its `assets/<name>.<ext>` is the `default` option.",
1350
698
  type: t.string().optional()
@@ -1362,14 +710,13 @@ function selectCommand(options) {
1362
710
  try {
1363
711
  const home = homedir();
1364
712
  const root = resolve(args.root ?? process.cwd());
1365
- const done = selectAsset(await referenceLayers({
713
+ const done = selectAsset(await assetLayers({
1366
714
  root,
1367
715
  home,
1368
716
  platform: process.platform,
1369
717
  env: process.env,
1370
718
  plugin: options.plugin,
1371
- defaultSkill: args.default,
1372
- folder: "assets"
719
+ defaultSkill: args.default
1373
720
  }), {
1374
721
  ...args,
1375
722
  root,
@@ -1389,11 +736,11 @@ function createAssetCommand(options = {}) {
1389
736
  name: "asset",
1390
737
  description: "Manage a skill's assets/ files by path (list, show, create, update, delete), and load one by name through the tiers (load, names, where, select).",
1391
738
  commands: [
1392
- listCommand(),
739
+ listCommand$1(),
1393
740
  showCommand(),
1394
741
  writeCommand("create", options),
1395
742
  writeCommand("update", options),
1396
- deleteCommand(),
743
+ deleteCommand$1(),
1397
744
  load,
1398
745
  names,
1399
746
  where,
@@ -1402,4 +749,918 @@ function createAssetCommand(options = {}) {
1402
749
  });
1403
750
  }
1404
751
  //#endregion
752
+ //#region src/references/reference-command-support.ts
753
+ const rootOption = {
754
+ description: "The repository root: its `.agents/references/` is the repo tier, and its `package.json` names the dependency plugins. Defaults to the nearest folder at or above the current directory holding `.git`, else the current directory.",
755
+ type: t.string().optional()
756
+ };
757
+ const traceOption = {
758
+ description: "Add the trace: every layer checked, with its outcome, and how the result was built.",
759
+ type: t.boolean().optional()
760
+ };
761
+ const defaultOption = {
762
+ description: "The calling skill's folder: its `references/<name>.default.md` joins as the `default` tier.",
763
+ type: t.string().optional()
764
+ };
765
+ function layerSource(plugin) {
766
+ return (args, home) => referenceLayers({
767
+ root: args.root,
768
+ cwd: process.cwd(),
769
+ home,
770
+ platform: process.platform,
771
+ env: process.env,
772
+ plugin,
773
+ defaultSkill: args.default
774
+ });
775
+ }
776
+ /** A field with nothing to say is left out: `undefined`, an empty string, or an empty list. */
777
+ function compact(value) {
778
+ return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== void 0 && field !== "" && !(Array.isArray(field) && !field.length)));
779
+ }
780
+ /**
781
+ * A thrown failure as the one axi error value the cli prints on stdout: a `ReferenceFailure` keeps its
782
+ * code and exits 1, or 2 for a usage error; a `CliError` passes through.
783
+ */
784
+ function asCliError(error, fallback) {
785
+ if (isCliError(error)) throw error;
786
+ if (error instanceof ReferenceFailure) throw new CliError(error.message, {
787
+ code: error.code,
788
+ exitCode: error.usage ? exitCodes.usage : exitCodes.error,
789
+ details: error.details,
790
+ help: error.help
791
+ });
792
+ throw new CliError(error instanceof Error ? error.message : fallback, { cause: error });
793
+ }
794
+ function writeWarnings(warnings) {
795
+ for (const { code, message } of warnings) process.stderr.write(`warning: ${code}: ${message}\n`);
796
+ }
797
+ function displayTrace(trace, home) {
798
+ const show = (path) => collapseHome(home, path);
799
+ return {
800
+ ...trace,
801
+ contributions: trace.contributions.map((row) => ({
802
+ ...row,
803
+ path: show(row.path)
804
+ })),
805
+ layers: trace.layers.map((layer) => ({
806
+ ...layer,
807
+ path: show(layer.path)
808
+ })),
809
+ projectSections: trace.projectSections.map((row) => ({
810
+ ...row,
811
+ path: show(row.path)
812
+ }))
813
+ };
814
+ }
815
+ /** A generated document's record in the text trace: each source, recorded and installed, then the request (R46). */
816
+ function recordLines(row, indent) {
817
+ const lines = (row.sources ?? []).map((source) => {
818
+ return `${indent}source ${source.default !== void 0 ? `default ${source.default}${source.plugin ? ` (plugin ${source.plugin})` : ""}` : `plugin ${source.plugin}`}: recorded ${source.recorded ?? "no version"}, installed ${source.status === "missing" ? "not installed" : source.installed ?? "no version"} (${source.status})`;
819
+ });
820
+ if (row.request !== void 0) lines.push(`${indent}request:`, ...row.request.replace(/\n$/, "").split("\n").map((line) => `${indent} ${line}`));
821
+ return lines;
822
+ }
823
+ /** The text trace: a summary line, then each section with its contributions, then each layer. */
824
+ function traceText(resolved, home) {
825
+ const trace = displayTrace(resolved.trace, home);
826
+ const lines = [`${resolved.name}: ${resolved.status}; stack: ${trace.stack.join(" → ") || "none"}; built by ${trace.builtBy}; harnesses: ${trace.harnesses.join(", ") || "none detected"}`];
827
+ if (resolved.message) lines.push(` ${resolved.message}`);
828
+ if (trace.sections.length) {
829
+ lines.push("sections:");
830
+ for (const section of trace.sections) {
831
+ const how = section.removedBy ? `removed by ${section.removedBy}` : section.contributions.map(({ contribution, operation }) => `${contribution} (${operation})`).join(", ") || "no text";
832
+ lines.push(` ${section.path}: ${how}`);
833
+ }
834
+ }
835
+ lines.push("layers:");
836
+ for (const layer of trace.layers) {
837
+ const who = layer.plugin ? ` ${layer.plugin}` : layer.skill ? ` ${layer.skill}` : "";
838
+ const how = layer.operation ? `; ${layer.operation} from ${layer.operationFrom}` : "";
839
+ const extendsNote = layer.extendsOutcome ? `; extends ${layer.extendsOutcome}` : "";
840
+ const selection = layer.selection ? "; selection" : "";
841
+ const frontmatter = layer.frontmatter && Object.keys(layer.frontmatter).length ? ` ${JSON.stringify(layer.frontmatter)}` : "";
842
+ lines.push(` ${layer.tier}${who} ${layer.path}: ${layer.outcome}${how}${extendsNote}${selection}${frontmatter}`);
843
+ lines.push(...recordLines(layer, " "));
844
+ }
845
+ if (trace.projectSections.length) {
846
+ lines.push("project sections:");
847
+ for (const row of trace.projectSections) {
848
+ const frontmatter = row.frontmatter && Object.keys(row.frontmatter).length ? ` ${JSON.stringify(row.frontmatter)}` : "";
849
+ const from = row.plugin === void 0 ? "" : ` (plugin ${row.plugin}${row.version ? `@${row.version}` : ""})`;
850
+ lines.push(` ${row.folder}${from} ${row.path}: ${row.outcome}${frontmatter}`);
851
+ lines.push(...recordLines(row, " "));
852
+ for (const section of row.sections ?? []) {
853
+ const how = section.contributions.map(({ contribution, operation }) => `${contribution} (${operation})`).join(", ");
854
+ lines.push(` ${section.path}: ${how || "no text"}`);
855
+ }
856
+ }
857
+ }
858
+ return `${lines.join("\n")}\n`;
859
+ }
860
+ function escapeText(value) {
861
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
862
+ }
863
+ function escapeAttribute(value) {
864
+ return escapeText(value).replace(/"/g, "&quot;");
865
+ }
866
+ /** A name not `found`, in text: its status and code, holding its message, and its options when ambiguous. */
867
+ function notFoundTag(entry) {
868
+ const options = entry.options?.length ? `; options: ${entry.options.join(", ")}` : "";
869
+ return `<reference name="${escapeAttribute(entry.name)}" status="${entry.status}" code="${entry.code}">${escapeText(`${entry.message ?? entry.status}${options}`)}</reference>`;
870
+ }
871
+ /**
872
+ * A found name's text: the main document, then each project section wrapped with its folder, in path
873
+ * order, keeping its own heading levels (R44). A package plugin's section also names the plugin, so
874
+ * the agent can put the folder's own section over it (R90).
875
+ */
876
+ function deliveredText(entry) {
877
+ const content = entry.content ?? "";
878
+ if (!entry.projectSections.length) return content;
879
+ return `${[...content.trim() ? [content.trimEnd()] : [], ...entry.projectSections.map(({ folder, plugin, content: text }) => `<project folder="${escapeAttribute(folder)}"${plugin === void 0 ? "" : ` plugin="${escapeAttribute(plugin)}"`}>\n${text}</project>`)].join("\n\n")}\n`;
880
+ }
881
+ /** A found name among several, in text. */
882
+ function referenceTag(name, content) {
883
+ return `<reference name="${escapeAttribute(name)}">\n${content}</reference>`;
884
+ }
885
+ const scopeOption = {
886
+ description: "Tier to write: repo (default), `.agents/references/` at the root, or user, `~/.agents/references/`.",
887
+ type: t.string().optional()
888
+ };
889
+ const inOption = {
890
+ description: "A folder strictly below the root, which must exist and not be ignored by git: acts on its project section, `<folder>/.agents/references/<name>.md`.",
891
+ type: t.string().optional()
892
+ };
893
+ /** The root a write checks its usage against before the layers are read: `--root`, else the one found from the working folder. */
894
+ function givenRoot(root) {
895
+ return root === void 0 ? findRoot(process.cwd()) : resolve(root);
896
+ }
897
+ const pluginOption = {
898
+ description: "A plugin's source folder in this repository: acts on its `references/<name>.md`.",
899
+ type: t.string().optional()
900
+ };
901
+ const skillOption = {
902
+ description: "A skill's source folder in this repository: acts on its `references/<name>.default.md`.",
903
+ type: t.string().optional()
904
+ };
905
+ //#endregion
906
+ //#region src/references/reference-check.command.ts
907
+ function checkCommand(layersFor) {
908
+ return command({
909
+ name: "check",
910
+ description: "Check references, each composed as `load` composes it: an interface document against its shape, an implementation against its interface, and every document for the defects composing it raises. With no names, checks every name found. Every warning `load` raises is reported. A failure is an entry in `failed`, not an error; a warning never fails a check.",
911
+ arguments: [{
912
+ name: "names",
913
+ description: "Reference names, without the `.md` extension; `<plugin>/<name>` builds that plugin’s own stack.",
914
+ type: t.array(t.string()).optional()
915
+ }],
916
+ options: {
917
+ root: rootOption,
918
+ trace: {
919
+ ...traceOption,
920
+ description: "Add every entry checked, with its kind and unsupported sections."
921
+ }
922
+ },
923
+ async run(args) {
924
+ try {
925
+ const home = homedir();
926
+ const entries = checkReferences(await layersFor(args, home), {
927
+ names: args.names ?? [],
928
+ display: (path) => collapseHome(home, path)
929
+ });
930
+ const failing = entries.filter((entry) => entry.failures.length);
931
+ const strip = (failures) => failures.map(({ check, message }) => ({
932
+ check,
933
+ message
934
+ }));
935
+ const report = compact({
936
+ failed: failing.map(({ name, loadedBy, failures, warnings }) => compact({
937
+ name,
938
+ loadedBy,
939
+ failures: strip(failures),
940
+ warnings
941
+ })),
942
+ warned: entries.filter((entry) => !entry.failures.length && entry.warnings.length).map(({ name, loadedBy, warnings }) => compact({
943
+ name,
944
+ loadedBy,
945
+ warnings
946
+ })),
947
+ passed: entries.length - failing.length,
948
+ references: args.trace ? entries.map(({ name, loadedBy, kind, status, failures, unsupported, warnings }) => compact({
949
+ name,
950
+ loadedBy,
951
+ kind,
952
+ status,
953
+ failures: strip(failures),
954
+ unsupported,
955
+ warnings
956
+ })) : void 0
957
+ });
958
+ return result(report);
959
+ } catch (error) {
960
+ return asCliError(error, "Reference check failed.");
961
+ }
962
+ }
963
+ });
964
+ }
965
+ //#endregion
966
+ //#region src/references/reference-create.command.ts
967
+ function createCommand(layersFor) {
968
+ return command({
969
+ name: "create",
970
+ description: "Start a contribution in the repo or user tier, a folder's project section, or a plugin's or a skill's source folder, from a template or an interface. Never overwrites a file with a body; writes a file a contribution above hides, with a warning.",
971
+ arguments: [{
972
+ name: "name",
973
+ description: "Reference name, without the `.md` extension.",
974
+ type: t.string()
975
+ }],
976
+ options: {
977
+ root: rootOption,
978
+ scope: scopeOption,
979
+ in: inOption,
980
+ plugin: pluginOption,
981
+ default: skillOption,
982
+ merge: {
983
+ description: "How it applies over what is below: replace, patch (default), combine, or remove. remove writes no body.",
984
+ type: t.string().optional()
985
+ },
986
+ template: {
987
+ description: "File whose text is written as it is, frontmatter included. Defaults to a built-in template of top-level `##` sections.",
988
+ type: t.string().optional()
989
+ },
990
+ implements: {
991
+ description: "Interface id to implement: the interface's sections become `##` headings, each stubbed `**Unsupported.**` with what it holds, and `merge: replace`; with --in, a project section with neither. Not with --template.",
992
+ type: t.string().optional()
993
+ },
994
+ "dry-run": {
995
+ description: "Print the target path and the exact content, and write nothing.",
996
+ type: t.boolean().optional()
997
+ },
998
+ trace: {
999
+ ...traceOption,
1000
+ description: "After the write, add the trace, in which the new file applies."
1001
+ }
1002
+ },
1003
+ async run(args) {
1004
+ const format = this.format;
1005
+ try {
1006
+ const home = homedir();
1007
+ const display = (path) => collapseHome(home, path);
1008
+ const request = {
1009
+ root: givenRoot(args.root),
1010
+ home,
1011
+ name: args.name,
1012
+ scope: args.scope,
1013
+ in: args.in,
1014
+ plugin: args.plugin,
1015
+ default: args.default,
1016
+ merge: args.merge,
1017
+ template: args.template,
1018
+ implements: args.implements,
1019
+ dryRun: args["dry-run"]
1020
+ };
1021
+ validateWrite({
1022
+ command: "create",
1023
+ ...request
1024
+ });
1025
+ const layers = await layersFor({
1026
+ root: args.root,
1027
+ default: args.default
1028
+ }, home);
1029
+ const created = createContribution(layers, {
1030
+ ...request,
1031
+ root: layers.root ?? request.root
1032
+ }, { display });
1033
+ const report = compact({
1034
+ path: display(created.path),
1035
+ folder: created.folder,
1036
+ hint: created.dryRun ? "Nothing was written. Run again without --dry-run to write it." : `Run \`load ${created.name}\` for the composed document.`,
1037
+ content: created.dryRun ? created.content : void 0,
1038
+ hints: created.hints,
1039
+ warnings: created.warnings,
1040
+ trace: args.trace && created.after ? displayTrace(created.after.trace, home) : void 0
1041
+ });
1042
+ if (format !== "text") return result(report);
1043
+ writeWarnings(created.warnings);
1044
+ for (const hint of created.hints) process.stderr.write(`hint: ${hint}\n`);
1045
+ if (args.trace && created.after) process.stderr.write(traceText(created.after, home));
1046
+ return document(created.dryRun ? `${report.path}\n\n${created.content}` : `${report.path}\n\n${report.hint}\n`);
1047
+ } catch (error) {
1048
+ return asCliError(error, "Reference creation failed.");
1049
+ }
1050
+ }
1051
+ });
1052
+ }
1053
+ //#endregion
1054
+ //#region src/references/reference-customize.command.ts
1055
+ function baseReport(customized) {
1056
+ const base = customized.base;
1057
+ const resolved = base?.resolved;
1058
+ return compact({
1059
+ status: resolved?.status ?? "missing",
1060
+ stack: resolved?.trace.stack,
1061
+ content: base?.content ?? "",
1062
+ projectSections: base?.projectSections.map(({ folder, content }) => ({
1063
+ folder,
1064
+ content
1065
+ }))
1066
+ });
1067
+ }
1068
+ function baseText(customized) {
1069
+ const base = customized.base;
1070
+ if (!base) return "";
1071
+ if (!base.content && !base.projectSections.length && base.resolved.status !== "found") return `${notFoundTag(base.resolved)}\n`;
1072
+ return deliveredText({
1073
+ ...base.resolved,
1074
+ content: base.content,
1075
+ projectSections: base.projectSections
1076
+ });
1077
+ }
1078
+ function report(customized, args, home) {
1079
+ const display = (path) => collapseHome(home, path);
1080
+ const after = customized.after;
1081
+ return compact({
1082
+ path: display(customized.path),
1083
+ folder: customized.folder,
1084
+ metadataFile: customized.metadataFile === void 0 ? void 0 : display(customized.metadataFile),
1085
+ code: customized.code,
1086
+ message: customized.message,
1087
+ extends: customized.extends,
1088
+ deleted: customized.deleted,
1089
+ status: after?.status,
1090
+ stack: after?.trace.stack,
1091
+ answer: after ? describeAnswer(after, display) : void 0,
1092
+ hint: customized.dryRun ? "Nothing was written. Run again without --dry-run to write it." : customized.code ? void 0 : `Run \`load ${customized.name}\` for the composed document.`,
1093
+ content: customized.dryRun ? customized.content : void 0,
1094
+ metadataContent: customized.dryRun ? customized.metadataContent : void 0,
1095
+ hints: customized.hints,
1096
+ warnings: customized.warnings,
1097
+ trace: args.trace && after && !customized.code ? displayTrace(after.trace, home) : void 0
1098
+ });
1099
+ }
1100
+ function text(customized, shown) {
1101
+ if (customized.code) return `${shown.path}\n\n${customized.code}: ${customized.message}\n`;
1102
+ if (customized.dryRun) {
1103
+ const head = customized.deleted ? "would delete the file, left with nothing in it.\n" : customized.content ?? "";
1104
+ const meta = customized.metadataContent === void 0 ? "" : `\n${shown.metadataFile}\n\n${customized.metadataContent}`;
1105
+ return `${shown.path}\n\n${head}${meta}`;
1106
+ }
1107
+ if (customized.action === "cleared") {
1108
+ const what = customized.deleted ? "selection removed; the file was left empty and deleted" : "selection removed";
1109
+ return `${shown.path}\n\n${what}. ${shown.answer}\n`;
1110
+ }
1111
+ if (customized.action === "selection") return `${shown.path}\n\nextends: ${JSON.stringify(customized.extends)}. ${shown.answer}\n`;
1112
+ const meta = shown.metadataFile ? ` Its record is in ${shown.metadataFile}.` : "";
1113
+ return `${shown.path}\n\n${shown.hint}${meta}\n`;
1114
+ }
1115
+ function customizeCommand(layersFor) {
1116
+ return command({
1117
+ name: "customize",
1118
+ description: "Customize a name for the repo or user tier, or a folder's project section, running no model: pick what it builds on (`extends`), write a body generated from a request with its record, or print what the generator reads (--base). Writes a file a contribution above hides, with a warning.",
1119
+ arguments: [{
1120
+ name: "name",
1121
+ description: "Reference name, without the `.md` extension.",
1122
+ type: t.string()
1123
+ }, {
1124
+ name: "steps",
1125
+ description: "More steps for --extends, so `--extends a b` lists both, bottom first.",
1126
+ type: t.array(t.string()).optional()
1127
+ }],
1128
+ options: {
1129
+ root: rootOption,
1130
+ scope: scopeOption,
1131
+ in: {
1132
+ ...inOption,
1133
+ description: `${inOption.description} Takes no --extends or --merge.`
1134
+ },
1135
+ default: {
1136
+ description: "The calling skill's folder, read only: its `references/<name>.default.md` joins the stack as the `default` tier.",
1137
+ type: t.string().optional()
1138
+ },
1139
+ plugin: {
1140
+ description: "Not accepted: customize never writes a plugin's file; an author uses `create --plugin`.",
1141
+ type: t.string().optional()
1142
+ },
1143
+ extends: {
1144
+ description: "The picks, bottom first: each a plugin holding the name, `<plugin>:<merge>` (such as `house-style:combine`), or `default`; `none` removes them. A file with no body is a selection.",
1145
+ type: t.array(t.string()).optional()
1146
+ },
1147
+ "extends-file": {
1148
+ description: "A YAML file holding the whole `extends` list, with every step key (`merge`, `extends`, `sections`).",
1149
+ type: t.string().optional()
1150
+ },
1151
+ request: {
1152
+ description: "A file holding the customizer's request, in their own words; recorded as `request`. Needs --body.",
1153
+ type: t.string().optional()
1154
+ },
1155
+ body: {
1156
+ description: "A file holding the body the generator wrote; written as given, with the picks, sources, and request recorded. Needs --request.",
1157
+ type: t.string().optional()
1158
+ },
1159
+ merge: {
1160
+ description: "How the body applies over what is below: patch (default), combine, or replace for a whole document.",
1161
+ type: t.string().optional()
1162
+ },
1163
+ implements: {
1164
+ description: "Interface id the body implements: a whole document recording `implements` and `merge: replace`; with --in, a project section from the interface's stub.",
1165
+ type: t.string().optional()
1166
+ },
1167
+ base: {
1168
+ description: "Print what the generator reads for the target, and write nothing: the name resolved with the picks, leaving out the target file, managed text, the tier above, and personal text; with --implements, the interface's stub first.",
1169
+ type: t.boolean().optional()
1170
+ },
1171
+ "dry-run": {
1172
+ description: "Print the target path and the exact content, and write nothing.",
1173
+ type: t.boolean().optional()
1174
+ },
1175
+ trace: {
1176
+ ...traceOption,
1177
+ description: "After the write, add the trace, in which the written file applies."
1178
+ }
1179
+ },
1180
+ async run(args) {
1181
+ const format = this.format;
1182
+ try {
1183
+ const home = homedir();
1184
+ const display = (path) => collapseHome(home, path);
1185
+ const picks = args.extends === void 0 ? void 0 : [...args.extends, ...args.steps ?? []];
1186
+ const request = {
1187
+ root: givenRoot(args.root),
1188
+ home,
1189
+ name: args.name,
1190
+ scope: args.scope,
1191
+ in: args.in,
1192
+ plugin: args.plugin,
1193
+ default: args.default,
1194
+ extends: picks,
1195
+ extendsFile: args["extends-file"],
1196
+ request: args.request,
1197
+ body: args.body,
1198
+ merge: args.merge,
1199
+ implements: args.implements,
1200
+ base: args.base,
1201
+ dryRun: args["dry-run"]
1202
+ };
1203
+ validateWrite({
1204
+ command: "customize",
1205
+ ...request
1206
+ });
1207
+ if (args.steps?.length && args.extends === void 0) throw new ReferenceFailure("options-missing", `${args.steps.join(" ")} follow no --extends; pass --extends before the steps.`);
1208
+ const layers = await layersFor({
1209
+ root: args.root,
1210
+ default: args.default
1211
+ }, home);
1212
+ const customized = customizeContribution(layers, {
1213
+ ...request,
1214
+ root: layers.root ?? request.root
1215
+ }, { display });
1216
+ if (customized.action === "base") {
1217
+ if (format !== "text") return result(baseReport(customized));
1218
+ return document(baseText(customized));
1219
+ }
1220
+ const shown = report(customized, args, home);
1221
+ if (format !== "text") return result(shown);
1222
+ writeWarnings(customized.warnings);
1223
+ for (const hint of customized.hints) process.stderr.write(`hint: ${hint}\n`);
1224
+ if (args.trace && customized.after && !customized.code) process.stderr.write(traceText(customized.after, home));
1225
+ return document(text(customized, shown));
1226
+ } catch (error) {
1227
+ return asCliError(error, "Reference customization failed.");
1228
+ }
1229
+ }
1230
+ });
1231
+ }
1232
+ //#endregion
1233
+ //#region src/references/reference-delete.command.ts
1234
+ function deleteReport(deleted, home) {
1235
+ const selection = deleted.selection ? `the selection extends: [${deleted.selection.join(", ")}] went with it; run \`reference customize ${deleted.name} --extends ${deleted.selection.join(" ")}\` to restore it` : void 0;
1236
+ return compact({
1237
+ path: collapseHome(home, deleted.path),
1238
+ folder: deleted.folder,
1239
+ metadataFile: deleted.metadataFile === void 0 ? void 0 : collapseHome(home, deleted.metadataFile),
1240
+ code: deleted.code,
1241
+ message: deleted.message,
1242
+ dryRun: deleted.dryRun || void 0,
1243
+ status: deleted.after.status,
1244
+ stack: deleted.after.trace.stack,
1245
+ answer: describeAnswer(deleted.after, (path) => collapseHome(home, path)),
1246
+ selection,
1247
+ options: deleted.after.options
1248
+ });
1249
+ }
1250
+ function deleteCommand(layersFor) {
1251
+ return command({
1252
+ name: "delete",
1253
+ description: "Delete a repo or user contribution, a folder's project section, or one in a plugin's or a skill's source folder, with a non-Markdown file's metadata file, and report what answers the name afterwards. Never deletes an installed or managed one.",
1254
+ arguments: [{
1255
+ name: "name",
1256
+ description: "Reference name, without the `.md` extension.",
1257
+ type: t.string()
1258
+ }],
1259
+ options: {
1260
+ root: rootOption,
1261
+ scope: {
1262
+ ...scopeOption,
1263
+ description: "Tier to delete from: repo (default) or user."
1264
+ },
1265
+ in: inOption,
1266
+ plugin: pluginOption,
1267
+ default: skillOption,
1268
+ "dry-run": {
1269
+ description: "Print the file that would be deleted and what would answer the name then, and delete nothing.",
1270
+ type: t.boolean().optional()
1271
+ }
1272
+ },
1273
+ async run(args) {
1274
+ const format = this.format;
1275
+ try {
1276
+ const home = homedir();
1277
+ const request = {
1278
+ root: givenRoot(args.root),
1279
+ home,
1280
+ name: args.name,
1281
+ scope: args.scope,
1282
+ in: args.in,
1283
+ plugin: args.plugin,
1284
+ default: args.default,
1285
+ dryRun: args["dry-run"]
1286
+ };
1287
+ validateWrite({
1288
+ command: "delete",
1289
+ ...request
1290
+ });
1291
+ const layers = await layersFor({
1292
+ root: args.root,
1293
+ default: args.default
1294
+ }, home);
1295
+ const deleted = deleteContribution(layers, {
1296
+ ...request,
1297
+ root: layers.root ?? request.root
1298
+ }, { display: (path) => collapseHome(home, path) });
1299
+ const report = deleteReport(deleted, home);
1300
+ if (format !== "text") return result(report);
1301
+ if (deleted.code) return document(`${report.path}\n\n${deleted.code}: ${deleted.message}\n`);
1302
+ const selection = report.selection ? ` Also, ${report.selection}.` : "";
1303
+ const metadata = report.metadataFile ? ` Its metadata file ${report.metadataFile} went with it.` : "";
1304
+ return document(`${report.path}\n\n${deleted.dryRun ? "would delete" : "deleted"}. ${report.answer}${metadata}${selection}\n`);
1305
+ } catch (error) {
1306
+ return asCliError(error, "Reference deletion failed.");
1307
+ }
1308
+ }
1309
+ });
1310
+ }
1311
+ //#endregion
1312
+ //#region src/references/reference-implementations.command.ts
1313
+ function parseAccept(value) {
1314
+ if (value === void 0) return void 0;
1315
+ const versions = value.split(",").map((part) => part.trim());
1316
+ if (!versions.length || versions.some((part) => !/^[1-9]\d*$/.test(part))) throw new ReferenceFailure("option-invalid", `--accept is a comma-separated list of versions, such as 1,2; got "${value}".`, { details: {
1317
+ option: "--accept",
1318
+ value
1319
+ } });
1320
+ return versions.map(Number);
1321
+ }
1322
+ function implementationsCommand(layersFor) {
1323
+ return command({
1324
+ name: "implementations",
1325
+ description: "List the implementations of an interface a consumer can load, each with its pin, description, and unsupported sections.",
1326
+ arguments: [{
1327
+ name: "id",
1328
+ description: "Interface id, the name of its `<id>.interface` document without the suffix.",
1329
+ type: t.string()
1330
+ }],
1331
+ options: {
1332
+ root: rootOption,
1333
+ accept: {
1334
+ description: "Versions the consumer knows, comma-separated, such as 1,2. Defaults to the interface's `versions`.",
1335
+ type: t.string().optional()
1336
+ },
1337
+ trace: {
1338
+ ...traceOption,
1339
+ description: "Add each one's name, tier, and version, the interface's cardinality and accepted versions, and the ones left out with why."
1340
+ }
1341
+ },
1342
+ async run(args) {
1343
+ try {
1344
+ const accept = parseAccept(args.accept);
1345
+ const home = homedir();
1346
+ const found = referenceImplementations(args.id, await layersFor(args, home), {
1347
+ accept,
1348
+ display: (path) => collapseHome(home, path)
1349
+ });
1350
+ const rows = found.implementations.map(({ pin, description, unsupported, name, tier, version }) => compact(args.trace ? {
1351
+ pin,
1352
+ description,
1353
+ unsupported,
1354
+ name,
1355
+ tier,
1356
+ version
1357
+ } : {
1358
+ pin,
1359
+ ...tier === "plugin" ? {} : { tier },
1360
+ description,
1361
+ unsupported
1362
+ }));
1363
+ if (!rows.length && !args.trace) return empty(`0 implementations of ${args.id}`, { help: `Run \`reference implementations ${args.id} --trace\` for the ones left out, with why.` });
1364
+ const report = { implementations: rows };
1365
+ if (args.trace) Object.assign(report, compact({
1366
+ interface: found.interface,
1367
+ cardinality: found.cardinality,
1368
+ accept: found.accept,
1369
+ excluded: found.excluded,
1370
+ warnings: found.warnings
1371
+ }));
1372
+ return result(report);
1373
+ } catch (error) {
1374
+ return asCliError(error, "Implementation lookup failed.");
1375
+ }
1376
+ }
1377
+ });
1378
+ }
1379
+ //#endregion
1380
+ //#region src/references/reference-list.command.ts
1381
+ function listCommand(layersFor) {
1382
+ return command({
1383
+ name: "list",
1384
+ description: "List every reference name, one row each, with its description, and its status when not found. A pattern keeps only the names it matches exactly.",
1385
+ arguments: [{
1386
+ name: "pattern",
1387
+ description: "Exact name pattern, where `*` stands for any run of characters, such as `project-tracker.capabilities.*`.",
1388
+ type: t.string().optional()
1389
+ }],
1390
+ options: {
1391
+ root: rootOption,
1392
+ trace: {
1393
+ ...traceOption,
1394
+ description: "Add one row per name per layer holding it, with its outcome."
1395
+ }
1396
+ },
1397
+ async run(args) {
1398
+ try {
1399
+ const home = homedir();
1400
+ const display = (path) => collapseHome(home, path);
1401
+ const listing = listReferences(await layersFor(args, home), {
1402
+ display,
1403
+ pattern: args.pattern
1404
+ });
1405
+ const traced = args.trace && listing.layers.length > 0;
1406
+ if (!listing.references.length && !traced && !listing.warnings.length) return empty(args.pattern ? `0 references match "${args.pattern}"` : "0 references — no layer holds one", { help: [...listing.defaults ? [listing.defaults] : [], args.pattern ? "Run `reference list` with no pattern for every name, or `reference search <query>` by topic." : "Run `reference create <name>` to start one."] });
1407
+ const report = { references: listing.references.map((row) => compact({
1408
+ name: row.name,
1409
+ description: row.description,
1410
+ format: row.format,
1411
+ status: row.status === "found" ? void 0 : row.status,
1412
+ code: row.code,
1413
+ options: row.options
1414
+ })) };
1415
+ if (listing.defaults) report.defaults = listing.defaults;
1416
+ if (args.trace) report.layers = listing.layers.map((row) => compact({
1417
+ ...row,
1418
+ path: display(row.path)
1419
+ }));
1420
+ if (listing.warnings.length) report.warnings = listing.warnings;
1421
+ return result(report);
1422
+ } catch (error) {
1423
+ return asCliError(error, "Reference listing failed.");
1424
+ }
1425
+ }
1426
+ });
1427
+ }
1428
+ //#endregion
1429
+ //#region src/references/reference-load.command.ts
1430
+ function loadEntry(resolved, home, { metadata, trace }) {
1431
+ if (resolved.status === "found" && !resolved.warnings.length && !resolved.projectSections.length && !metadata && !trace && resolved.format === "markdown") return resolved.content;
1432
+ return compact({
1433
+ name: resolved.name,
1434
+ status: resolved.status,
1435
+ content: resolved.content,
1436
+ projectSections: resolved.projectSections.map(({ folder, plugin, content }) => plugin === void 0 ? {
1437
+ folder,
1438
+ content
1439
+ } : {
1440
+ folder,
1441
+ plugin,
1442
+ content
1443
+ }),
1444
+ format: resolved.format === "markdown" ? void 0 : resolved.format,
1445
+ code: resolved.code,
1446
+ options: resolved.options,
1447
+ metadata: metadata ? resolved.metadata : void 0,
1448
+ warnings: resolved.warnings,
1449
+ trace: trace ? displayTrace(resolved.trace, home) : void 0
1450
+ });
1451
+ }
1452
+ /** One entry per name asked, as the array itself rather than under a `result` key. */
1453
+ const bareArray = {
1454
+ toon: (entries) => encode(entries, "toon"),
1455
+ json: (entries) => encode(entries, "json")
1456
+ };
1457
+ /** One found name prints alone; several, and any name not `found`, print as `<reference>` tags in the order asked. */
1458
+ function loadText(resolved) {
1459
+ const tag = (entry) => entry.status === "found" ? referenceTag(entry.name, deliveredText(entry)) : notFoundTag(entry);
1460
+ if (resolved.length === 1) {
1461
+ const [only] = resolved;
1462
+ return document(only.status === "found" ? deliveredText(only) : notFoundTag(only));
1463
+ }
1464
+ return document(resolved.map(tag).join("\n\n"));
1465
+ }
1466
+ function loadCommand(layersFor) {
1467
+ return command({
1468
+ name: "load",
1469
+ description: "Print one or more references, each composed from its contributions as their extends and merge declare.",
1470
+ arguments: [{
1471
+ name: "names",
1472
+ description: "Reference names, without the `.md` extension; `<plugin>/<name>` builds that plugin’s own stack.",
1473
+ type: t.array(t.string())
1474
+ }],
1475
+ options: {
1476
+ root: rootOption,
1477
+ default: defaultOption,
1478
+ metadata: {
1479
+ description: "Make every entry a record with its status and composed frontmatter.",
1480
+ type: t.boolean().optional()
1481
+ },
1482
+ trace: traceOption
1483
+ },
1484
+ async run(args) {
1485
+ const format = this.format;
1486
+ try {
1487
+ if (!args.names.length) throw new ReferenceFailure("name-missing", "name at least one reference to load.", { help: "Run `reference load <name>...`, or `reference list` for every name." });
1488
+ for (const name of args.names) requireName(name);
1489
+ const home = homedir();
1490
+ const layers = await layersFor(args, home);
1491
+ const display = (path) => collapseHome(home, path);
1492
+ const lookup = interfaceLookup(layers, { display });
1493
+ const resolved = args.names.map((name) => {
1494
+ const entry = resolveReference(parseReferenceName(name), layers, { display });
1495
+ const checked = checkResolved(entry, lookup);
1496
+ entry.warnings.push(...checked.warnings);
1497
+ for (const failure of checked.failures) {
1498
+ if (failure.check === "compose") continue;
1499
+ entry.warnings.push({
1500
+ code: "interface-check",
1501
+ message: `it fails its interface check (${failure.check}): ${failure.message}`
1502
+ });
1503
+ }
1504
+ return entry;
1505
+ });
1506
+ if (format === "text") {
1507
+ for (const entry of resolved) {
1508
+ writeWarnings(entry.warnings);
1509
+ if (args.trace) process.stderr.write(traceText(entry, home));
1510
+ }
1511
+ return loadText(resolved);
1512
+ }
1513
+ const flags = {
1514
+ metadata: Boolean(args.metadata),
1515
+ trace: Boolean(args.trace)
1516
+ };
1517
+ return result(resolved.map((entry) => loadEntry(entry, home, flags)), { render: bareArray });
1518
+ } catch (error) {
1519
+ return asCliError(error, "Reference lookup failed.");
1520
+ }
1521
+ }
1522
+ });
1523
+ }
1524
+ //#endregion
1525
+ //#region src/references/reference-search.command.ts
1526
+ function searchCommand(layersFor) {
1527
+ return command({
1528
+ name: "search",
1529
+ description: "Find references by name, description, tags, headings, or body, best match first.",
1530
+ arguments: [{
1531
+ name: "query",
1532
+ description: "What the reference is about.",
1533
+ type: t.string()
1534
+ }],
1535
+ options: {
1536
+ root: rootOption,
1537
+ trace: {
1538
+ ...traceOption,
1539
+ description: "Add the tier and plugin or skill of each match."
1540
+ }
1541
+ },
1542
+ async run(args) {
1543
+ const query = args.query.trim();
1544
+ try {
1545
+ const home = homedir();
1546
+ const { matches, defaults } = searchReferences(query, await layersFor(args, home), { display: (path) => collapseHome(home, path) });
1547
+ if (!matches.length) return empty(`0 references match "${query}"`, { help: [...defaults ? [defaults] : [], "Try other words, or run `reference list` for every name."] });
1548
+ const report = compact({
1549
+ references: matches.map(({ name, description, match, folder, tier, plugin, skill, loadedBy }) => compact(args.trace ? {
1550
+ name,
1551
+ description,
1552
+ match,
1553
+ folder,
1554
+ loadedBy,
1555
+ tier,
1556
+ plugin,
1557
+ skill
1558
+ } : {
1559
+ name,
1560
+ description,
1561
+ match,
1562
+ folder,
1563
+ loadedBy
1564
+ })),
1565
+ defaults
1566
+ });
1567
+ return result(report);
1568
+ } catch (error) {
1569
+ return asCliError(error, "Reference search failed.");
1570
+ }
1571
+ }
1572
+ });
1573
+ }
1574
+ //#endregion
1575
+ //#region src/references/reference-where.command.ts
1576
+ function whereCommand(layersFor) {
1577
+ return command({
1578
+ name: "where",
1579
+ description: "Give the repo and user files, and with --in a folder's project section, a customization or a selection of a reference is written to, who each reaches and what hides it, the main document's heading paths, and the read-only plugins and default the stack uses.",
1580
+ arguments: [{
1581
+ name: "name",
1582
+ description: "Reference name, without the `.md` extension.",
1583
+ type: t.string()
1584
+ }],
1585
+ options: {
1586
+ root: rootOption,
1587
+ default: defaultOption,
1588
+ in: {
1589
+ ...inOption,
1590
+ description: "A folder strictly below the root: also give its project section's file."
1591
+ },
1592
+ trace: {
1593
+ ...traceOption,
1594
+ description: "Add every layer checked, with its outcome."
1595
+ }
1596
+ },
1597
+ async run(args) {
1598
+ const format = this.format;
1599
+ try {
1600
+ const home = homedir();
1601
+ const display = (path) => collapseHome(home, path);
1602
+ const layers = await layersFor(args, home);
1603
+ const report = whereReference(args.name, layers, {
1604
+ root: layers.root ?? givenRoot(args.root),
1605
+ display,
1606
+ in: args.in
1607
+ });
1608
+ const output = compact({
1609
+ name: report.name,
1610
+ status: report.status,
1611
+ files: report.files,
1612
+ headings: report.headings,
1613
+ used: report.used,
1614
+ options: report.options,
1615
+ hint: report.used.length ? `${report.used.join(", ")} ${report.used.length === 1 ? "is" : "are"} read-only; a customization goes in one of the files.` : void 0,
1616
+ layers: args.trace ? report.layers.map((layer) => ({
1617
+ ...layer,
1618
+ path: display(layer.path)
1619
+ })) : void 0,
1620
+ warnings: report.warnings
1621
+ });
1622
+ if (format === "text") writeWarnings(report.warnings);
1623
+ return result(output);
1624
+ } catch (error) {
1625
+ return asCliError(error, "Reference placement lookup failed.");
1626
+ }
1627
+ }
1628
+ });
1629
+ }
1630
+ //#endregion
1631
+ //#region src/references/reference.command.ts
1632
+ /** A factory, so each host CLI names itself as the plugin whose own references come first. */
1633
+ function createReferenceCommands({ plugin } = {}) {
1634
+ const layersFor = layerSource(plugin);
1635
+ return {
1636
+ load: loadCommand(layersFor),
1637
+ list: listCommand(layersFor),
1638
+ search: searchCommand(layersFor),
1639
+ where: whereCommand(layersFor),
1640
+ create: createCommand(layersFor),
1641
+ customize: customizeCommand(layersFor),
1642
+ delete: deleteCommand(layersFor),
1643
+ check: checkCommand(layersFor),
1644
+ implementations: implementationsCommand(layersFor)
1645
+ };
1646
+ }
1647
+ function createReferenceCommand(options = {}) {
1648
+ const { load, list, search, where, create, customize, delete: remove, check, implementations } = createReferenceCommands(options);
1649
+ return command({
1650
+ name: "reference",
1651
+ description: "Read reference documents by name, composed from the managed, repo, user, plugin, and default tiers, and create, customize, or delete a contribution.",
1652
+ commands: [
1653
+ load,
1654
+ list,
1655
+ search,
1656
+ where,
1657
+ create,
1658
+ customize,
1659
+ remove,
1660
+ implementations,
1661
+ check
1662
+ ]
1663
+ });
1664
+ }
1665
+ //#endregion
1405
1666
  export { createAssetCommand, createReferenceCommand, createReferenceCommands };